Newsletter, June 2022
Custom Window Decoration on More Platforms and Optimizations

This month saw an emphasis on improvements to the architecture document, performance, and desktop windowing system integration.

core gio

This month, macOS and Windows joined the Wayland backend with support for app.Decorated(false). This enables Gio applications to draw their own window decorations using Gio’s native operations. Thanks to Plato Team for sponsoring this work!

Additionally, performance discussions sparked by Dominik Honnef led to optimizations throughout Gio by Dominik, Egon, and Elias.

I mostly fixed HiDPI bugs in our Wayland backend.

Dominik also fixed numerous problems in the implementation of widget.Scrollbar.

There were 59 commits to core since the last newsletter.

Breaking API changes by author:

Elias Naur:

  • io/system: remove resize actions. Allowing clients to initiate resize gestures is a waste: macOS doesn’t support them, and the only reason we added them was to implement client-side decorations for Wayland. Now all desktop platforms implement resize gestures as needed, and we no longer need the system.Action actions. b53cdfef
  • io/system: add ActionInputOp to register window move gesture areas. The app.Window.Perform(ActionMove) is the wrong abstraction for initiating a move gesture: Windows needs to know the move gesture area at pointer move, and macOS needs to know the pointer button down event that triggers the move gesture. This change replaces Perform(ActionMove) with a new system.ActionInputOp that marks an area movable. 3f38e67c

Non-breaking changes by author:

Elias Naur:

  • gpu: re-align coverUniforms struct. Direct3D requires GPU vertex attribute structs sizes be a multiple 16. A cleanup commit removed an unusued field, and broke that assumption. 5ff316ed
  • app: restore IME snippet after an EditorReplace. Commit 02732037436f547717ec53073ce8b295329c9bd8 removed the snippet restore event, which broke IME on macOS and Windows. bf6371c8
  • app: draw fallback decorations on top. Before this change, client-side decorations for Wayland were drawn before client content, which was prevented from drawing over decorations with a clip. While visually correct, resize handles would’t work as long as client listeners are near the window edges to swallow pointer input. e31aa356
  • app: fix racing app.Window.Perform and app.Window.Option. 43116400
  • app: replace driver.Close with Perform(ActionClose). 371de346
  • gpu/headless: tweak test to pass on MacBook Pro M1. Apparently, there is a rounding error somewhere in the pipeline from clearing a FBO to downloading its contents on at least one Apple M1 machine. Tweak the test colors a bit to make it pass. 3ca4c985
  • app: [Wayland] don’t allow changes to decoration mode. We’re about to enable platform support for switching native window decorations on and off. However, the Wayland platform only supports server-side switching of decoration mode, not (yet) client-side. Thus, don’t switch mode even when asked to. 9f91fecd
  • app: don’t draw fallback decorations for undecorated windows.. Until now, fallback decorations were only needed for Wayland client-side decorations. We’re about to support app.Decorated(false) one some platforms, where Window should not fall back to drawing its own decorations. 6a5d3f99
  • widget/material: make DecorationsStyle method receivers by-value. Style values are ephemeral, and pointer methods can’t be called in the same expression a style value is constructed. Matches other style types. 8a938294
  • app: [macOS] add support custom window decorations. 8457df2d, c5e07ba0
  • app: guarantee a ConfigEvent for every Window.Configure call. Not only is the client guaranteed a ConfigEvent, but app.Window can assume that an unsupported decoration change will be corrected (by a ConfigEvent with Decorated forced to the supported value). 69e4a3cf
  • app: [Windows] implement custom window decoration support. 59480066, 5cf65706, df43ba8b, 8ef0ad43, 5e7bf171, 2973c7fa, 5bdf5950, 45443a2c, fd3a3eb1, 5dc8e0e3, 4d593927
  • app: [Wayland] hard-code border resize gestures. We’re about to remove the system.Action machinery for initiating resize gestures. This is the Wayland implementation that hard-codes the border drag gesture for resizing. b34dc635
  • app: [Wayland] ensure monitor scale changes propagate to active windows. 546d971e
  • app: remove ackEvent, tighten error check. fa538f21
  • internal/gl: avoid excessive Cgo pointer checks. As suggested by Egon Elbre, passing a large struct of function pointers forces Cgo checks on all of the pointer on every Cgo call. This change instead passes only the relevant function pointer. dab79680
  • layout: truncate negative List.Position.First positions to 0. b82b9b25
  • app: call driver Perform and Configure after idling. Before this change, Perform and Configure could be called during the event processing where additional events would be queued. However, a Maximize animation on macOS works by repeatedly sending draw requests, and they must not be postponed. 78d1eab9
  • app: [macOS] use NSWindow.zoom for Maximized. fa3978e1
  • app: [macOS] fix Intel build. d8766f6d
  • app: [Wayland] improve resizing by not processing resize gestures when decorated, only initiating resizing on pointer down, and shrinking the resize area. cab11848, 2957d007, 2adf4efc
  • io/router: search all key handlers when there is no focus. Fixes: #434 0057e871

