mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 10:25:37 +00:00
app: support changing Window options at runtime
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>
This commit is contained in:
@@ -172,11 +172,10 @@ func (w *window) Option(opts *Options) {
|
|||||||
func (w *window) SetWindowMode(mode WindowMode) {
|
func (w *window) SetWindowMode(mode WindowMode) {
|
||||||
switch mode {
|
switch mode {
|
||||||
case w.mode:
|
case w.mode:
|
||||||
return
|
case Windowed, Fullscreen:
|
||||||
case Fullscreen:
|
|
||||||
C.gio_toggleFullScreen(w.window)
|
C.gio_toggleFullScreen(w.window)
|
||||||
|
w.mode = mode
|
||||||
}
|
}
|
||||||
w.mode = mode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) SetCursor(name pointer.CursorName) {
|
func (w *window) SetCursor(name pointer.CursorName) {
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ type window struct {
|
|||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
animating bool
|
animating bool
|
||||||
|
opts *Options
|
||||||
needAck bool
|
needAck bool
|
||||||
// The most recent configure serial waiting to be ack'ed.
|
// The most recent configure serial waiting to be ack'ed.
|
||||||
serial C.uint32_t
|
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_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))
|
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 {
|
if d.decor != nil {
|
||||||
// Request server side decorations.
|
// Request server side decorations.
|
||||||
@@ -910,6 +911,13 @@ func (w *window) WriteClipboard(s string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Option(opts *Options) {
|
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()
|
_, _, cfg := w.config()
|
||||||
if o := opts.Size; o != nil {
|
if o := opts.Size; o != nil {
|
||||||
w.width = cfg.Px(o.Width)
|
w.width = cfg.Px(o.Width)
|
||||||
@@ -1143,8 +1151,10 @@ func (w *window) process() {
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
readClipboard := w.readClipboard
|
readClipboard := w.readClipboard
|
||||||
writeClipboard := w.writeClipboard
|
writeClipboard := w.writeClipboard
|
||||||
|
opts := w.opts
|
||||||
w.readClipboard = false
|
w.readClipboard = false
|
||||||
w.writeClipboard = nil
|
w.writeClipboard = nil
|
||||||
|
w.opts = nil
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
if readClipboard {
|
if readClipboard {
|
||||||
r, err := w.disp.readClipboard()
|
r, err := w.disp.readClipboard()
|
||||||
@@ -1163,6 +1173,9 @@ func (w *window) process() {
|
|||||||
if writeClipboard != nil {
|
if writeClipboard != nil {
|
||||||
w.disp.writeClipboard([]byte(*writeClipboard))
|
w.disp.writeClipboard([]byte(*writeClipboard))
|
||||||
}
|
}
|
||||||
|
if opts != nil {
|
||||||
|
w.setOptions(opts)
|
||||||
|
}
|
||||||
// pass false to skip unnecessary drawing.
|
// pass false to skip unnecessary drawing.
|
||||||
w.draw(false)
|
w.draw(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ type window struct {
|
|||||||
const (
|
const (
|
||||||
_WM_REDRAW = windows.WM_USER + iota
|
_WM_REDRAW = windows.WM_USER + iota
|
||||||
_WM_CURSOR
|
_WM_CURSOR
|
||||||
|
_WM_OPTION
|
||||||
)
|
)
|
||||||
|
|
||||||
type gpuAPI struct {
|
type gpuAPI struct {
|
||||||
@@ -317,6 +318,8 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
|||||||
windows.SetCursor(w.cursor)
|
windows.SetCursor(w.cursor)
|
||||||
return windows.TRUE
|
return windows.TRUE
|
||||||
}
|
}
|
||||||
|
case _WM_OPTION:
|
||||||
|
w.setOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
return windows.DefWindowProc(hwnd, msg, wParam, lParam)
|
return windows.DefWindowProc(hwnd, msg, wParam, lParam)
|
||||||
@@ -520,6 +523,18 @@ func (w *window) readClipboard() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Option(opts *Options) {
|
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 {
|
if o := opts.Size; o != nil {
|
||||||
dpi := windows.GetSystemDPI()
|
dpi := windows.GetSystemDPI()
|
||||||
cfg := configForDPI(dpi)
|
cfg := configForDPI(dpi)
|
||||||
|
|||||||
+53
-10
@@ -89,6 +89,7 @@ type x11Window struct {
|
|||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
animating bool
|
animating bool
|
||||||
|
opts *Options
|
||||||
|
|
||||||
pointerBtns pointer.Buttons
|
pointerBtns pointer.Buttons
|
||||||
|
|
||||||
@@ -98,6 +99,7 @@ type x11Window struct {
|
|||||||
content []byte
|
content []byte
|
||||||
}
|
}
|
||||||
cursor pointer.CursorName
|
cursor pointer.CursorName
|
||||||
|
mode WindowMode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *x11Window) SetAnimating(anim bool) {
|
func (w *x11Window) SetAnimating(anim bool) {
|
||||||
@@ -124,22 +126,33 @@ func (w *x11Window) WriteClipboard(s string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *x11Window) Option(opts *Options) {
|
func (w *x11Window) Option(opts *Options) {
|
||||||
dpy := w.x
|
w.mu.Lock()
|
||||||
win := w.xw
|
w.opts = opts
|
||||||
cfg := w.cfg
|
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
|
var shints C.XSizeHints
|
||||||
if o := opts.MinSize; o != nil {
|
if o := opts.MinSize; o != nil {
|
||||||
shints.min_width = C.int(cfg.Px(o.Width))
|
shints.min_width = C.int(w.cfg.Px(o.Width))
|
||||||
shints.min_height = C.int(cfg.Px(o.Height))
|
shints.min_height = C.int(w.cfg.Px(o.Height))
|
||||||
shints.flags = C.PMinSize
|
shints.flags = C.PMinSize
|
||||||
}
|
}
|
||||||
if o := opts.MaxSize; o != nil {
|
if o := opts.MaxSize; o != nil {
|
||||||
shints.max_width = C.int(cfg.Px(o.Width))
|
shints.max_width = C.int(w.cfg.Px(o.Width))
|
||||||
shints.max_height = C.int(cfg.Px(o.Height))
|
shints.max_height = C.int(w.cfg.Px(o.Height))
|
||||||
shints.flags = shints.flags | C.PMaxSize
|
shints.flags = shints.flags | C.PMaxSize
|
||||||
}
|
}
|
||||||
if shints.flags != 0 {
|
if shints.flags != 0 {
|
||||||
C.XSetWMNormalHints(dpy, win, &shints)
|
C.XSetWMNormalHints(w.x, w.xw, &shints)
|
||||||
}
|
}
|
||||||
|
|
||||||
var title string
|
var title string
|
||||||
@@ -148,9 +161,9 @@ func (w *x11Window) Option(opts *Options) {
|
|||||||
}
|
}
|
||||||
ctitle := C.CString(title)
|
ctitle := C.CString(title)
|
||||||
defer C.free(unsafe.Pointer(ctitle))
|
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.
|
// 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{
|
&C.XTextProperty{
|
||||||
value: (*C.uchar)(unsafe.Pointer(ctitle)),
|
value: (*C.uchar)(unsafe.Pointer(ctitle)),
|
||||||
encoding: w.atoms.utf8string,
|
encoding: w.atoms.utf8string,
|
||||||
@@ -190,6 +203,8 @@ func (w *x11Window) SetCursor(name pointer.CursorName) {
|
|||||||
|
|
||||||
func (w *x11Window) SetWindowMode(mode WindowMode) {
|
func (w *x11Window) SetWindowMode(mode WindowMode) {
|
||||||
switch mode {
|
switch mode {
|
||||||
|
case w.mode:
|
||||||
|
return
|
||||||
case Windowed:
|
case Windowed:
|
||||||
C.XDeleteProperty(w.x, w.xw, w.atoms.wmStateFullscreen)
|
C.XDeleteProperty(w.x, w.xw, w.atoms.wmStateFullscreen)
|
||||||
case Fullscreen:
|
case Fullscreen:
|
||||||
@@ -197,7 +212,34 @@ func (w *x11Window) SetWindowMode(mode WindowMode) {
|
|||||||
32, C.PropModeReplace,
|
32, C.PropModeReplace,
|
||||||
(*C.uchar)(unsafe.Pointer(&w.atoms.wmStateFullscreen)), 1,
|
(*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) {}
|
func (w *x11Window) ShowTextInput(show bool) {}
|
||||||
@@ -287,6 +329,7 @@ loop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
w.setOptions()
|
||||||
// Clear notifications.
|
// Clear notifications.
|
||||||
for {
|
for {
|
||||||
_, err := syscall.Read(w.notify.read, buf)
|
_, err := syscall.Read(w.notify.read, buf)
|
||||||
|
|||||||
@@ -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
|
// ReadClipboard initiates a read of the clipboard in the form
|
||||||
// of a clipboard.Event. Multiple reads may be coalesced
|
// of a clipboard.Event. Multiple reads may be coalesced
|
||||||
// to a single event.
|
// to a single event.
|
||||||
|
|||||||
Reference in New Issue
Block a user