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.