~eliasnaur/gio-patches

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
6 3

[PATCH gio] app: support changing Window options at runtime

~pierrec
Details
Message ID
<161777946282.22092.5810234049738375513-0@git.sr.ht>
DKIM signature
missing
Download raw message
Patch: +95 -14
From: pierre <pierre.curto@gmail.com>

A Window can now be requested to change its options after
it has been started via its Option method.

All options are supported on macOS, Windows and X11.
On Wayland, only the Size and Title options can be changed
at runtime.

Signed-off-by: pierre <pierre.curto@gmail.com>
---
-

 app/internal/wm/os_macos.go   |  5 ++-
 app/internal/wm/os_wayland.go | 15 ++++++++-
 app/internal/wm/os_windows.go | 15 +++++++++
 app/internal/wm/os_x11.go     | 63 +++++++++++++++++++++++++++++------
 app/window.go                 | 11 ++++++
 5 files changed, 95 insertions(+), 14 deletions(-)

diff --git a/app/internal/wm/os_macos.go b/app/internal/wm/os_macos.go
index d088a924..b9266d83 100644
--- a/app/internal/wm/os_macos.go
+++ b/app/internal/wm/os_macos.go
@@ -172,11 +172,10 @@ func (w *window) Option(opts *Options) {
func (w *window) SetWindowMode(mode WindowMode) {
	switch mode {
	case w.mode:
		return
	case Fullscreen:
	case Windowed, Fullscreen:
		C.gio_toggleFullScreen(w.window)
		w.mode = mode
	}
	w.mode = mode
}

func (w *window) SetCursor(name pointer.CursorName) {
diff --git a/app/internal/wm/os_wayland.go b/app/internal/wm/os_wayland.go
index 1e8c59d9..03790959 100644
--- a/app/internal/wm/os_wayland.go
+++ b/app/internal/wm/os_wayland.go
@@ -181,6 +181,7 @@ type window struct {

	mu        sync.Mutex
	animating bool
	opts      *Options
	needAck   bool
	// The most recent configure serial waiting to be ack'ed.
	serial   C.uint32_t
@@ -357,7 +358,7 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*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.Option(opts)
	w.setOptions(opts)

	if d.decor != nil {
		// Request server side decorations.
@@ -910,6 +911,13 @@ func (w *window) WriteClipboard(s string) {
}

func (w *window) Option(opts *Options) {
	w.mu.Lock()
	w.opts = opts
	w.mu.Unlock()
	w.disp.wakeup()
}

func (w *window) setOptions(opts *Options) {
	_, _, cfg := w.config()
	if o := opts.Size; o != nil {
		w.width = cfg.Px(o.Width)
@@ -1143,8 +1151,10 @@ func (w *window) process() {
	w.mu.Lock()
	readClipboard := w.readClipboard
	writeClipboard := w.writeClipboard
	opts := w.opts
	w.readClipboard = false
	w.writeClipboard = nil
	w.opts = nil
	w.mu.Unlock()
	if readClipboard {
		r, err := w.disp.readClipboard()
@@ -1163,6 +1173,9 @@ func (w *window) process() {
	if writeClipboard != nil {
		w.disp.writeClipboard([]byte(*writeClipboard))
	}
	if opts != nil {
		w.setOptions(opts)
	}
	// pass false to skip unnecessary drawing.
	w.draw(false)
}
diff --git a/app/internal/wm/os_windows.go b/app/internal/wm/os_windows.go
index 611c7cb0..05d440ea 100644
--- a/app/internal/wm/os_windows.go
+++ b/app/internal/wm/os_windows.go
@@ -66,6 +66,7 @@ type window struct {
const (
	_WM_REDRAW = windows.WM_USER + iota
	_WM_CURSOR
	_WM_OPTION
)

type gpuAPI struct {
@@ -317,6 +318,8 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
			windows.SetCursor(w.cursor)
			return windows.TRUE
		}
	case _WM_OPTION:
		w.setOptions()
	}

	return windows.DefWindowProc(hwnd, msg, wParam, lParam)
@@ -520,6 +523,18 @@ func (w *window) readClipboard() error {
}

func (w *window) Option(opts *Options) {
	w.mu.Lock()
	w.opts = opts
	w.mu.Unlock()
	if err := windows.PostMessage(w.hwnd, _WM_OPTION, 0, 0); err != nil {
		panic(err)
	}
}

func (w *window) setOptions() {
	w.mu.Lock()
	opts := w.opts
	w.mu.Unlock()
	if o := opts.Size; o != nil {
		dpi := windows.GetSystemDPI()
		cfg := configForDPI(dpi)
diff --git a/app/internal/wm/os_x11.go b/app/internal/wm/os_x11.go
index b921d468..75b956e2 100644
--- a/app/internal/wm/os_x11.go
+++ b/app/internal/wm/os_x11.go
@@ -89,6 +89,7 @@ type x11Window struct {

	mu        sync.Mutex
	animating bool
	opts      *Options

	pointerBtns pointer.Buttons

@@ -98,6 +99,7 @@ type x11Window struct {
		content []byte
	}
	cursor pointer.CursorName
	mode   WindowMode
}

func (w *x11Window) SetAnimating(anim bool) {
@@ -124,22 +126,33 @@ func (w *x11Window) WriteClipboard(s string) {
}

func (w *x11Window) Option(opts *Options) {
	dpy := w.x
	win := w.xw
	cfg := w.cfg
	w.mu.Lock()
	w.opts = opts
	w.mu.Unlock()
	w.wakeup()
}

func (w *x11Window) setOptions() {
	w.mu.Lock()
	opts := w.opts
	w.opts = nil
	w.mu.Unlock()
	if opts == nil {
		return
	}
	var shints C.XSizeHints
	if o := opts.MinSize; o != nil {
		shints.min_width = C.int(cfg.Px(o.Width))
		shints.min_height = C.int(cfg.Px(o.Height))
		shints.min_width = C.int(w.cfg.Px(o.Width))
		shints.min_height = C.int(w.cfg.Px(o.Height))
		shints.flags = C.PMinSize
	}
	if o := opts.MaxSize; o != nil {
		shints.max_width = C.int(cfg.Px(o.Width))
		shints.max_height = C.int(cfg.Px(o.Height))
		shints.max_width = C.int(w.cfg.Px(o.Width))
		shints.max_height = C.int(w.cfg.Px(o.Height))
		shints.flags = shints.flags | C.PMaxSize
	}
	if shints.flags != 0 {
		C.XSetWMNormalHints(dpy, win, &shints)
		C.XSetWMNormalHints(w.x, w.xw, &shints)
	}

	var title string
@@ -148,9 +161,9 @@ func (w *x11Window) Option(opts *Options) {
	}
	ctitle := C.CString(title)
	defer C.free(unsafe.Pointer(ctitle))
	C.XStoreName(dpy, win, ctitle)
	C.XStoreName(w.x, w.xw, ctitle)
	// set _NET_WM_NAME as well for UTF-8 support in window title.
	C.XSetTextProperty(dpy, win,
	C.XSetTextProperty(w.x, w.xw,
		&C.XTextProperty{
			value:    (*C.uchar)(unsafe.Pointer(ctitle)),
			encoding: w.atoms.utf8string,
@@ -190,6 +203,8 @@ func (w *x11Window) SetCursor(name pointer.CursorName) {

func (w *x11Window) SetWindowMode(mode WindowMode) {
	switch mode {
	case w.mode:
		return
	case Windowed:
		C.XDeleteProperty(w.x, w.xw, w.atoms.wmStateFullscreen)
	case Fullscreen:
@@ -197,7 +212,34 @@ func (w *x11Window) SetWindowMode(mode WindowMode) {
			32, C.PropModeReplace,
			(*C.uchar)(unsafe.Pointer(&w.atoms.wmStateFullscreen)), 1,
		)
	default:
		return
	}
	w.mode = mode
	// "A Client wishing to change the state of a window MUST send
	//  a _NET_WM_STATE client message to the root window (see below)."
	var xev C.XEvent
	ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev))
	*ev = C.XClientMessageEvent{
		_type:        C.ClientMessage,
		display:      w.x,
		window:       w.xw,
		message_type: w.atoms.wmState,
		format:       32,
	}
	arr := (*[5]C.long)(unsafe.Pointer(&ev.data))
	arr[0] = 2 // _NET_WM_STATE_TOGGLE
	arr[1] = C.long(w.atoms.wmStateFullscreen)
	arr[2] = 0
	arr[3] = 1 // application
	arr[4] = 0
	C.XSendEvent(
		w.x,
		C.XDefaultRootWindow(w.x), // MUST be the root window
		C.False,
		C.SubstructureNotifyMask|C.SubstructureRedirectMask,
		&xev,
	)
}

func (w *x11Window) ShowTextInput(show bool) {}
@@ -287,6 +329,7 @@ loop:
				}
			}
		}
		w.setOptions()
		// Clear notifications.
		for {
			_, err := syscall.Read(w.notify.read, buf)
diff --git a/app/window.go b/app/window.go
index e8f691a9..20ee8b07 100644
--- a/app/window.go
+++ b/app/window.go
@@ -211,6 +211,17 @@ func (w *Window) Invalidate() {
	}
}

// Option applies the options to the window.
func (w *Window) Option(opts ...Option) {
	go w.driverDo(func() {
		o := new(wm.Options)
		for _, opt := range opts {
			opt(o)
		}
		w.driver.Option(o)
	})
}

// ReadClipboard initiates a read of the clipboard in the form
// of a clipboard.Event. Multiple reads may be coalesced
// to a single event.
-- 
2.30.2
Details
Message ID
<CAHB9V3JEIDS.29G0136T2I8J0@testmac>
In-Reply-To
<161777946282.22092.5810234049738375513-0@git.sr.ht> (view parent)
DKIM signature
fail
Download raw message
DKIM signature: fail
Thanks, merged.

Elias

[gio/patches] build success

builds.sr.ht
Details
Message ID
<CAHBFTTTJ0WX.3EIHA7X0PD5IK@cirno>
In-Reply-To
<161777946282.22092.5810234049738375513-0@git.sr.ht> (view parent)
DKIM signature
missing
Download raw message
gio/patches: SUCCESS in 22m37s

[app: support changing Window options at runtime][0] from [~pierrec][1]

[0]: https://lists.sr.ht/~eliasnaur/gio-patches/patches/21862
[1]: mailto:pierre.curto@gmail.com

✓ #479574 SUCCESS gio/patches/freebsd.yml https://builds.sr.ht/~eliasnaur/job/479574
✓ #479575 SUCCESS gio/patches/linux.yml   https://builds.sr.ht/~eliasnaur/job/479575
✓ #479576 SUCCESS gio/patches/openbsd.yml https://builds.sr.ht/~eliasnaur/job/479576
✓ #479573 SUCCESS gio/patches/apple.yml   https://builds.sr.ht/~eliasnaur/job/479573
Details
Message ID
<CAG3idSeBZU2JtcNvTSc2nVvozbx4EpriGahRBdzEhNRnjes5Rw@mail.gmail.com>
In-Reply-To
<CAHB9V3JEIDS.29G0136T2I8J0@testmac> (view parent)
DKIM signature
pass
Download raw message
Great!

Now, how do you see manipulating the window options from ops?
A WindowOptionOp in the op package for instance?

Le mer. 7 avr. 2021 à 09:25, Elias Naur <mail@eliasnaur.com> a écrit :
>
> Thanks, merged.
>
> Elias
Details
Message ID
<CAHBYLZ8F8P4.1LR1ZSSX64CW6@testmac>
In-Reply-To
<CAG3idSeBZU2JtcNvTSc2nVvozbx4EpriGahRBdzEhNRnjes5Rw@mail.gmail.com> (view parent)
DKIM signature
pass
Download raw message
On Wed Apr 7, 2021 at 09:44 CEST, Pierre Curto wrote:
> Great!
>
> Now, how do you see manipulating the window options from ops?
> A WindowOptionOp in the op package for instance?
>

Can you think of a use-case for WindowOptionOp where app.Window.Option
is not appropriate?

Triggering an option change (say, fullscreen mode) may happen as a
result of interacting with a widget (say, a checkbox) but setting the
option itself seems to belong in the main program logic, not in an op.

Elias
Details
Message ID
<CAG3idSfow7JaaqbtiOaQ-PdJKe7EiicO+M2bSiQcYMsTOvnX9Q@mail.gmail.com>
In-Reply-To
<CAHBYLZ8F8P4.1LR1ZSSX64CW6@testmac> (view parent)
DKIM signature
pass
Download raw message
Le mer. 7 avr. 2021 à 09:58, Elias Naur <mail@eliasnaur.com> a écrit :
>
> On Wed Apr 7, 2021 at 09:44 CEST, Pierre Curto wrote:
> > Great!
> >
> > Now, how do you see manipulating the window options from ops?
> > A WindowOptionOp in the op package for instance?
> >
>
> Can you think of a use-case for WindowOptionOp where app.Window.Option
> is not appropriate?
>
> Triggering an option change (say, fullscreen mode) may happen as a
> result of interacting with a widget (say, a checkbox) but setting the
> option itself seems to belong in the main program logic, not in an op.

I was thinking of the window title, where an app may show the file
being open in the title. Having to pass around the window pointer
seems too much?

>
> Elias
Details
Message ID
<CAHCUFVHO0D4.26U0S9EF2YYPI@testmac>
In-Reply-To
<CAG3idSfow7JaaqbtiOaQ-PdJKe7EiicO+M2bSiQcYMsTOvnX9Q@mail.gmail.com> (view parent)
DKIM signature
pass
Download raw message
On Wed Apr 7, 2021 at 10:01 CEST, Pierre Curto wrote:
> Le mer. 7 avr. 2021 à 09:58, Elias Naur <mail@eliasnaur.com> a écrit :
> >
> > On Wed Apr 7, 2021 at 09:44 CEST, Pierre Curto wrote:
> > > Great!
> > >
> > > Now, how do you see manipulating the window options from ops?
> > > A WindowOptionOp in the op package for instance?
> > >
> >
> > Can you think of a use-case for WindowOptionOp where app.Window.Option
> > is not appropriate?
> >
> > Triggering an option change (say, fullscreen mode) may happen as a
> > result of interacting with a widget (say, a checkbox) but setting the
> > option itself seems to belong in the main program logic, not in an op.
>
> I was thinking of the window title, where an app may show the file
> being open in the title. Having to pass around the window pointer
> seems too much?
>

I would structure the file dialog to send events that describes the
current directory. What if the Gio program want to use the window title
for other purposes than the current directory, or wants to add context
(program name) to it?

Elias
Reply to thread Export thread (mbox)