From 18c2ba8e203c4871d14b5cacdd9397ad967a8ded Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Sun, 19 Sep 2021 09:57:41 +0200 Subject: [PATCH] app: replace Window.Config with ConfigEvent Unlike Raise, Close and other fire-and-forget methods on Window, Config calls driverRun because it needs to wait for the result. However, driverRun isn't guaranteed to block in all contexts. This change avoids the synchronization dance altogether by removing the Config method and introducing a ConfigEvent event. The event also makes it clear when the configuration changes. Signed-off-by: Elias Naur --- app/os.go | 11 +++++---- app/os_android.go | 22 +++++++++--------- app/os_ios.go | 5 ----- app/os_js.go | 35 +++++++++++++++-------------- app/os_macos.go | 26 +++++++++++---------- app/os_wayland.go | 57 +++++++++++++++++++++++++---------------------- app/os_windows.go | 24 +++++++++++--------- app/os_x11.go | 19 ++++++++-------- app/window.go | 11 --------- 9 files changed, 102 insertions(+), 108 deletions(-) diff --git a/app/os.go b/app/os.go index 812222bc..d396a4a7 100644 --- a/app/os.go +++ b/app/os.go @@ -46,6 +46,11 @@ type Config struct { CustomRenderer bool } +// ConfigEvent is sent whenever the configuration of a Window changes. +type ConfigEvent struct { + Config Config +} + func (c *Config) apply(m unit.Metric, options []Option) { for _, o := range options { o(m, c) @@ -135,9 +140,6 @@ type driver interface { // Configure the window. Configure([]Option) - // Config returns the current configuration. - Config() Config - // SetCursor updates the current cursor to name. SetCursor(name pointer.CursorName) @@ -187,4 +189,5 @@ func newWindowRendezvous() *windowRendezvous { return wr } -func (_ wakeupEvent) ImplementsEvent() {} +func (wakeupEvent) ImplementsEvent() {} +func (ConfigEvent) ImplementsEvent() {} diff --git a/app/os_android.go b/app/os_android.go index eb7a4138..a0d36f28 100644 --- a/app/os_android.go +++ b/app/os_android.go @@ -507,19 +507,20 @@ func (w *window) SetAnimating(anim bool) { } func (w *window) draw(sync bool) { - width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win) - if width == 0 || height == 0 { + size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win))) + if size != w.config.Size { + w.config.Size = size + w.callbacks.Event(ConfigEvent{Config: w.config}) + } + if size.X == 0 || size.Y == 0 { return } const inchPrDp = 1.0 / 160 ppdp := float32(w.dpi) * inchPrDp w.callbacks.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: int(width), - Y: int(height), - }, + Now: time.Now(), + Size: w.config.Size, Insets: w.insets, Metric: unit.Metric{ PxPerDp: ppdp, @@ -815,13 +816,12 @@ func (w *window) Configure(options []Option) { w.config.Mode = Windowed } } + if w.config != prev { + w.callbacks.Event(ConfigEvent{Config: w.config}) + } }) } -func (w *window) Config() Config { - return w.config -} - func (w *window) Raise() {} func (w *window) SetCursor(name pointer.CursorName) { diff --git a/app/os_ios.go b/app/os_ios.go index 92c32fa2..54a81468 100644 --- a/app/os_ios.go +++ b/app/os_ios.go @@ -95,7 +95,6 @@ type window struct { visible bool cursor pointer.CursorName - config Config pointerMap []C.CFTypeRef } @@ -271,10 +270,6 @@ func (w *window) WriteClipboard(s string) { func (w *window) Configure([]Option) {} -func (w *window) Config() Config { - return w.config -} - func (w *window) Raise() {} func (w *window) SetAnimating(anim bool) { diff --git a/app/os_js.go b/app/os_js.go index 5ccaf0da..4950e501 100644 --- a/app/os_js.go +++ b/app/os_js.go @@ -94,12 +94,12 @@ func newWindow(win *callbacks, options []Option) error { }) w.addEventListeners() w.addHistory() - w.Configure(options) w.w = win go func() { defer w.cleanup() w.w.SetDriver(w) + w.Configure(options) w.blur() w.w.Event(system.StageEvent{Stage: system.StageRunning}) w.resize() @@ -528,10 +528,9 @@ func (w *window) Configure(options []Option) { w.config.Orientation = cnf.Orientation w.orientation(cnf.Orientation) } -} - -func (w *window) Config() Config { - return w.config + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } } func (w *window) Raise() {} @@ -571,8 +570,14 @@ func (w *window) resize() { w.scale = float32(w.window.Get("devicePixelRatio").Float()) rect := w.cnv.Call("getBoundingClientRect") - w.config.Size.X = int(rect.Get("width").Float()) * int(w.scale) - w.config.Size.Y = int(rect.Get("height").Float()) * int(w.scale) + size := image.Point{ + X: int(float32(rect.Get("width").Float()) * w.scale), + Y: int(float32(rect.Get("height").Float()) * w.scale), + } + if size != w.config.Size { + w.config.Size = size + w.w.Event(ConfigEvent{Config: w.config}) + } if vx, vy := w.visualViewport.Get("width"), w.visualViewport.Get("height"); !vx.IsUndefined() && !vy.IsUndefined() { w.inset.X = float32(w.config.Size.X) - float32(vx.Float())*w.scale @@ -588,18 +593,14 @@ func (w *window) resize() { } func (w *window) draw(sync bool) { - width, height, insets, metric := w.getConfig() - if metric == (unit.Metric{}) || width == 0 || height == 0 { + size, insets, metric := w.getConfig() + if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 { return } - w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, + Now: time.Now(), + Size: size, Insets: insets, Metric: metric, }, @@ -607,8 +608,8 @@ func (w *window) draw(sync bool) { }) } -func (w *window) getConfig() (int, int, system.Insets, unit.Metric) { - return w.config.Size.X, w.config.Size.Y, system.Insets{ +func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) { + return image.Pt(w.config.Size.X, w.config.Size.Y), system.Insets{ Bottom: unit.Px(w.inset.Y), Right: unit.Px(w.inset.X), }, unit.Metric{ diff --git a/app/os_macos.go b/app/os_macos.go index 62426a91..1ee4df7e 100644 --- a/app/os_macos.go +++ b/app/os_macos.go @@ -252,10 +252,9 @@ func (w *window) Configure(options []Option) { C.toggleFullScreen(w.window) } } -} - -func (w *window) Config() Config { - return w.config + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } } func (w *window) SetCursor(name pointer.CursorName) { @@ -389,20 +388,23 @@ func gio_onChangeScreen(view C.CFTypeRef, did uint64) { func (w *window) draw() { w.scale = float32(C.getViewBackingScale(w.view)) wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view)) - if wf == 0 || hf == 0 { + sz := image.Point{ + X: int(wf*w.scale + .5), + Y: int(hf*w.scale + .5), + } + if sz != w.config.Size { + w.config.Size = sz + w.w.Event(ConfigEvent{Config: w.config}) + } + if sz.X == 0 || sz.Y == 0 { return } - width := int(wf*w.scale + .5) - height := int(hf*w.scale + .5) cfg := configFor(w.scale) w.setStage(system.StageRunning) w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, + Now: time.Now(), + Size: w.config.Size, Metric: cfg, }, Sync: true, diff --git a/app/os_wayland.go b/app/os_wayland.go index 0753fcd2..3a6112d6 100644 --- a/app/os_wayland.go +++ b/app/os_wayland.go @@ -187,7 +187,9 @@ type window struct { serial C.uint32_t newScale bool scale int - config Config + // size is the unscaled window size (unlike config.Size which is scaled). + size image.Point + config Config wakeups chan struct{} } @@ -222,7 +224,7 @@ func init() { wlDriver = newWLWindow } -func newWLWindow(window *callbacks, options []Option) error { +func newWLWindow(callbacks *callbacks, options []Option) error { d, err := newWLDisplay() if err != nil { return err @@ -232,10 +234,14 @@ func newWLWindow(window *callbacks, options []Option) error { d.destroy() return err } - w.w = window + w.w = callbacks go func() { defer d.destroy() defer w.destroy() + // Finish and commit setup from createNativeWindow. + w.Configure(options) + C.wl_surface_commit(w.surf) + w.w.SetDriver(w) if err := w.loop(); err != nil { panic(err) @@ -356,15 +362,12 @@ func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) { C.xdg_surface_add_listener(w.wmSurf, &C.gio_xdg_surface_listener, unsafe.Pointer(w.surf)) C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf)) - w.Configure(options) - if d.decor != nil { // Request server side decorations. w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl) C.zxdg_toplevel_decoration_v1_set_mode(w.decor, C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) } w.updateOpaqueRegion() - C.wl_surface_commit(w.surf) return w, nil } @@ -487,8 +490,7 @@ func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) { func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel, width, height C.int32_t, states *C.struct_wl_array) { w := callbackLoad(data).(*window) if width != 0 && height != 0 { - w.config.Size.X = int(width) - w.config.Size.Y = int(height) + w.size = image.Pt(int(width), int(height)) w.updateOpaqueRegion() } } @@ -855,7 +857,7 @@ func (w *window) flushFling() { w.fling.xExtrapolation = fling.Extrapolation{} w.fling.yExtrapolation = fling.Extrapolation{} vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity))) - _, _, c := w.getConfig() + _, c := w.getConfig() if !w.fling.anim.Start(c, time.Now(), vel) { return } @@ -908,11 +910,12 @@ func (w *window) WriteClipboard(s string) { } func (w *window) Configure(options []Option) { - _, _, cfg := w.getConfig() + _, cfg := w.getConfig() prev := w.config cnf := w.config cnf.apply(cfg, options) if prev.Size != cnf.Size { + w.size = image.Pt(cnf.Size.X/w.scale, cnf.Size.Y/w.scale) w.config.Size = cnf.Size } if prev.Title != cnf.Title { @@ -921,10 +924,9 @@ func (w *window) Configure(options []Option) { C.xdg_toplevel_set_title(w.topLvl, title) C.free(unsafe.Pointer(title)) } -} - -func (w *window) Config() Config { - return w.config + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } } func (w *window) Raise() {} @@ -1374,7 +1376,7 @@ func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) { func (w *window) updateOpaqueRegion() { reg := C.wl_compositor_create_region(w.disp.compositor) - C.wl_region_add(reg, 0, 0, C.int32_t(w.config.Size.X), C.int32_t(w.config.Size.Y)) + C.wl_region_add(reg, 0, 0, C.int32_t(w.size.X), C.int32_t(w.size.Y)) C.wl_surface_set_opaque_region(w.surf, reg) C.wl_region_destroy(reg) } @@ -1404,9 +1406,9 @@ func (w *window) updateOutputs() { } } -func (w *window) getConfig() (int, int, unit.Metric) { - width, height := w.config.Size.X*w.scale, w.config.Size.Y*w.scale - return width, height, unit.Metric{ +func (w *window) getConfig() (image.Point, unit.Metric) { + size := w.size.Mul(w.scale) + return size, unit.Metric{ PxPerDp: w.ppdp * float32(w.scale), PxPerSp: w.ppsp * float32(w.scale), } @@ -1419,7 +1421,11 @@ func (w *window) draw(sync bool) { if dead || (!anim && !sync) { return } - width, height, cfg := w.getConfig() + size, cfg := w.getConfig() + if size != w.config.Size { + w.config.Size = size + w.w.Event(ConfigEvent{Config: w.config}) + } if cfg == (unit.Metric{}) { return } @@ -1430,11 +1436,8 @@ func (w *window) draw(sync bool) { } w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, + Now: time.Now(), + Size: w.config.Size, Metric: cfg, }, Sync: sync, @@ -1458,12 +1461,12 @@ func (w *window) surface() (*C.struct_wl_surface, int, int) { C.xdg_surface_ack_configure(w.wmSurf, w.serial) w.needAck = false } - width, height, scale := w.config.Size.X, w.config.Size.Y, w.scale if w.newScale { - C.wl_surface_set_buffer_scale(w.surf, C.int32_t(scale)) + C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale)) w.newScale = false } - return w.surf, width * scale, height * scale + sz, _ := w.getConfig() + return w.surf, sz.X, sz.Y } func (w *window) ShowTextInput(show bool) {} diff --git a/app/os_windows.go b/app/os_windows.go index 59a9a98f..c9ac8b47 100644 --- a/app/os_windows.go +++ b/app/os_windows.go @@ -428,20 +428,23 @@ func (w *window) setStage(s system.Stage) { func (w *window) draw(sync bool) { var r windows.Rect windows.GetClientRect(w.hwnd, &r) - w.config.Size.X = int(r.Right - r.Left) - w.config.Size.Y = int(r.Bottom - r.Top) + size := image.Point{ + X: int(r.Right - r.Left), + Y: int(r.Bottom - r.Top), + } if w.config.Size.X == 0 || w.config.Size.Y == 0 { return } + if size != w.config.Size { + w.config.Size = size + w.w.Event(ConfigEvent{Config: w.config}) + } dpi := windows.GetWindowDPI(w.hwnd) cfg := configForDPI(dpi) w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: w.config.Size.X, - Y: w.config.Size.Y, - }, + Now: time.Now(), + Size: w.config.Size, Metric: cfg, }, Sync: sync, @@ -531,10 +534,9 @@ func (w *window) Configure(options []Option) { if prev.Mode != cnf.Mode { w.SetWindowMode(cnf.Mode) } -} - -func (w *window) Config() Config { - return w.config + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } } func (w *window) SetWindowMode(mode WindowMode) { diff --git a/app/os_x11.go b/app/os_x11.go index 42d52bf4..1b01020c 100644 --- a/app/os_x11.go +++ b/app/os_x11.go @@ -161,10 +161,9 @@ func (w *x11Window) Configure(options []Option) { if prev.Mode != cnf.Mode { w.SetWindowMode(cnf.Mode) } -} - -func (w *x11Window) Config() Config { - return w.config + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } } func (w *x11Window) Raise() { @@ -355,11 +354,8 @@ loop: if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 { w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: w.config.Size.X, - Y: w.config.Size.Y, - }, + Now: time.Now(), + Size: w.config.Size, Metric: w.metric, }, Sync: syn, @@ -516,7 +512,10 @@ func (h *x11EventHandler) handleEvents() bool { w.w.Event(key.FocusEvent{Focus: false}) case C.ConfigureNotify: // window configuration change cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev)) - w.config.Size = image.Pt(int(cevt.width), int(cevt.height)) + if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size { + w.config.Size = sz + w.w.Event(ConfigEvent{Config: w.config}) + } // redraw will be done by a later expose event case C.SelectionNotify: cevt := (*C.XSelectionEvent)(unsafe.Pointer(xev)) diff --git a/app/window.go b/app/window.go index 4669b9af..1a286590 100644 --- a/app/window.go +++ b/app/window.go @@ -263,17 +263,6 @@ func (w *Window) Option(opts ...Option) { }) } -// Config returns the Window configuration. -// -// A FrameEvent will occur whenever the configuration changes. -func (w *Window) Config() Config { - var cnf Config - w.driverRun(func(d driver) { - cnf = d.Config() - }) - return cnf -} - // ReadClipboard initiates a read of the clipboard in the form // of a clipboard.Event. Multiple reads may be coalesced // to a single event.