mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
app/internal/window: [macOS] run most callbacks on the main thread
Before this change, we had a viewDo that serialized a function on a view on a single goroutine. For better or worse, most callbacks in Cocoa happen on the main thread, so we might as well use that. The only exception is the frame callback from the CADisplayLink. We could force that through the main thread as well, but for efficiency settle with making the view-to-window map synchronized. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -42,42 +42,37 @@ type window struct {
|
||||
scale float32
|
||||
}
|
||||
|
||||
type viewCmd struct {
|
||||
view C.CFTypeRef
|
||||
f viewFunc
|
||||
}
|
||||
|
||||
type viewFunc func(views viewMap, view C.CFTypeRef)
|
||||
|
||||
type viewMap map[C.CFTypeRef]*window
|
||||
|
||||
var (
|
||||
viewOnce sync.Once
|
||||
viewCmds = make(chan viewCmd)
|
||||
viewAcks = make(chan struct{})
|
||||
)
|
||||
// viewMap is the mapping from Cocoa NSViews to Go windows.
|
||||
var viewMap sync.Map
|
||||
|
||||
var mainWindow = newWindowRendezvous()
|
||||
|
||||
var viewFactory func() C.CFTypeRef
|
||||
|
||||
func viewDo(view C.CFTypeRef, f viewFunc) {
|
||||
viewOnce.Do(func() {
|
||||
go runViewCmdLoop()
|
||||
})
|
||||
viewCmds <- viewCmd{view, f}
|
||||
<-viewAcks
|
||||
// mustView is like lookoupView, except that it panics
|
||||
// if the view isn't mapped.
|
||||
func mustView(view C.CFTypeRef) *window {
|
||||
w, ok := lookupView(view)
|
||||
if !ok {
|
||||
panic("no window view view")
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func runViewCmdLoop() {
|
||||
views := make(viewMap)
|
||||
for {
|
||||
select {
|
||||
case cmd := <-viewCmds:
|
||||
cmd.f(views, cmd.view)
|
||||
viewAcks <- struct{}{}
|
||||
}
|
||||
func lookupView(view C.CFTypeRef) (*window, bool) {
|
||||
w, exists := viewMap.Load(view)
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
return w.(*window), true
|
||||
}
|
||||
|
||||
func deleteView(view C.CFTypeRef) {
|
||||
viewMap.Delete(view)
|
||||
}
|
||||
|
||||
func insertView(view C.CFTypeRef, w *window) {
|
||||
viewMap.Store(view, w)
|
||||
}
|
||||
|
||||
func (w *window) contextView() C.CFTypeRef {
|
||||
@@ -102,46 +97,34 @@ func (w *window) setStage(stage system.Stage) {
|
||||
w.w.Event(system.StageEvent{Stage: stage})
|
||||
}
|
||||
|
||||
// Use a top level func for onFrameCallback to avoid
|
||||
// garbage from viewDo.
|
||||
func onFrameCmd(views viewMap, view C.CFTypeRef) {
|
||||
// CVDisplayLink does not run on the main thread,
|
||||
// so we have to ignore requests to windows being
|
||||
// deleted.
|
||||
if w, exists := views[view]; exists {
|
||||
w.draw(false)
|
||||
}
|
||||
}
|
||||
|
||||
//export gio_onFrameCallback
|
||||
func gio_onFrameCallback(view C.CFTypeRef) {
|
||||
viewDo(view, onFrameCmd)
|
||||
w, exists := lookupView(view)
|
||||
if exists {
|
||||
w.draw(false)
|
||||
}
|
||||
}
|
||||
|
||||
//export gio_onKeys
|
||||
func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) {
|
||||
str := C.GoString(cstr)
|
||||
kmods := convertMods(mods)
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
for _, k := range str {
|
||||
if n, ok := convertKey(k); ok {
|
||||
w.w.Event(key.Event{
|
||||
Name: n,
|
||||
Modifiers: kmods,
|
||||
})
|
||||
}
|
||||
w := mustView(view)
|
||||
for _, k := range str {
|
||||
if n, ok := convertKey(k); ok {
|
||||
w.w.Event(key.Event{
|
||||
Name: n,
|
||||
Modifiers: kmods,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//export gio_onText
|
||||
func gio_onText(view C.CFTypeRef, cstr *C.char) {
|
||||
str := C.GoString(cstr)
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.w.Event(key.EditEvent{Text: str})
|
||||
})
|
||||
w := mustView(view)
|
||||
w.w.Event(key.EditEvent{Text: str})
|
||||
}
|
||||
|
||||
//export gio_onMouse
|
||||
@@ -168,36 +151,30 @@ func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy
|
||||
btns |= pointer.ButtonMiddle
|
||||
}
|
||||
t := time.Duration(float64(ti)*float64(time.Second) + .5)
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
x, y := float32(x)*w.scale, float32(y)*w.scale
|
||||
dx, dy := float32(dx)*w.scale, float32(dy)*w.scale
|
||||
w.w.Event(pointer.Event{
|
||||
Type: typ,
|
||||
Source: pointer.Mouse,
|
||||
Time: t,
|
||||
Buttons: btns,
|
||||
Position: f32.Point{X: x, Y: y},
|
||||
Scroll: f32.Point{X: dx, Y: dy},
|
||||
Modifiers: convertMods(mods),
|
||||
})
|
||||
w := mustView(view)
|
||||
xf, yf := float32(x)*w.scale, float32(y)*w.scale
|
||||
dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale
|
||||
w.w.Event(pointer.Event{
|
||||
Type: typ,
|
||||
Source: pointer.Mouse,
|
||||
Time: t,
|
||||
Buttons: btns,
|
||||
Position: f32.Point{X: xf, Y: yf},
|
||||
Scroll: f32.Point{X: dxf, Y: dyf},
|
||||
Modifiers: convertMods(mods),
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onDraw
|
||||
func gio_onDraw(view C.CFTypeRef) {
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.draw(true)
|
||||
})
|
||||
w := mustView(view)
|
||||
w.draw(true)
|
||||
}
|
||||
|
||||
//export gio_onFocus
|
||||
func gio_onFocus(view C.CFTypeRef, focus C.BOOL) {
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.w.Event(key.FocusEvent{Focus: focus == C.YES})
|
||||
})
|
||||
w := mustView(view)
|
||||
w.w.Event(key.FocusEvent{Focus: focus == C.YES})
|
||||
}
|
||||
|
||||
func (w *window) draw(sync bool) {
|
||||
@@ -232,42 +209,34 @@ func configFor(scale float32) config {
|
||||
|
||||
//export gio_onTerminate
|
||||
func gio_onTerminate(view C.CFTypeRef) {
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
delete(views, view)
|
||||
w.w.Event(system.DestroyEvent{})
|
||||
})
|
||||
w := mustView(view)
|
||||
deleteView(view)
|
||||
w.w.Event(system.DestroyEvent{})
|
||||
}
|
||||
|
||||
//export gio_onHide
|
||||
func gio_onHide(view C.CFTypeRef) {
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.setStage(system.StagePaused)
|
||||
})
|
||||
w := mustView(view)
|
||||
w.setStage(system.StagePaused)
|
||||
}
|
||||
|
||||
//export gio_onShow
|
||||
func gio_onShow(view C.CFTypeRef) {
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.setStage(system.StageRunning)
|
||||
})
|
||||
w := mustView(view)
|
||||
w.setStage(system.StageRunning)
|
||||
}
|
||||
|
||||
//export gio_onCreate
|
||||
func gio_onCreate(view C.CFTypeRef) {
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
scale := float32(C.gio_getViewBackingScale(view))
|
||||
w := &window{
|
||||
view: view,
|
||||
scale: scale,
|
||||
}
|
||||
wopts := <-mainWindow.out
|
||||
w.w = wopts.window
|
||||
w.w.SetDriver(w)
|
||||
views[view] = w
|
||||
})
|
||||
scale := float32(C.gio_getViewBackingScale(view))
|
||||
w := &window{
|
||||
view: view,
|
||||
scale: scale,
|
||||
}
|
||||
wopts := <-mainWindow.out
|
||||
w.w = wopts.window
|
||||
w.w.SetDriver(w)
|
||||
insertView(view, w)
|
||||
}
|
||||
|
||||
func NewWindow(win Callbacks, opts *Options) error {
|
||||
|
||||
Reference in New Issue
Block a user