diff --git a/app/os_darwin.go b/app/os_darwin.go index 8f82cc94..782cf099 100644 --- a/app/os_darwin.go +++ b/app/os_darwin.go @@ -15,8 +15,8 @@ __attribute__ ((visibility ("hidden"))) void gio_hideCursor(); __attribute__ ((visibility ("hidden"))) void gio_showCursor(); __attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID); -static bool isMainThread() { - return [NSThread isMainThread]; +static int isMainThread() { + return [NSThread isMainThread] ? 1 : 0; } static NSUInteger nsstringLength(CFTypeRef cstr) { @@ -77,7 +77,7 @@ var mainFuncs = make(chan func(), 1) // runOnMain runs the function on the main thread. func runOnMain(f func()) { - if C.isMainThread() { + if isMainThread() { f() return } @@ -87,6 +87,10 @@ func runOnMain(f func()) { }() } +func isMainThread() bool { + return C.isMainThread() != 0 +} + //export gio_dispatchMainFuncs func gio_dispatchMainFuncs() { for { @@ -259,10 +263,3 @@ func windowSetCursor(from, to pointer.Cursor) pointer.Cursor { C.gio_setCursor(C.NSUInteger(macosCursorID[to])) return to } - -func (w *window) wakeup() { - runOnMain(func() { - w.loop.Wakeup() - w.loop.FlushEvents() - }) -} diff --git a/app/os_darwin.m b/app/os_darwin.m deleted file mode 100644 index 69e18218..00000000 --- a/app/os_darwin.m +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -#import - -#include "_cgo_export.h" - -void gio_wakeupMainThread(void) { - dispatch_async(dispatch_get_main_queue(), ^{ - gio_dispatchMainFuncs(); - }); -} diff --git a/app/os_ios.go b/app/os_ios.go index 34163e63..d2880b62 100644 --- a/app/os_ios.go +++ b/app/os_ios.go @@ -396,5 +396,12 @@ func gio_runMain() { runMain() } +func (w *window) wakeup() { + runOnMain(func() { + w.loop.Wakeup() + w.loop.FlushEvents() + }) +} + func (UIKitViewEvent) implementsViewEvent() {} func (UIKitViewEvent) ImplementsEvent() {} diff --git a/app/os_ios.m b/app/os_ios.m index 1c3fe58f..61db7d1c 100644 --- a/app/os_ios.m +++ b/app/os_ios.m @@ -276,3 +276,9 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) { GioView *v = (__bridge GioView *)viewRef; v.handle = handle; } + +void gio_wakeupMainThread(void) { + dispatch_async(dispatch_get_main_queue(), ^{ + gio_dispatchMainFuncs(); + }); +} diff --git a/app/os_macos.go b/app/os_macos.go index b0e5526e..707cb2ac 100644 --- a/app/os_macos.go +++ b/app/os_macos.go @@ -39,7 +39,7 @@ import ( #define MOUSE_DOWN 3 #define MOUSE_SCROLL 4 -__attribute__ ((visibility ("hidden"))) void gio_main(void); +__attribute__ ((visibility ("hidden"))) void gio_initApp(void); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(void); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); __attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle); @@ -289,6 +289,16 @@ static void invalidateCharacterCoordinates(CFTypeRef viewRef) { } } } + +static void dispatchEvent(void) { + @autoreleasepool { + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + [NSApp sendEvent:event]; + } +} */ import "C" @@ -323,13 +333,16 @@ type window struct { config Config } -// launched is closed when applicationDidFinishLaunching is called. +// launched is closed after gio_initApp returns. var launched = make(chan struct{}) // nextTopLeft is the offset to use for the next window's call to // cascadeTopLeftFromPoint. var nextTopLeft C.NSPoint +// mainThreadWindow is the window currently in control of the main thread. +var mainThreadWindow *window + func windowFor(h C.uintptr_t) *window { return cgo.Handle(h).Value().(*window) } @@ -822,23 +835,51 @@ func (w *window) draw() { func (w *window) ProcessEvent(e event.Event) { w.w.ProcessEvent(e) - w.loop.FlushEvents() + // The main thread window deliver events in Event. + if w != mainThreadWindow { + w.loop.FlushEvents() + } } func (w *window) Event() event.Event { - return w.loop.Event() + if !isMainThread() { + return w.loop.Event() + } + mainThreadWindow = w + defer func() { mainThreadWindow = nil }() + for { + if evt, ok := w.loop.win.nextEvent(); ok { + return evt + } + C.dispatchEvent() + gio_dispatchMainFuncs() + } } func (w *window) Invalidate() { + if isMainThread() { + mainThreadWindow = w + defer func() { mainThreadWindow = nil }() + } w.loop.Invalidate() } func (w *window) Run(f func()) { + if isMainThread() { + mainThreadWindow = w + defer func() { mainThreadWindow = nil }() + } w.loop.Run(f) } func (w *window) Frame(frame *op.Ops) { - w.loop.Frame(frame) + if !isMainThread() { + w.loop.Frame(frame) + return + } + mainThreadWindow = w + defer func() { mainThreadWindow = nil }() + w.loop.win.ProcessFrame(frame, nil) } func configFor(scale float32) unit.Metric { @@ -898,20 +939,27 @@ func gio_onWindowed(h C.uintptr_t) { w.ProcessEvent(ConfigEvent{Config: w.config}) } -//export gio_onFinishLaunching -func gio_onFinishLaunching() { - close(launched) -} - func newWindow(win *callbacks, options []Option) { - <-launched - res := make(chan struct{}) - runOnMain(func() { - w := &window{ - redraw: make(chan struct{}, 1), - w: win, + w := &window{ + redraw: make(chan struct{}, 1), + w: win, + } + w.loop = newEventLoop(w.w, w.wakeup) + if isMainThread() { + mainThreadWindow = w + defer func() { mainThreadWindow = nil }() + select { + case <-launched: + default: + // If we're the main thread, initialize the GUI. + C.gio_initApp() + close(launched) } - w.loop = newEventLoop(w.w, w.wakeup) + } else { + <-launched + } + res := make(chan struct{}, 1) + runOnMain(func() { win.SetDriver(w) res <- struct{}{} if err := w.init(); err != nil { @@ -965,7 +1013,12 @@ func (w *window) init() error { } func osMain() { - C.gio_main() + C.gio_initApp() + close(launched) + for { + C.dispatchEvent() + gio_dispatchMainFuncs() + } } func convertKey(k rune) (key.Name, bool) { @@ -1052,5 +1105,14 @@ func convertMods(mods C.NSUInteger) key.Modifiers { return kmods } +func (w *window) wakeup() { + runOnMain(func() { + w.loop.Wakeup() + if w != mainThreadWindow { + w.loop.FlushEvents() + } + }) +} + func (AppKitViewEvent) implementsViewEvent() {} func (AppKitViewEvent) ImplementsEvent() {} diff --git a/app/os_macos.m b/app/os_macos.m index 768d109f..95f115a2 100644 --- a/app/os_macos.m +++ b/app/os_macos.m @@ -348,7 +348,6 @@ static BOOL isEqualNSCursor(NSCursor *c1, SEL name2) { // Don't pass commands up the responder chain. // They will end up in a beep. } - - (BOOL)hasMarkedText { int res = gio_hasMarkedText(self.handle); return res ? YES : NO; @@ -613,11 +612,22 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp activateIgnoringOtherApps:YES]; - gio_onFinishLaunching(); + // Force the [NSApp run] call to return. + [NSApp stop:nil]; + NSEvent *dummy = [NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:NSZeroPoint + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:dummy atStart:YES]; } @end -void gio_main() { +void gio_initApp() { @autoreleasepool { [GioApplication sharedApplication]; GioAppDelegate *del = [[GioAppDelegate alloc] init]; @@ -641,6 +651,22 @@ void gio_main() { globalWindowDel = [[GioWindowDelegate alloc] init]; + // Runs until stopped by applicationDidFinishLaunching. [NSApp run]; } } + +void gio_wakeupMainThread(void) { + @autoreleasepool { + NSEvent *dummy = [NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:NSZeroPoint + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:dummy atStart:YES]; + } +}