mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-05 17:35:36 +00:00
app/internal/window: [macOS,iOS] reduce display link starting and stopping
Recent changes to the macOS threading exposed a problem where a window's display link may fail to start after being started and stopped in rapid succession. Introduce a displayLink type that waits a while after the last stop request before stopping its display link. That seems to be the way other projects are using display links. As a bonus, the new implementation avoids the potentially expensive overhead of frequent starting and stopping the underlying OS thread. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -19,39 +19,14 @@ static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFlo
|
|||||||
gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, p.y, dx, dy, [event timestamp], [event modifierFlags]);
|
gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, p.y, dx, dy, [event timestamp], [event modifierFlags]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
|
|
||||||
CFTypeRef view = (CFTypeRef *)displayLinkContext;
|
|
||||||
gio_onFrameCallback(view);
|
|
||||||
return kCVReturnSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
@interface GioView : NSOpenGLView
|
@interface GioView : NSOpenGLView
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GioView {
|
@implementation GioView {
|
||||||
CVDisplayLinkRef displayLink;
|
|
||||||
}
|
}
|
||||||
- (instancetype)initWithFrame:(NSRect)frameRect
|
- (instancetype)initWithFrame:(NSRect)frameRect
|
||||||
pixelFormat:(NSOpenGLPixelFormat *)format {
|
pixelFormat:(NSOpenGLPixelFormat *)format {
|
||||||
self = [super initWithFrame:frameRect pixelFormat:format];
|
return [super initWithFrame:frameRect pixelFormat:format];
|
||||||
if (self) {
|
|
||||||
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
|
|
||||||
CVDisplayLinkSetOutputCallback(displayLink, displayLinkCallback, (__bridge void*)self);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
- (void)dealloc {
|
|
||||||
CVDisplayLinkRelease(displayLink);
|
|
||||||
}
|
|
||||||
- (void)setAnimating:(BOOL)anim {
|
|
||||||
if (anim) {
|
|
||||||
CVDisplayLinkStart(displayLink);
|
|
||||||
} else {
|
|
||||||
CVDisplayLinkStop(displayLink);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- (void)updateDisplay:(CGDirectDisplayID)dispID {
|
|
||||||
CVDisplayLinkSetCurrentCGDisplay(displayLink, dispID);
|
|
||||||
}
|
}
|
||||||
- (void)prepareOpenGL {
|
- (void)prepareOpenGL {
|
||||||
[super prepareOpenGL];
|
[super prepareOpenGL];
|
||||||
@@ -139,16 +114,6 @@ CFTypeRef gio_createGLView(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gio_updateDisplayLink(CFTypeRef viewRef, CGDirectDisplayID dispID) {
|
|
||||||
GioView *view = (__bridge GioView *)viewRef;
|
|
||||||
[view updateDisplay:dispID];
|
|
||||||
}
|
|
||||||
|
|
||||||
void gio_setAnimating(CFTypeRef viewRef, BOOL anim) {
|
|
||||||
GioView *view = (__bridge GioView *)viewRef;
|
|
||||||
[view setAnimating:anim];
|
|
||||||
}
|
|
||||||
|
|
||||||
CFTypeRef gio_contextForView(CFTypeRef viewRef) {
|
CFTypeRef gio_contextForView(CFTypeRef viewRef) {
|
||||||
NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef;
|
NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef;
|
||||||
return (__bridge CFTypeRef)view.openGLContext;
|
return (__bridge CFTypeRef)view.openGLContext;
|
||||||
|
|||||||
@@ -8,13 +8,44 @@ package window
|
|||||||
__attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void);
|
__attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void);
|
||||||
__attribute__ ((visibility ("hidden"))) NSUInteger gio_nsstringLength(CFTypeRef str);
|
__attribute__ ((visibility ("hidden"))) NSUInteger gio_nsstringLength(CFTypeRef str);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_nsstringGetCharacters(CFTypeRef str, unichar *chars, NSUInteger loc, NSUInteger length);
|
__attribute__ ((visibility ("hidden"))) void gio_nsstringGetCharacters(CFTypeRef str, unichar *chars, NSUInteger loc, NSUInteger length);
|
||||||
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void);
|
||||||
|
__attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl);
|
||||||
|
__attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl);
|
||||||
|
__attribute__ ((visibility ("hidden"))) int gio_stopDisplayLink(CFTypeRef dl);
|
||||||
|
__attribute__ ((visibility ("hidden"))) void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// displayLink is the state for a display link (CVDisplayLinkRef on macOS,
|
||||||
|
// CADisplayLink on iOS). It runs a state-machine goroutine that keeps the
|
||||||
|
// display link running for a while after being stopped to avoid the thread
|
||||||
|
// start/stop overhead and because the CVDisplayLink sometimes fails to
|
||||||
|
// start, stop and start again within a short duration.
|
||||||
|
type displayLink struct {
|
||||||
|
callback func()
|
||||||
|
// states is for starting or stopping the display link.
|
||||||
|
states chan bool
|
||||||
|
// done is closed when the display link is destroyed.
|
||||||
|
done chan struct{}
|
||||||
|
// dids receives the display id when the callback owner is moved
|
||||||
|
// to a different screen.
|
||||||
|
dids chan uint64
|
||||||
|
// running tracks the desired state of the link. running is accessed
|
||||||
|
// with atomic.
|
||||||
|
running uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// displayLinks maps CFTypeRefs to *displayLinks.
|
||||||
|
var displayLinks sync.Map
|
||||||
|
|
||||||
var mainFuncs = make(chan func(), 1)
|
var mainFuncs = make(chan func(), 1)
|
||||||
|
|
||||||
// runOnMain runs the function on the main thread.
|
// runOnMain runs the function on the main thread.
|
||||||
@@ -47,3 +78,93 @@ func nsstringToString(str C.CFTypeRef) string {
|
|||||||
utf8 := utf16.Decode(chars)
|
utf8 := utf16.Decode(chars)
|
||||||
return string(utf8)
|
return string(utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDisplayLink(callback func()) (*displayLink, error) {
|
||||||
|
d := &displayLink{
|
||||||
|
callback: callback,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
states: make(chan bool),
|
||||||
|
dids: make(chan uint64),
|
||||||
|
}
|
||||||
|
dl := C.gio_createDisplayLink()
|
||||||
|
if dl == 0 {
|
||||||
|
return nil, errors.New("app: failed to create display link")
|
||||||
|
}
|
||||||
|
go d.run(dl)
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayLink) run(dl C.CFTypeRef) {
|
||||||
|
defer C.gio_releaseDisplayLink(dl)
|
||||||
|
displayLinks.Store(dl, d)
|
||||||
|
defer displayLinks.Delete(dl)
|
||||||
|
var stopTimer *time.Timer
|
||||||
|
var tchan <-chan time.Time
|
||||||
|
started := false
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tchan:
|
||||||
|
tchan = nil
|
||||||
|
started = false
|
||||||
|
C.gio_stopDisplayLink(dl)
|
||||||
|
case start := <-d.states:
|
||||||
|
switch {
|
||||||
|
case !start && tchan == nil:
|
||||||
|
// stopTimeout is the delay before stopping the display link to
|
||||||
|
// avoid the overhead of frequently starting and stopping the
|
||||||
|
// link thread.
|
||||||
|
const stopTimeout = 500 * time.Millisecond
|
||||||
|
if stopTimer == nil {
|
||||||
|
stopTimer = time.NewTimer(stopTimeout)
|
||||||
|
} else {
|
||||||
|
// stopTimer is always drained when tchan == nil.
|
||||||
|
stopTimer.Reset(stopTimeout)
|
||||||
|
}
|
||||||
|
tchan = stopTimer.C
|
||||||
|
atomic.StoreUint32(&d.running, 0)
|
||||||
|
case start:
|
||||||
|
if tchan != nil && !stopTimer.Stop() {
|
||||||
|
<-tchan
|
||||||
|
}
|
||||||
|
tchan = nil
|
||||||
|
atomic.StoreUint32(&d.running, 1)
|
||||||
|
if !started {
|
||||||
|
started = true
|
||||||
|
if res := C.gio_startDisplayLink(dl); res != 0 {
|
||||||
|
panic("failed to start display link")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case did := <-d.dids:
|
||||||
|
C.gio_setDisplayLinkDisplay(dl, C.uint64_t(did))
|
||||||
|
case <-d.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayLink) Start() {
|
||||||
|
d.states <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayLink) Stop() {
|
||||||
|
d.states <- false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayLink) Close() {
|
||||||
|
close(d.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *displayLink) SetDisplayID(did uint64) {
|
||||||
|
d.dids <- did
|
||||||
|
}
|
||||||
|
|
||||||
|
//export gio_onFrameCallback
|
||||||
|
func gio_onFrameCallback(dl C.CFTypeRef) {
|
||||||
|
if d, exists := displayLinks.Load(dl); exists {
|
||||||
|
d := d.(*displayLink)
|
||||||
|
if atomic.LoadUint32(&d.running) != 0 {
|
||||||
|
d.callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ __attribute__ ((visibility ("hidden"))) void gio_hideTextInput(CFTypeRef viewRef
|
|||||||
__attribute__ ((visibility ("hidden"))) void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef);
|
__attribute__ ((visibility ("hidden"))) void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef);
|
__attribute__ ((visibility ("hidden"))) void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_removeLayer(CFTypeRef layerRef);
|
__attribute__ ((visibility ("hidden"))) void gio_removeLayer(CFTypeRef layerRef);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_setAnimating(CFTypeRef viewRef, int anim);
|
|
||||||
__attribute__ ((visibility ("hidden"))) struct drawParams gio_viewDrawParams(CFTypeRef viewRef);
|
__attribute__ ((visibility ("hidden"))) struct drawParams gio_viewDrawParams(CFTypeRef viewRef);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
|
__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
|
||||||
@@ -46,8 +45,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type window struct {
|
type window struct {
|
||||||
view C.CFTypeRef
|
view C.CFTypeRef
|
||||||
w Callbacks
|
w Callbacks
|
||||||
|
displayLink *displayLink
|
||||||
|
|
||||||
layer C.CFTypeRef
|
layer C.CFTypeRef
|
||||||
visible atomic.Value
|
visible atomic.Value
|
||||||
@@ -71,6 +71,13 @@ func onCreate(view C.CFTypeRef) {
|
|||||||
w := &window{
|
w := &window{
|
||||||
view: view,
|
view: view,
|
||||||
}
|
}
|
||||||
|
dl, err := NewDisplayLink(func() {
|
||||||
|
w.draw(false)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
w.displayLink = dl
|
||||||
wopts := <-mainWindow.out
|
wopts := <-mainWindow.out
|
||||||
w.w = wopts.window
|
w.w = wopts.window
|
||||||
w.w.SetDriver(w)
|
w.w.SetDriver(w)
|
||||||
@@ -81,12 +88,6 @@ func onCreate(view C.CFTypeRef) {
|
|||||||
w.w.Event(system.StageEvent{Stage: system.StagePaused})
|
w.w.Event(system.StageEvent{Stage: system.StagePaused})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onFrameCallback
|
|
||||||
func gio_onFrameCallback(view C.CFTypeRef) {
|
|
||||||
w := views[view]
|
|
||||||
w.draw(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export gio_onDraw
|
//export gio_onDraw
|
||||||
func gio_onDraw(view C.CFTypeRef) {
|
func gio_onDraw(view C.CFTypeRef) {
|
||||||
w := views[view]
|
w := views[view]
|
||||||
@@ -139,6 +140,7 @@ func onDestroy(view C.CFTypeRef) {
|
|||||||
w := views[view]
|
w := views[view]
|
||||||
delete(views, view)
|
delete(views, view)
|
||||||
w.w.Event(system.DestroyEvent{})
|
w.w.Event(system.DestroyEvent{})
|
||||||
|
w.displayLink.Close()
|
||||||
C.gio_removeLayer(w.layer)
|
C.gio_removeLayer(w.layer)
|
||||||
C.CFRelease(w.layer)
|
C.CFRelease(w.layer)
|
||||||
w.layer = 0
|
w.layer = 0
|
||||||
@@ -240,15 +242,11 @@ func (w *window) SetAnimating(anim bool) {
|
|||||||
if v == 0 {
|
if v == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var animi C.int
|
|
||||||
if anim {
|
if anim {
|
||||||
animi = 1
|
w.displayLink.Start()
|
||||||
|
} else {
|
||||||
|
w.displayLink.Stop()
|
||||||
}
|
}
|
||||||
C.CFRetain(v)
|
|
||||||
runOnMain(func() {
|
|
||||||
defer C.CFRelease(v)
|
|
||||||
C.gio_setAnimating(v, animi)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) onKeyCommand(name string) {
|
func (w *window) onKeyCommand(name string) {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "framework_ios.h"
|
#include "framework_ios.h"
|
||||||
|
|
||||||
@interface GioView: UIView <UIKeyInput>
|
@interface GioView: UIView <UIKeyInput>
|
||||||
- (void)setAnimating:(BOOL)anim;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GioViewController : UIViewController
|
@interface GioViewController : UIViewController
|
||||||
@@ -134,20 +133,9 @@ static void handleTouches(int last, UIView *view, NSSet<UITouch *> *touches, UIE
|
|||||||
}
|
}
|
||||||
|
|
||||||
@implementation GioView
|
@implementation GioView
|
||||||
CADisplayLink *displayLink;
|
|
||||||
NSArray<UIKeyCommand *> *_keyCommands;
|
NSArray<UIKeyCommand *> *_keyCommands;
|
||||||
|
+ (void)onFrameCallback:(CADisplayLink *)link {
|
||||||
- (void)onFrameCallback:(CADisplayLink *)link {
|
gio_onFrameCallback((__bridge CFTypeRef)link);
|
||||||
gio_onFrameCallback((__bridge CFTypeRef)self);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithFrame:(CGRect)frame {
|
|
||||||
self = [super initWithFrame:frame];
|
|
||||||
if (self) {
|
|
||||||
__weak id weakSelf = self;
|
|
||||||
displayLink = [CADisplayLink displayLinkWithTarget:weakSelf selector:@selector(onFrameCallback:)];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)willMoveToWindow:(UIWindow *)newWindow {
|
- (void)willMoveToWindow:(UIWindow *)newWindow {
|
||||||
@@ -182,16 +170,6 @@ NSArray<UIKeyCommand *> *_keyCommands;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[displayLink invalidate];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAnimating:(BOOL)anim {
|
|
||||||
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
|
||||||
if (anim) {
|
|
||||||
[displayLink addToRunLoop:runLoop forMode:[runLoop currentMode]];
|
|
||||||
} else {
|
|
||||||
[displayLink removeFromRunLoop:runLoop forMode:[runLoop currentMode]];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||||
@@ -281,11 +259,6 @@ CFTypeRef gio_readClipboard(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gio_setAnimating(CFTypeRef viewRef, int anim) {
|
|
||||||
GioView *view = (__bridge GioView *)viewRef;
|
|
||||||
[view setAnimating:(anim ? YES : NO)];
|
|
||||||
}
|
|
||||||
|
|
||||||
void gio_showTextInput(CFTypeRef viewRef) {
|
void gio_showTextInput(CFTypeRef viewRef) {
|
||||||
UIView *view = (__bridge UIView *)viewRef;
|
UIView *view = (__bridge UIView *)viewRef;
|
||||||
[view becomeFirstResponder];
|
[view becomeFirstResponder];
|
||||||
@@ -335,3 +308,33 @@ struct drawParams gio_viewDrawParams(CFTypeRef viewRef) {
|
|||||||
params.left = insets.left*scale;
|
params.left = insets.left*scale;
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CFTypeRef gio_createDisplayLink(void) {
|
||||||
|
CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)];
|
||||||
|
dl.paused = YES;
|
||||||
|
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
|
||||||
|
[dl addToRunLoop:runLoop forMode:[runLoop currentMode]];
|
||||||
|
return (__bridge_retained CFTypeRef)dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gio_startDisplayLink(CFTypeRef dlref) {
|
||||||
|
CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
|
||||||
|
dl.paused = NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gio_stopDisplayLink(CFTypeRef dlref) {
|
||||||
|
CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
|
||||||
|
dl.paused = YES;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gio_releaseDisplayLink(CFTypeRef dlref) {
|
||||||
|
CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
|
||||||
|
[dl invalidate];
|
||||||
|
CFRelease(dlref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
|
||||||
|
// Nothing to do on iOS.
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ import (
|
|||||||
__attribute__ ((visibility ("hidden"))) void gio_main(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height);
|
__attribute__ ((visibility ("hidden"))) void gio_main(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height);
|
||||||
__attribute__ ((visibility ("hidden"))) CGFloat gio_viewWidth(CFTypeRef viewRef);
|
__attribute__ ((visibility ("hidden"))) CGFloat gio_viewWidth(CFTypeRef viewRef);
|
||||||
__attribute__ ((visibility ("hidden"))) CGFloat gio_viewHeight(CFTypeRef viewRef);
|
__attribute__ ((visibility ("hidden"))) CGFloat gio_viewHeight(CFTypeRef viewRef);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_setAnimating(CFTypeRef viewRef, BOOL anim);
|
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_updateDisplayLink(CFTypeRef viewRef, CGDirectDisplayID dispID);
|
|
||||||
__attribute__ ((visibility ("hidden"))) CGFloat gio_getViewBackingScale(CFTypeRef viewRef);
|
__attribute__ ((visibility ("hidden"))) CGFloat gio_getViewBackingScale(CFTypeRef viewRef);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
|
__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
|
||||||
@@ -48,9 +46,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type window struct {
|
type window struct {
|
||||||
view C.CFTypeRef
|
view C.CFTypeRef
|
||||||
w Callbacks
|
w Callbacks
|
||||||
stage system.Stage
|
stage system.Stage
|
||||||
|
displayLink *displayLink
|
||||||
|
|
||||||
// mu protect the following fields
|
// mu protect the following fields
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@@ -116,16 +115,11 @@ func (w *window) WriteClipboard(s string) {
|
|||||||
func (w *window) ShowTextInput(show bool) {}
|
func (w *window) ShowTextInput(show bool) {}
|
||||||
|
|
||||||
func (w *window) SetAnimating(anim bool) {
|
func (w *window) SetAnimating(anim bool) {
|
||||||
var animb C.BOOL
|
|
||||||
if anim {
|
if anim {
|
||||||
animb = 1
|
w.displayLink.Start()
|
||||||
|
} else {
|
||||||
|
w.displayLink.Stop()
|
||||||
}
|
}
|
||||||
v := w.view
|
|
||||||
C.CFRetain(v)
|
|
||||||
runOnMain(func() {
|
|
||||||
defer C.CFRelease(v)
|
|
||||||
C.gio_setAnimating(v, animb)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) setStage(stage system.Stage) {
|
func (w *window) setStage(stage system.Stage) {
|
||||||
@@ -136,14 +130,6 @@ func (w *window) setStage(stage system.Stage) {
|
|||||||
w.w.Event(system.StageEvent{Stage: stage})
|
w.w.Event(system.StageEvent{Stage: stage})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onFrameCallback
|
|
||||||
func gio_onFrameCallback(view C.CFTypeRef) {
|
|
||||||
w, exists := lookupView(view)
|
|
||||||
if exists {
|
|
||||||
w.draw(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export gio_onKeys
|
//export gio_onKeys
|
||||||
func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) {
|
func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) {
|
||||||
str := C.GoString(cstr)
|
str := C.GoString(cstr)
|
||||||
@@ -222,6 +208,12 @@ func gio_onFocus(view C.CFTypeRef, focus C.BOOL) {
|
|||||||
w.w.Event(key.FocusEvent{Focus: focus == C.YES})
|
w.w.Event(key.FocusEvent{Focus: focus == C.YES})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export gio_onChangeScreen
|
||||||
|
func gio_onChangeScreen(view C.CFTypeRef, did uint64) {
|
||||||
|
w := mustView(view)
|
||||||
|
w.displayLink.SetDisplayID(did)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *window) draw(sync bool) {
|
func (w *window) draw(sync bool) {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
wf, hf, scale := w.width, w.height, w.scale
|
wf, hf, scale := w.width, w.height, w.scale
|
||||||
@@ -256,6 +248,7 @@ func configFor(scale float32) config {
|
|||||||
//export gio_onTerminate
|
//export gio_onTerminate
|
||||||
func gio_onTerminate(view C.CFTypeRef) {
|
func gio_onTerminate(view C.CFTypeRef) {
|
||||||
w := mustView(view)
|
w := mustView(view)
|
||||||
|
w.displayLink.Close()
|
||||||
deleteView(view)
|
deleteView(view)
|
||||||
w.w.Event(system.DestroyEvent{})
|
w.w.Event(system.DestroyEvent{})
|
||||||
}
|
}
|
||||||
@@ -279,6 +272,13 @@ func gio_onCreate(view C.CFTypeRef) {
|
|||||||
view: view,
|
view: view,
|
||||||
scale: scale,
|
scale: scale,
|
||||||
}
|
}
|
||||||
|
dl, err := NewDisplayLink(func() {
|
||||||
|
w.draw(false)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
w.displayLink = dl
|
||||||
wopts := <-mainWindow.out
|
wopts := <-mainWindow.out
|
||||||
w.w = wopts.window
|
w.w = wopts.window
|
||||||
w.w.SetDriver(w)
|
w.w.SetDriver(w)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
- (void)windowDidChangeScreen:(NSNotification *)notification {
|
- (void)windowDidChangeScreen:(NSNotification *)notification {
|
||||||
CGDirectDisplayID dispID = [[[self.window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue];
|
CGDirectDisplayID dispID = [[[self.window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue];
|
||||||
CFTypeRef view = (__bridge CFTypeRef)self.window.contentView;
|
CFTypeRef view = (__bridge CFTypeRef)self.window.contentView;
|
||||||
gio_updateDisplayLink(view, dispID);
|
gio_onChangeScreen(view, dispID);
|
||||||
}
|
}
|
||||||
- (void)windowDidBecomeKey:(NSNotification *)notification {
|
- (void)windowDidBecomeKey:(NSNotification *)notification {
|
||||||
gio_onFocus((__bridge CFTypeRef)self.window.contentView, YES);
|
gio_onFocus((__bridge CFTypeRef)self.window.contentView, YES);
|
||||||
@@ -81,6 +81,34 @@ CGFloat gio_getViewBackingScale(CFTypeRef viewRef) {
|
|||||||
return [view.window backingScaleFactor];
|
return [view.window backingScaleFactor];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
|
||||||
|
gio_onFrameCallback(dl);
|
||||||
|
return kCVReturnSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFTypeRef gio_createDisplayLink(void) {
|
||||||
|
CVDisplayLinkRef dl;
|
||||||
|
CVDisplayLinkCreateWithActiveCGDisplays(&dl);
|
||||||
|
CVDisplayLinkSetOutputCallback(dl, displayLinkCallback, nil);
|
||||||
|
return dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gio_startDisplayLink(CFTypeRef dl) {
|
||||||
|
return CVDisplayLinkStart((CVDisplayLinkRef)dl);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gio_stopDisplayLink(CFTypeRef dl) {
|
||||||
|
return CVDisplayLinkStop((CVDisplayLinkRef)dl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gio_releaseDisplayLink(CFTypeRef dl) {
|
||||||
|
CVDisplayLinkRelease((CVDisplayLinkRef)dl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
|
||||||
|
CVDisplayLinkSetCurrentCGDisplay((CVDisplayLinkRef)dl, (CGDirectDisplayID)did);
|
||||||
|
}
|
||||||
|
|
||||||
void gio_main(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height) {
|
void gio_main(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSView *view = (NSView *)CFBridgingRelease(viewRef);
|
NSView *view = (NSView *)CFBridgingRelease(viewRef);
|
||||||
|
|||||||
Reference in New Issue
Block a user