The app-overhaul
branch, built on top of #550, is a proposal for several changes to package app
.
Please test your your programs on available platforms. If you encounter problems, please report your exact system configuration and a minimal reproducer. If you're able, I would appreciate you git bisect
to determine the first failing commit.
To fetch the changes,
$ go get gioui.org@app-overhaul
Below is a description of the changes.
The requirement to call app.Main
in your main goroutine was caused by the macOS platform needing control of the main thread. I managed to work around this limitation, leading to the removal of app.Main
.
Note that at least one Window
is required to have its Event
method called on the main goroutine to avoid deadlocks on macOS.
See also my Go proposal for a more robust solution to the issues on macOS, as well as a way towards go run
for iOS and the merging of the maOS and iOS backends.
Window.Event
now matches #550's Source.Event
.
A major refactor pushes the window event loop to backends.
Notably, this simplifies the Wayland, X11, WASM platforms that no longer need a dedicated goroutine for each window. Every WIndow is now driven directly by the Window.Event
method.
StageEvent served only redundant purposes:
The GUI window is created and shown by the first call to a Window
method. The options passed to NewWindow
can instead be passed to Window.Option
.
windows 11 go get gioui.org@app-overhaul
https://go.dev/play/p/-v0MTsq9Rxm
Windowed -> Fullscreen
Fullscreen -> Windowed
When switching from fullscreen to windowed mode, the window size becomes maximized. Is this the intended behavior?
I'm not quite convinced that you can easily and reliably detect StageEvent-s from the presence of FrameEvents. e.g. You have a separate audio goroutine that should be paused when the app is minimized, then it's not obvious whether the app isn't getting events due to not needing to render anything or due to being minimized.
~egonelbre: note that we have the
app.Minimized
WindowMode
already. More importantly, platforms such as Wayland won't ever tell you whether a window is minimized, so your use-case seems better triggered by, say, the loss of window focus.
~beikege: this was also a bug on main. I've fixed it and rebased the
app-overhaul
andevent-filters
branches. Usego get gioui.org@e6f8fcb3b027818
to fetch the newest version.
~eliasnaur Thank you
I share ~egonelbre's concern that StageEvent filled a useful niche. I know one use-case for it was detecting focus loss in order to close the window. This was for an application launcher, which it makes sense to just close if the user clicks away from it. Another use case is wanting to play sound only when your window has focus. It is not clear to me how to tell the difference between "my window has focus" and "my window isn't being invalidated because there are no input events right now."
I also don't see how
key.FocusEvent
covers window focus tracking entirely. If I install a root-level key handler, it will get a focus event each time the focus changes between widgets, and I suppose it also gets one when the window loses focus, but I don't see any fields onkey.FocusEvent
that would differentiate between "focus transferred to another widget" and "focus transferred to another window."Am I missing some invariants or side-channels that make the above issues moot?
You're not missing anything.
StageEvent
conflated "visible enough for FrameEvents" and "focused", and the latter overlapped with thekey.FocusEvent
sent when backends detect focus changes. If we want to track window focus apart from widget focus, I suggest it be in the form of aapp.Config
field tracked by the usualapp.ConfigEvent
.
Okay, I'm fine with an
app.Config
field tracking whether the window is focused. I think that is a necessary change for existing Gio applications that were using the stage event as a proxy for focused-ness to migrate to this new API.
Alright, I've added
Config.Focused
and changed the backends to update that. As a nice side-effect,app.Window
is now in charge of sending the appropriatekey.FocusEvent
.
System: Fedora 39, UM690 (32Gb RAM, AMD Ryzen™ 9 6900HX with Radeon™ Graphics × 16)
$ go version -m ./hello ./hello: go1.21.6 path github.com/ajstarks/giocanvas/hello mod github.com/ajstarks/giocanvas (devel) dep gioui.org v0.4.2-0.20231221171002-cb8efefa839a h1:QBZFhRotXHnB+Nq2iGfIZoinKbZRjEPTnA+9ReOj2LI= dep gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc= dep gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA= dep github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo= dep golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg= dep golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0= dep golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= dep golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= dep golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
I converted all giocanvas clients to this new API, and all run fine on X11 as well as Mac OS (11.7.9, MacBook Pro 2.3 GHz Quad-Core Intel Core i7). If I switch to Wayland on Fedora, the same binary crashes: (See:
https://gist.github.com/ajstarks/080972af9bf905ffe595e14009e8e471
Here is the "hello" program that crashed:
// hello is the giocanvas hello, world package main import ( "flag" "image/color" "io" "os" "gioui.org/app" "gioui.org/unit" "github.com/ajstarks/giocanvas" ) func main() { var cw, ch int flag.IntVar(&cw, "width", 1000, "canvas width") flag.IntVar(&ch, "height", 1000, "canvas height") flag.Parse() width := float32(cw) height := float32(ch) w := &app.Window{} w.Option(app.Title("hello"), app.Size(unit.Dp(width), unit.Dp(height))) if err := hello(w, width, height); err != nil { io.WriteString(os.Stderr, "Cannot create the window\n") os.Exit(1) } os.Exit(0) } func hello(w *app.Window, width, height float32) error { for { ev := w.Event() switch e := ev.(type) { case app.DestroyEvent: return e.Err case app.FrameEvent: canvas := giocanvas.NewCanvas(float32(e.Size.X), float32(e.Size.Y), app.FrameEvent{}) canvas.CenterRect(50, 50, 100, 100, color.NRGBA{0, 0, 0, 255}) canvas.Circle(50, 0, 50, color.NRGBA{0, 0, 255, 255}) canvas.TextMid(50, 20, 10, "hello, world", color.NRGBA{255, 255, 255, 255}) canvas.CenterImage("earth.jpg", 50, 70, 1000, 1000, 30) e.Frame(canvas.Context.Ops) } } } '''
Thank you for the report, ~ajstarks. I've fixed the issue on the branch.
I've also re-added
app.Main
, pending a decision on https://github.com/golang/go/issues/64755 .
This has been merged into main, except for the removal of
app.Main
.