mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-02 07:57:29 +00:00
ui/app: (macOS) serialize access to the views map
The CVDisplayLink used for synchronizing rendering to the display refresh rate runs on a separate thread from the main thread. View callbacks must therefore be serialized. Implement that by introducing a singleton goroutine that owns the view map and runs incoming commands. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+92
-35
@@ -15,6 +15,7 @@ import (
|
||||
"errors"
|
||||
"image"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
@@ -34,11 +35,43 @@ type window struct {
|
||||
stage Stage
|
||||
}
|
||||
|
||||
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{})
|
||||
)
|
||||
|
||||
var mainWindow = newWindowRendezvous()
|
||||
|
||||
var viewFactory func() C.CFTypeRef
|
||||
|
||||
var views = make(map[C.CFTypeRef]*window)
|
||||
func viewDo(view C.CFTypeRef, f viewFunc) {
|
||||
viewOnce.Do(func() {
|
||||
go runViewCmdLoop()
|
||||
})
|
||||
viewCmds <- viewCmd{view, f}
|
||||
<-viewAcks
|
||||
}
|
||||
|
||||
func runViewCmdLoop() {
|
||||
views := make(viewMap)
|
||||
for {
|
||||
select {
|
||||
case cmd := <-viewCmds:
|
||||
cmd.f(views, cmd.view)
|
||||
viewAcks <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *window) contextView() C.CFTypeRef {
|
||||
return w.view
|
||||
@@ -62,12 +95,18 @@ func (w *window) setStage(stage Stage) {
|
||||
w.w.event(StageEvent{stage})
|
||||
}
|
||||
|
||||
//export gio_onFrameCallback
|
||||
func gio_onFrameCallback(view C.CFTypeRef) {
|
||||
// Use a top level func for onFrameCallback to avoid
|
||||
// garbage from viewDo.
|
||||
func onFrameCmd(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.draw(false)
|
||||
}
|
||||
|
||||
//export gio_onFrameCallback
|
||||
func gio_onFrameCallback(view C.CFTypeRef) {
|
||||
viewDo(view, onFrameCmd)
|
||||
}
|
||||
|
||||
//export gio_onKeys
|
||||
func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) {
|
||||
str := C.GoString(cstr)
|
||||
@@ -78,19 +117,23 @@ func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger)
|
||||
if mods&C.NSEventModifierFlagShift != 0 {
|
||||
kmods |= key.ModShift
|
||||
}
|
||||
w := views[view]
|
||||
for _, k := range str {
|
||||
if n, ok := convertKey(k); ok {
|
||||
w.w.event(key.ChordEvent{Name: n, Modifiers: kmods})
|
||||
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.ChordEvent{Name: n, Modifiers: kmods})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onText
|
||||
func gio_onText(view C.CFTypeRef, cstr *C.char) {
|
||||
str := C.GoString(cstr)
|
||||
w := views[view]
|
||||
w.w.event(key.EditEvent{Text: str})
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.w.event(key.EditEvent{Text: str})
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onMouse
|
||||
@@ -107,26 +150,32 @@ func gio_onMouse(view C.CFTypeRef, cdir C.int, x, y, dx, dy C.CGFloat, ti C.doub
|
||||
panic("invalid direction")
|
||||
}
|
||||
t := time.Duration(float64(ti)*float64(time.Second) + .5)
|
||||
w := views[view]
|
||||
w.w.event(pointer.Event{
|
||||
Type: typ,
|
||||
Source: pointer.Mouse,
|
||||
Time: t,
|
||||
Position: f32.Point{X: float32(x), Y: float32(y)},
|
||||
Scroll: f32.Point{X: float32(dx), Y: float32(dy)},
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.w.event(pointer.Event{
|
||||
Type: typ,
|
||||
Source: pointer.Mouse,
|
||||
Time: t,
|
||||
Position: f32.Point{X: float32(x), Y: float32(y)},
|
||||
Scroll: f32.Point{X: float32(dx), Y: float32(dy)},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onDraw
|
||||
func gio_onDraw(view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.draw(true)
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.draw(true)
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onFocus
|
||||
func gio_onFocus(view C.CFTypeRef, focus C.BOOL) {
|
||||
w := views[view]
|
||||
w.w.event(key.FocusEvent{Focus: focus == C.YES})
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.w.event(key.FocusEvent{Focus: focus == C.YES})
|
||||
})
|
||||
}
|
||||
|
||||
func (w *window) draw(sync bool) {
|
||||
@@ -161,32 +210,40 @@ func getConfig() Config {
|
||||
|
||||
//export gio_onTerminate
|
||||
func gio_onTerminate(view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
delete(views, view)
|
||||
w.w.event(DestroyEvent{})
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
delete(views, view)
|
||||
w.w.event(DestroyEvent{})
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onHide
|
||||
func gio_onHide(view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.setStage(StagePaused)
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.setStage(StagePaused)
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onShow
|
||||
func gio_onShow(view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.setStage(StageRunning)
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := views[view]
|
||||
w.setStage(StageRunning)
|
||||
})
|
||||
}
|
||||
|
||||
//export gio_onCreate
|
||||
func gio_onCreate(view C.CFTypeRef) {
|
||||
w := &window{
|
||||
view: view,
|
||||
}
|
||||
wopts := <-mainWindow.out
|
||||
w.w = wopts.window
|
||||
w.w.setDriver(w)
|
||||
views[view] = w
|
||||
viewDo(view, func(views viewMap, view C.CFTypeRef) {
|
||||
w := &window{
|
||||
view: view,
|
||||
}
|
||||
wopts := <-mainWindow.out
|
||||
w.w = wopts.window
|
||||
w.w.setDriver(w)
|
||||
views[view] = w
|
||||
})
|
||||
}
|
||||
|
||||
func createWindow(win *Window, opts *WindowOptions) error {
|
||||
|
||||
Reference in New Issue
Block a user