mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
app: [macOS] implement custom event dispatching
To get rid of app.Main, we need to control the main thread. The macOS [NSApp run] must be called on the main goroutine and never yields control. Implement the escape hatch which is calling [NSApp stop] to force [NSApp run] to return and allow us to fetch and dispatch events one at a time. This change is separate from the larger change removing app.Main to ease bisecting. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+7
-10
@@ -15,8 +15,8 @@ __attribute__ ((visibility ("hidden"))) void gio_hideCursor();
|
|||||||
__attribute__ ((visibility ("hidden"))) void gio_showCursor();
|
__attribute__ ((visibility ("hidden"))) void gio_showCursor();
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID);
|
__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID);
|
||||||
|
|
||||||
static bool isMainThread() {
|
static int isMainThread() {
|
||||||
return [NSThread isMainThread];
|
return [NSThread isMainThread] ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSUInteger nsstringLength(CFTypeRef cstr) {
|
static NSUInteger nsstringLength(CFTypeRef cstr) {
|
||||||
@@ -77,7 +77,7 @@ var mainFuncs = make(chan func(), 1)
|
|||||||
|
|
||||||
// runOnMain runs the function on the main thread.
|
// runOnMain runs the function on the main thread.
|
||||||
func runOnMain(f func()) {
|
func runOnMain(f func()) {
|
||||||
if C.isMainThread() {
|
if isMainThread() {
|
||||||
f()
|
f()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -87,6 +87,10 @@ func runOnMain(f func()) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isMainThread() bool {
|
||||||
|
return C.isMainThread() != 0
|
||||||
|
}
|
||||||
|
|
||||||
//export gio_dispatchMainFuncs
|
//export gio_dispatchMainFuncs
|
||||||
func gio_dispatchMainFuncs() {
|
func gio_dispatchMainFuncs() {
|
||||||
for {
|
for {
|
||||||
@@ -259,10 +263,3 @@ func windowSetCursor(from, to pointer.Cursor) pointer.Cursor {
|
|||||||
C.gio_setCursor(C.NSUInteger(macosCursorID[to]))
|
C.gio_setCursor(C.NSUInteger(macosCursorID[to]))
|
||||||
return to
|
return to
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) wakeup() {
|
|
||||||
runOnMain(func() {
|
|
||||||
w.loop.Wakeup()
|
|
||||||
w.loop.FlushEvents()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
// SPDX-License-Identifier: Unlicense OR MIT
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
#include "_cgo_export.h"
|
|
||||||
|
|
||||||
void gio_wakeupMainThread(void) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
gio_dispatchMainFuncs();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -396,5 +396,12 @@ func gio_runMain() {
|
|||||||
runMain()
|
runMain()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *window) wakeup() {
|
||||||
|
runOnMain(func() {
|
||||||
|
w.loop.Wakeup()
|
||||||
|
w.loop.FlushEvents()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (UIKitViewEvent) implementsViewEvent() {}
|
func (UIKitViewEvent) implementsViewEvent() {}
|
||||||
func (UIKitViewEvent) ImplementsEvent() {}
|
func (UIKitViewEvent) ImplementsEvent() {}
|
||||||
|
|||||||
@@ -276,3 +276,9 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
|
|||||||
GioView *v = (__bridge GioView *)viewRef;
|
GioView *v = (__bridge GioView *)viewRef;
|
||||||
v.handle = handle;
|
v.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gio_wakeupMainThread(void) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
gio_dispatchMainFuncs();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
+80
-18
@@ -39,7 +39,7 @@ import (
|
|||||||
#define MOUSE_DOWN 3
|
#define MOUSE_DOWN 3
|
||||||
#define MOUSE_SCROLL 4
|
#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_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"))) 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);
|
__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"
|
import "C"
|
||||||
|
|
||||||
@@ -323,13 +333,16 @@ type window struct {
|
|||||||
config Config
|
config Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// launched is closed when applicationDidFinishLaunching is called.
|
// launched is closed after gio_initApp returns.
|
||||||
var launched = make(chan struct{})
|
var launched = make(chan struct{})
|
||||||
|
|
||||||
// nextTopLeft is the offset to use for the next window's call to
|
// nextTopLeft is the offset to use for the next window's call to
|
||||||
// cascadeTopLeftFromPoint.
|
// cascadeTopLeftFromPoint.
|
||||||
var nextTopLeft C.NSPoint
|
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 {
|
func windowFor(h C.uintptr_t) *window {
|
||||||
return cgo.Handle(h).Value().(*window)
|
return cgo.Handle(h).Value().(*window)
|
||||||
}
|
}
|
||||||
@@ -822,23 +835,51 @@ func (w *window) draw() {
|
|||||||
|
|
||||||
func (w *window) ProcessEvent(e event.Event) {
|
func (w *window) ProcessEvent(e event.Event) {
|
||||||
w.w.ProcessEvent(e)
|
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 {
|
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() {
|
func (w *window) Invalidate() {
|
||||||
|
if isMainThread() {
|
||||||
|
mainThreadWindow = w
|
||||||
|
defer func() { mainThreadWindow = nil }()
|
||||||
|
}
|
||||||
w.loop.Invalidate()
|
w.loop.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Run(f func()) {
|
func (w *window) Run(f func()) {
|
||||||
|
if isMainThread() {
|
||||||
|
mainThreadWindow = w
|
||||||
|
defer func() { mainThreadWindow = nil }()
|
||||||
|
}
|
||||||
w.loop.Run(f)
|
w.loop.Run(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Frame(frame *op.Ops) {
|
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 {
|
func configFor(scale float32) unit.Metric {
|
||||||
@@ -898,20 +939,27 @@ func gio_onWindowed(h C.uintptr_t) {
|
|||||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onFinishLaunching
|
|
||||||
func gio_onFinishLaunching() {
|
|
||||||
close(launched)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWindow(win *callbacks, options []Option) {
|
func newWindow(win *callbacks, options []Option) {
|
||||||
<-launched
|
w := &window{
|
||||||
res := make(chan struct{})
|
redraw: make(chan struct{}, 1),
|
||||||
runOnMain(func() {
|
w: win,
|
||||||
w := &window{
|
}
|
||||||
redraw: make(chan struct{}, 1),
|
w.loop = newEventLoop(w.w, w.wakeup)
|
||||||
w: win,
|
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)
|
win.SetDriver(w)
|
||||||
res <- struct{}{}
|
res <- struct{}{}
|
||||||
if err := w.init(); err != nil {
|
if err := w.init(); err != nil {
|
||||||
@@ -965,7 +1013,12 @@ func (w *window) init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func osMain() {
|
func osMain() {
|
||||||
C.gio_main()
|
C.gio_initApp()
|
||||||
|
close(launched)
|
||||||
|
for {
|
||||||
|
C.dispatchEvent()
|
||||||
|
gio_dispatchMainFuncs()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertKey(k rune) (key.Name, bool) {
|
func convertKey(k rune) (key.Name, bool) {
|
||||||
@@ -1052,5 +1105,14 @@ func convertMods(mods C.NSUInteger) key.Modifiers {
|
|||||||
return kmods
|
return kmods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *window) wakeup() {
|
||||||
|
runOnMain(func() {
|
||||||
|
w.loop.Wakeup()
|
||||||
|
if w != mainThreadWindow {
|
||||||
|
w.loop.FlushEvents()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (AppKitViewEvent) implementsViewEvent() {}
|
func (AppKitViewEvent) implementsViewEvent() {}
|
||||||
func (AppKitViewEvent) ImplementsEvent() {}
|
func (AppKitViewEvent) ImplementsEvent() {}
|
||||||
|
|||||||
+29
-3
@@ -348,7 +348,6 @@ static BOOL isEqualNSCursor(NSCursor *c1, SEL name2) {
|
|||||||
// Don't pass commands up the responder chain.
|
// Don't pass commands up the responder chain.
|
||||||
// They will end up in a beep.
|
// They will end up in a beep.
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasMarkedText {
|
- (BOOL)hasMarkedText {
|
||||||
int res = gio_hasMarkedText(self.handle);
|
int res = gio_hasMarkedText(self.handle);
|
||||||
return res ? YES : NO;
|
return res ? YES : NO;
|
||||||
@@ -613,11 +612,22 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
|
|||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||||
[NSApp activateIgnoringOtherApps:YES];
|
[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
|
@end
|
||||||
|
|
||||||
void gio_main() {
|
void gio_initApp() {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
[GioApplication sharedApplication];
|
[GioApplication sharedApplication];
|
||||||
GioAppDelegate *del = [[GioAppDelegate alloc] init];
|
GioAppDelegate *del = [[GioAppDelegate alloc] init];
|
||||||
@@ -641,6 +651,22 @@ void gio_main() {
|
|||||||
|
|
||||||
globalWindowDel = [[GioWindowDelegate alloc] init];
|
globalWindowDel = [[GioWindowDelegate alloc] init];
|
||||||
|
|
||||||
|
// Runs until stopped by applicationDidFinishLaunching.
|
||||||
[NSApp run];
|
[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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user