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 <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-09-19 09:57:41 +02:00
parent 64bcb1ccd5
commit 18c2ba8e20
9 changed files with 102 additions and 108 deletions
+7 -4
View File
@@ -46,6 +46,11 @@ type Config struct {
CustomRenderer bool 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) { func (c *Config) apply(m unit.Metric, options []Option) {
for _, o := range options { for _, o := range options {
o(m, c) o(m, c)
@@ -135,9 +140,6 @@ type driver interface {
// Configure the window. // Configure the window.
Configure([]Option) Configure([]Option)
// Config returns the current configuration.
Config() Config
// SetCursor updates the current cursor to name. // SetCursor updates the current cursor to name.
SetCursor(name pointer.CursorName) SetCursor(name pointer.CursorName)
@@ -187,4 +189,5 @@ func newWindowRendezvous() *windowRendezvous {
return wr return wr
} }
func (_ wakeupEvent) ImplementsEvent() {} func (wakeupEvent) ImplementsEvent() {}
func (ConfigEvent) ImplementsEvent() {}
+10 -10
View File
@@ -507,8 +507,12 @@ func (w *window) SetAnimating(anim bool) {
} }
func (w *window) draw(sync bool) { func (w *window) draw(sync bool) {
width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win) size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win)))
if width == 0 || height == 0 { if size != w.config.Size {
w.config.Size = size
w.callbacks.Event(ConfigEvent{Config: w.config})
}
if size.X == 0 || size.Y == 0 {
return return
} }
const inchPrDp = 1.0 / 160 const inchPrDp = 1.0 / 160
@@ -516,10 +520,7 @@ func (w *window) draw(sync bool) {
w.callbacks.Event(frameEvent{ w.callbacks.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: w.config.Size,
X: int(width),
Y: int(height),
},
Insets: w.insets, Insets: w.insets,
Metric: unit.Metric{ Metric: unit.Metric{
PxPerDp: ppdp, PxPerDp: ppdp,
@@ -815,13 +816,12 @@ func (w *window) Configure(options []Option) {
w.config.Mode = Windowed 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) Raise() {}
func (w *window) SetCursor(name pointer.CursorName) { func (w *window) SetCursor(name pointer.CursorName) {
-5
View File
@@ -95,7 +95,6 @@ type window struct {
visible bool visible bool
cursor pointer.CursorName cursor pointer.CursorName
config Config
pointerMap []C.CFTypeRef pointerMap []C.CFTypeRef
} }
@@ -271,10 +270,6 @@ func (w *window) WriteClipboard(s string) {
func (w *window) Configure([]Option) {} func (w *window) Configure([]Option) {}
func (w *window) Config() Config {
return w.config
}
func (w *window) Raise() {} func (w *window) Raise() {}
func (w *window) SetAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
+17 -16
View File
@@ -94,12 +94,12 @@ func newWindow(win *callbacks, options []Option) error {
}) })
w.addEventListeners() w.addEventListeners()
w.addHistory() w.addHistory()
w.Configure(options)
w.w = win w.w = win
go func() { go func() {
defer w.cleanup() defer w.cleanup()
w.w.SetDriver(w) w.w.SetDriver(w)
w.Configure(options)
w.blur() w.blur()
w.w.Event(system.StageEvent{Stage: system.StageRunning}) w.w.Event(system.StageEvent{Stage: system.StageRunning})
w.resize() w.resize()
@@ -528,10 +528,9 @@ func (w *window) Configure(options []Option) {
w.config.Orientation = cnf.Orientation w.config.Orientation = cnf.Orientation
w.orientation(cnf.Orientation) w.orientation(cnf.Orientation)
} }
} if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
func (w *window) Config() Config { }
return w.config
} }
func (w *window) Raise() {} func (w *window) Raise() {}
@@ -571,8 +570,14 @@ func (w *window) resize() {
w.scale = float32(w.window.Get("devicePixelRatio").Float()) w.scale = float32(w.window.Get("devicePixelRatio").Float())
rect := w.cnv.Call("getBoundingClientRect") rect := w.cnv.Call("getBoundingClientRect")
w.config.Size.X = int(rect.Get("width").Float()) * int(w.scale) size := image.Point{
w.config.Size.Y = int(rect.Get("height").Float()) * int(w.scale) 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() { 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 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) { func (w *window) draw(sync bool) {
width, height, insets, metric := w.getConfig() size, insets, metric := w.getConfig()
if metric == (unit.Metric{}) || width == 0 || height == 0 { if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 {
return return
} }
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: size,
X: width,
Y: height,
},
Insets: insets, Insets: insets,
Metric: metric, Metric: metric,
}, },
@@ -607,8 +608,8 @@ func (w *window) draw(sync bool) {
}) })
} }
func (w *window) getConfig() (int, int, system.Insets, unit.Metric) { func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) {
return w.config.Size.X, w.config.Size.Y, system.Insets{ return image.Pt(w.config.Size.X, w.config.Size.Y), system.Insets{
Bottom: unit.Px(w.inset.Y), Bottom: unit.Px(w.inset.Y),
Right: unit.Px(w.inset.X), Right: unit.Px(w.inset.X),
}, unit.Metric{ }, unit.Metric{
+13 -11
View File
@@ -252,10 +252,9 @@ func (w *window) Configure(options []Option) {
C.toggleFullScreen(w.window) C.toggleFullScreen(w.window)
} }
} }
} if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
func (w *window) Config() Config { }
return w.config
} }
func (w *window) SetCursor(name pointer.CursorName) { func (w *window) SetCursor(name pointer.CursorName) {
@@ -389,20 +388,23 @@ func gio_onChangeScreen(view C.CFTypeRef, did uint64) {
func (w *window) draw() { func (w *window) draw() {
w.scale = float32(C.getViewBackingScale(w.view)) w.scale = float32(C.getViewBackingScale(w.view))
wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(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 return
} }
width := int(wf*w.scale + .5)
height := int(hf*w.scale + .5)
cfg := configFor(w.scale) cfg := configFor(w.scale)
w.setStage(system.StageRunning) w.setStage(system.StageRunning)
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: w.config.Size,
X: width,
Y: height,
},
Metric: cfg, Metric: cfg,
}, },
Sync: true, Sync: true,
+28 -25
View File
@@ -187,6 +187,8 @@ type window struct {
serial C.uint32_t serial C.uint32_t
newScale bool newScale bool
scale int scale int
// size is the unscaled window size (unlike config.Size which is scaled).
size image.Point
config Config config Config
wakeups chan struct{} wakeups chan struct{}
@@ -222,7 +224,7 @@ func init() {
wlDriver = newWLWindow wlDriver = newWLWindow
} }
func newWLWindow(window *callbacks, options []Option) error { func newWLWindow(callbacks *callbacks, options []Option) error {
d, err := newWLDisplay() d, err := newWLDisplay()
if err != nil { if err != nil {
return err return err
@@ -232,10 +234,14 @@ func newWLWindow(window *callbacks, options []Option) error {
d.destroy() d.destroy()
return err return err
} }
w.w = window w.w = callbacks
go func() { go func() {
defer d.destroy() defer d.destroy()
defer w.destroy() defer w.destroy()
// Finish and commit setup from createNativeWindow.
w.Configure(options)
C.wl_surface_commit(w.surf)
w.w.SetDriver(w) w.w.SetDriver(w)
if err := w.loop(); err != nil { if err := w.loop(); err != nil {
panic(err) 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_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)) C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf))
w.Configure(options)
if d.decor != nil { if d.decor != nil {
// Request server side decorations. // Request server side decorations.
w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl) 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) C.zxdg_toplevel_decoration_v1_set_mode(w.decor, C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE)
} }
w.updateOpaqueRegion() w.updateOpaqueRegion()
C.wl_surface_commit(w.surf)
return w, nil 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) { 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) w := callbackLoad(data).(*window)
if width != 0 && height != 0 { if width != 0 && height != 0 {
w.config.Size.X = int(width) w.size = image.Pt(int(width), int(height))
w.config.Size.Y = int(height)
w.updateOpaqueRegion() w.updateOpaqueRegion()
} }
} }
@@ -855,7 +857,7 @@ func (w *window) flushFling() {
w.fling.xExtrapolation = fling.Extrapolation{} w.fling.xExtrapolation = fling.Extrapolation{}
w.fling.yExtrapolation = fling.Extrapolation{} w.fling.yExtrapolation = fling.Extrapolation{}
vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity))) 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) { if !w.fling.anim.Start(c, time.Now(), vel) {
return return
} }
@@ -908,11 +910,12 @@ func (w *window) WriteClipboard(s string) {
} }
func (w *window) Configure(options []Option) { func (w *window) Configure(options []Option) {
_, _, cfg := w.getConfig() _, cfg := w.getConfig()
prev := w.config prev := w.config
cnf := w.config cnf := w.config
cnf.apply(cfg, options) cnf.apply(cfg, options)
if prev.Size != cnf.Size { if prev.Size != cnf.Size {
w.size = image.Pt(cnf.Size.X/w.scale, cnf.Size.Y/w.scale)
w.config.Size = cnf.Size w.config.Size = cnf.Size
} }
if prev.Title != cnf.Title { if prev.Title != cnf.Title {
@@ -921,10 +924,9 @@ func (w *window) Configure(options []Option) {
C.xdg_toplevel_set_title(w.topLvl, title) C.xdg_toplevel_set_title(w.topLvl, title)
C.free(unsafe.Pointer(title)) C.free(unsafe.Pointer(title))
} }
} if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
func (w *window) Config() Config { }
return w.config
} }
func (w *window) Raise() {} 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() { func (w *window) updateOpaqueRegion() {
reg := C.wl_compositor_create_region(w.disp.compositor) 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_surface_set_opaque_region(w.surf, reg)
C.wl_region_destroy(reg) C.wl_region_destroy(reg)
} }
@@ -1404,9 +1406,9 @@ func (w *window) updateOutputs() {
} }
} }
func (w *window) getConfig() (int, int, unit.Metric) { func (w *window) getConfig() (image.Point, unit.Metric) {
width, height := w.config.Size.X*w.scale, w.config.Size.Y*w.scale size := w.size.Mul(w.scale)
return width, height, unit.Metric{ return size, unit.Metric{
PxPerDp: w.ppdp * float32(w.scale), PxPerDp: w.ppdp * float32(w.scale),
PxPerSp: w.ppsp * float32(w.scale), PxPerSp: w.ppsp * float32(w.scale),
} }
@@ -1419,7 +1421,11 @@ func (w *window) draw(sync bool) {
if dead || (!anim && !sync) { if dead || (!anim && !sync) {
return 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{}) { if cfg == (unit.Metric{}) {
return return
} }
@@ -1431,10 +1437,7 @@ func (w *window) draw(sync bool) {
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: w.config.Size,
X: width,
Y: height,
},
Metric: cfg, Metric: cfg,
}, },
Sync: sync, 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) C.xdg_surface_ack_configure(w.wmSurf, w.serial)
w.needAck = false w.needAck = false
} }
width, height, scale := w.config.Size.X, w.config.Size.Y, w.scale
if w.newScale { 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 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) {} func (w *window) ShowTextInput(show bool) {}
+12 -10
View File
@@ -428,20 +428,23 @@ func (w *window) setStage(s system.Stage) {
func (w *window) draw(sync bool) { func (w *window) draw(sync bool) {
var r windows.Rect var r windows.Rect
windows.GetClientRect(w.hwnd, &r) windows.GetClientRect(w.hwnd, &r)
w.config.Size.X = int(r.Right - r.Left) size := image.Point{
w.config.Size.Y = int(r.Bottom - r.Top) X: int(r.Right - r.Left),
Y: int(r.Bottom - r.Top),
}
if w.config.Size.X == 0 || w.config.Size.Y == 0 { if w.config.Size.X == 0 || w.config.Size.Y == 0 {
return return
} }
if size != w.config.Size {
w.config.Size = size
w.w.Event(ConfigEvent{Config: w.config})
}
dpi := windows.GetWindowDPI(w.hwnd) dpi := windows.GetWindowDPI(w.hwnd)
cfg := configForDPI(dpi) cfg := configForDPI(dpi)
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: w.config.Size,
X: w.config.Size.X,
Y: w.config.Size.Y,
},
Metric: cfg, Metric: cfg,
}, },
Sync: sync, Sync: sync,
@@ -531,10 +534,9 @@ func (w *window) Configure(options []Option) {
if prev.Mode != cnf.Mode { if prev.Mode != cnf.Mode {
w.SetWindowMode(cnf.Mode) w.SetWindowMode(cnf.Mode)
} }
} if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
func (w *window) Config() Config { }
return w.config
} }
func (w *window) SetWindowMode(mode WindowMode) { func (w *window) SetWindowMode(mode WindowMode) {
+8 -9
View File
@@ -161,10 +161,9 @@ func (w *x11Window) Configure(options []Option) {
if prev.Mode != cnf.Mode { if prev.Mode != cnf.Mode {
w.SetWindowMode(cnf.Mode) w.SetWindowMode(cnf.Mode)
} }
} if w.config != prev {
w.w.Event(ConfigEvent{Config: w.config})
func (w *x11Window) Config() Config { }
return w.config
} }
func (w *x11Window) Raise() { func (w *x11Window) Raise() {
@@ -356,10 +355,7 @@ loop:
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: w.config.Size,
X: w.config.Size.X,
Y: w.config.Size.Y,
},
Metric: w.metric, Metric: w.metric,
}, },
Sync: syn, Sync: syn,
@@ -516,7 +512,10 @@ func (h *x11EventHandler) handleEvents() bool {
w.w.Event(key.FocusEvent{Focus: false}) w.w.Event(key.FocusEvent{Focus: false})
case C.ConfigureNotify: // window configuration change case C.ConfigureNotify: // window configuration change
cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev)) 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 // redraw will be done by a later expose event
case C.SelectionNotify: case C.SelectionNotify:
cevt := (*C.XSelectionEvent)(unsafe.Pointer(xev)) cevt := (*C.XSelectionEvent)(unsafe.Pointer(xev))
-11
View File
@@ -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 // ReadClipboard initiates a read of the clipboard in the form
// of a clipboard.Event. Multiple reads may be coalesced // of a clipboard.Event. Multiple reads may be coalesced
// to a single event. // to a single event.