~ajstarks


#555 Overhaul of package app 5 months ago

on ~eliasnaur/gio

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 .

#555 Overhaul of package app 6 months ago

Comment by ~ajstarks on ~eliasnaur/gio

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)
		}
	}
}
'''

#550 Overhaul of event routing 7 months ago

Comment by ~ajstarks on ~eliasnaur/gio

~eliasnaur, the use of Optional works fine. thanks. Giocanvas and its clients are now converted. Overall the new API seems at bit cleaner and more straightforward.

New: https://gist.github.com/ajstarks/ef633da557c4f90429990d66f0f6c7c6

Old: https://gist.github.com/ajstarks/9224ad09306ecb240c6618bf32da45e6

Diff:

2c2
< func kbpointer(q event.Queue, cfg config) {
---
> func kbpointer(q input.Source, cfg config) {
6,9c6,18
< 	for _, ev := range q.Events(pressed) {
< 		// keyboard events
< 		if k, ok := ev.(key.Event); ok {
< 			switch k.State {
---
> 	for {
> 		e, ok := q.Event(
> 			key.Filter{Optional: key.ModCtrl},
> 			pointer.Filter{Kinds: pointer.Press | pointer.Move | pointer.Release},
> 		)
> 		if !ok {
> 			break
> 		}
> 		switch e := e.(type) {
> 
> 		case key.Event: // keyboard events
> 
> 			switch e.State {
11c20
< 				switch k.Name {
---
> 				switch e.Name {
31c40
< 					switch k.Modifiers {
---
> 					switch e.Modifiers {
38c47
< 					switch k.Modifiers {
---
> 					switch e.Modifiers {
45c54
< 					switch k.Modifiers {
---
> 					switch e.Modifiers {
52c61
< 					switch k.Modifiers {
---
> 					switch e.Modifiers {
62,65c71,74
< 		}
< 		// pointer events
< 		if p, ok := ev.(pointer.Event); ok {
< 			switch p.Kind {
---
> 
> 		case pointer.Event: // pointer events
> 
> 			switch e.Kind {
67c76
< 				mouseX, mouseY = pctcoord(p.Position.X, p.Position.Y, width, height)
---
> 				mouseX, mouseY = pctcoord(e.Position.X, e.Position.Y, width, height)
69c78
< 				switch p.Buttons {
---
> 				switch e.Buttons {
71c80
< 					bx, by = pctcoord(p.Position.X, p.Position.Y, width, height)
---
> 					bx, by = pctcoord(e.Position.X, e.Position.Y, width, height)
73c82
< 					ex, ey = pctcoord(p.Position.X, p.Position.Y, width, height)
---
> 					ex, ey = pctcoord(e.Position.X, e.Position.Y, width, height)
77d85
< 				pressed = true
96c104
< 		case system.DestroyEvent:
---
> 		case app.DestroyEvent:
100,103c108,109
< 		case system.FrameEvent:
< 			canvas := giocanvas.NewCanvas(float32(e.Size.X), float32(e.Size.Y), system.FrameEvent{})
< 			key.InputOp{Tag: pressed}.Add(canvas.Context.Ops)
< 			pointer.InputOp{Tag: pressed, Grab: false, Kinds: pointer.Press | pointer.Move}.Add(canvas.Context.Ops)
---
> 		case app.FrameEvent:
> 			canvas := giocanvas.NewCanvas(float32(e.Size.X), float32(e.Size.Y), app.FrameEvent{})
143c149
< 			kbpointer(e.Queue, cfg)
---
> 			kbpointer(e.Source, cfg)

also in giocanvas abs.go, changed

op.InvalidateOp{}.Add(ops)

to

c.Context.Execute(op.InvalidateCmd{})

#550 Overhaul of event routing 7 months ago

on ~eliasnaur/gio

~ajstarks: use the Optional field of key.Filter instead of Required.

#550 Overhaul of event routing 7 months ago

Comment by ~ajstarks on ~eliasnaur/gio

@eliasnaur: thanks for the pointers. I have it mostly working now with a slight correction:

kbpointer(e.Source)
...
// handle kb and pointer
func kbpointer(q input.Source) {
    for {
         e, ok := q.Event( // q.Event, not e.Event
             key.Filter{},
             pointer.Filter{Target: pressed, Kinds: pointer.Press | pointer.Move | pointer.Release},
         )
         if !ok { break }
         switch e := e.(type) {
         case key.Event:
               // handle kb
         case pointer.Event:
              // handle pointer
         }
    }
}

One issue: I cannot turning on modifiers for the key events:

key.Filter{Required: key.ModCtrl}

disables all keyboard events.

#550 Overhaul of event routing 7 months ago

Comment by ~ajstarks on ~eliasnaur/gio

Previously I did this:

var pressed bool
for {
    ev := w.NextEvent() 
    switch e := ev.(type) {
    case app.FrameEvent:
       ....
        kbpointer(e.Queue) // handle key and pointer events
        e.Frame(....)
  }
}

// handle kb and pointer
func kbpointer(q event.Queue) {
    for _, ev := range q.Events(pressed) { 
         if k, ok := ev.(key.Event); ok { ... } // handle kb
         if p, ok := ev.(pointer.Event); ok { ...} // handle pointer
    }
}

What is the best re-factor?

#550 Overhaul of event routing 7 months ago

on ~eliasnaur/gio

~beikege: thank you, I've updated the branch to fix widget.List. Use go get gioui.org@cf3e0c744246b4ae to get around the Go proxy caching of the branch.

~gedw99: yes, code will need refactoring, but mostly in low-level widget code. I'm using the kitchen example for testing:

diff --git a/kitchen/kitchen.go b/kitchen/kitchen.go
index 10d4c05..da8d2f0 100644
--- a/kitchen/kitchen.go
+++ b/kitchen/kitchen.go
@@ -22,8 +22,7 @@ import (
 	"gioui.org/font/gofont"
 	"gioui.org/gpu/headless"
 	"gioui.org/io/event"
-	"gioui.org/io/router"
-	"gioui.org/io/system"
+	"gioui.org/io/input"
 	"gioui.org/layout"
 	"gioui.org/op"
 	"gioui.org/op/clip"
@@ -96,7 +95,7 @@ func saveScreenshot(f string) error {
 			PxPerSp: scale,
 		},
 		Constraints: layout.Exact(sz),
-		Queue:       new(router.Router),
+		Source:      input.Source{},
 	}
 	th := material.NewTheme()
 	th.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))
@@ -126,7 +125,7 @@ func loop(w *app.Window) error {
 			ev := w.NextEvent()
 			events <- ev
 			<-acks
-			if _, ok := ev.(system.DestroyEvent); ok {
+			if _, ok := ev.(app.DestroyEvent); ok {
 				return
 			}
 		}
@@ -137,11 +136,11 @@ func loop(w *app.Window) error {
 		select {
 		case e := <-events:
 			switch e := e.(type) {
-			case system.DestroyEvent:
+			case app.DestroyEvent:
 				acks <- struct{}{}
 				return e.Err
-			case system.FrameEvent:
-				gtx := layout.NewContext(&ops, e)
+			case app.FrameEvent:
+				gtx := app.NewContext(&ops, e)
 				if *disable {
 					gtx = gtx.Disabled()
 				}
@@ -170,7 +169,7 @@ func transformedKitchen(gtx layout.Context, th *material.Theme) layout.Dimension
 	if !transformTime.IsZero() {
 		dt := float32(gtx.Now.Sub(transformTime).Seconds())
 		angle := dt * .1
-		op.InvalidateOp{}.Add(gtx.Ops)
+		gtx.Execute(op.InvalidateCmd{})
 		tr := f32.Affine2D{}
 		tr = tr.Rotate(f32.Pt(300, 20), -angle)
 		scale := 1.0 - dt*.5
@@ -332,7 +331,7 @@ func kitchen(gtx layout.Context, th *material.Theme) layout.Dimensions {
 						return material.Clickable(gtx, flatBtn, func(gtx C) D {
 							return layout.UniformInset(unit.Dp(12)).Layout(gtx, func(gtx C) D {
 								flatBtnText := material.Body1(th, "Flat")
-								if gtx.Queue == nil {
+								if !gtx.Enabled() {
 									flatBtnText.Color.A = 150
 								}
 								return layout.Center.Layout(gtx, flatBtnText.Layout)

~ajstarks: I believe this is what you want:

case app.FrameEvent:
    canvas := giocanvas.NewCanvas(float32(e.Size.X), float32(e.Size.Y), app.FrameEvent{})
    event.Op{Tag: pressed}.Add(canvas.Context.Ops)

The pointer.InputOp kinds are replaced with filters at your event handling:

for {
   e, ok := gtx.Event(pointer.Filter{Target: pressed, Kinds: pointer.Press | pointer.Move})
   if !ok { break }
}

#550 Overhaul of event routing 7 months ago

Comment by ~ajstarks on ~eliasnaur/gio

I updated giocanvas with the new event handling system, (many thanks for the gofmt commands from @egonelbre) and some clients work as expected (modulo the still existing bug #542). I'm not clear on the proper refactoring for clients with more extensive event handling. For example, is the best way to refactor:

case app.FrameEvent:
			canvas := giocanvas.NewCanvas(float32(e.Size.X), float32(e.Size.Y), app.FrameEvent{})
			key.InputOp{Tag: pressed}.Add(canvas.Context.Ops)
			pointer.InputOp{Tag: pressed, Grab: false, Kinds: pointer.Press | pointer.Move}.Add(canvas.Context.Ops)

This:

		case app.FrameEvent:
			canvas := giocanvas.NewCanvas(float32(e.Size.X), float32(e.Size.Y), app.FrameEvent{})
			canvas.Context.Execute(key.FocusCmd{Tag: pressed})
			canvas.Context.Event(pointer.Filter{Kinds: pointer.Press | pointer.Move})

#542 Window controls inoperable with Wayland 7 months ago

Comment by ~ajstarks on ~eliasnaur/gio

It appears to still be broken with v0.3.3-0.20231116145621-35cdcbe4d4b6

go run gioui.org/example/hello@latest # works
go run gioui.org/example/hello@main   # breaks

Also shows with giocanvas clients built with v0.3.2-0.20231125175025-f39245df99bf

#542 Window controls inoperable with Wayland 9 months ago

Comment by ~ajstarks on ~eliasnaur/gio

Yes, the hello example shows the bug. Here is a pointer to a demo of the bug: https://gophers.slack.com/archives/CM87SNCGM/p1697120564422829?thread_ts=1696868667.083419&cid=CM87SNCGM