Egon Elbre:

  • unit: add Metric.DpToSp and Metric.SpToDp. It’s sometimes necessary to specify padding or spacing based on the text size. 72669e19
  • internal/f32color: optimize LinearFromSRGB. 3670f70c
  • f32: add Affine2D.Split. splitTransform func was creating multiple copies of f32.Affine2D due to not having access to the internal and passing around non-pointer. 9de13e37
  • internal/ops: avoid bounds check in OpType.Size(). d3d2c517
  • internal/ops: use lookup table for NumRefs. f8efc9c2
  • internal/ops: avoid some bounds checks in decode. e7dd1804
  • internal/ops: use single table for OpType. Size and NumRefs are always used together, so consolidate info to a single table to avoid two separate lookups. 17f604fb
  • internal/ops: optimize Decode. Using clean struct creation creates a lot of temporary variables in assembly. Inline the assignments, which generates less code. f8f68a4e
  • gpu: optimize resourceCache. By keeping all the information in a single map, we avoid multiple lookups and can switch between frames more easily. 6bf5d4dc

Dominik Honnef:

  • text: optimize faceCache.hashGIDs. Use binary.LittleEndian directly instead of going through the binary.Write indirection. This allows the following optimizations to occur: e21c665e
  • widget: constrain drag offset to [0, 1]. Once the user begins dragging, the cursor can move outside the clip area (or even the window on at least X11), leading to events with positions that are either negative, or larger than the clip area. ea371246
  • widget: correctly set s.dragging to false when releasing drag. Before, we would set s.dragging to false on pointer.Release and then immediately set it back to true because we were processing the event and saw that s.dragging was false. 8f990a6f
  • widget: clicking on the scrollbar indicator shouldn’t jump. aea376fb
  • widget: consider size of indicator when limiting scrollbar dragging. f229601e
  • widget: move scrollbar indicator if dragging starts outside of it. 6981a887
  • widget: when clicking on scrollbar, center on that point. Previously, we’d scroll so the new viewportStart corresponded to the clicked position. This felt okay if clicking above the current indicator, but felt jarring when clicking below it. Centering gives a consistent behavior regardless of the scroll direction. 992f568a

Chris Waldon:

  • app: [Wayland] handle multiple global registry event orders. Not all wayland compositors advertise the global registry events in the same order. In particular, river and sway differ in that sway advertises the data_device_manager before the seat, and river does it after. This commit updates our code to correctly bind the data_device so that we can work with the clipboard regardless of the registry event order. 55c96adb
  • app: [Wayland] scale min/max window size correctly. The xdg_toplevel expects the min/max window size in DP rather than pixels. The scaling factor would be applied twice because we supplied pixels that we scaled ourselves, resulting in windows twice the expected size on HiDPI screens. This bug probably went for so long without being detected because it only manifests if you actually set a minimum or maximum size. 6a6ddc3f
  • app: [Wayland] use HiDPI cursor on HiDPI screen. This commit scales both the loaded cursor theme and the cursor surface appropriately so that the cursor image is not blurry on HiDPI screens. 414a91c4
  • app: [Wayland] respect XCURSOR_* environment variables. This commit adds support for the commonly-used XCURSOR_THEME and XCURSOR_SIZE environment variables. Wayland lacks a protocol-level way to standardize cursor size right now, but these variables are used consistently by many applications and compositors. Many users (including me) will find that their environment is already configuring these for them, and will get consistent cursor sizing for free. 9151009b
  • app: [Wayland] scale pointer hotspot coordinates. This commit updates the way that we change cursors so that the hotspot of the cursor is properly set to surface-local coordinates. The previous raw hotspot coordinates are relative to the cursor image buffer data, and do not take the buffer’s scaling factor into account. a5f8aa35



Elias updated the example repo to the latest Gio APIs and added the customdeco example to demonstrate how to create windows with native Gio decorations. To try it:

go run

I updated parts of the architecture document on the site to correct inaccuracies or deficiencies that Dominik Honnef brought to my attention. Egon kindly spent some time integrating the newsletter into the site proper, which is why you’re now reading this on the site instead of the mailing list. I will endeavor to add all of the old newsletters as time permits.

Chris Waldon:

  • architecture: update text handling info page. b87e915
  • architecture: clarify and expand drawing section. ba73359
  • architecture: clarify and expand window section. 84212b4
  • include/files/architecture: simplify button example. The button example was updated to use two overlapping clip.AreaOps when we converted pointer.AreaOp to be a clip.AreaOp. This wasn’t necessary, since a single clip.AreaOp can now both capture the drawing and input area. 6f814a1
  • architecture: add discussion of input tree with pointer example. This commit adds a discussion of the hierarchy of clip areas and how pointer events propagate through it. It also adds an example demonstrating the ways that pointer input areas interact in a hierarchy. d40cb3d

Elias Naur:

  • go.*,include/files/architecture: upgrade to latest Gio. 38c9628

Egon Elbre:

  • content,site,template: add Newsletters. This is initial template for newsletters and the four most recent newsletters by Chris Waldon. 38905d0


I added an end-to-end test that ensures app.CustomRenderer works correctly (at least on X11).

Chris Waldon:

  • gogio: implement custom rendering test. This commit adds an end to end test for the custom rendering use-case. I confirmed that the new test failed when custom rendering frame lifecycle was broken, and succeeds now. ecebd40