From a30600a42c7a0a6b38067008a52d9deaee6e6b27 Mon Sep 17 00:00:00 2001 From: Richard Higgins Date: Thu, 4 Dec 2025 20:27:46 -0800 Subject: [PATCH 1/4] Add Linux getScreens X11 fallback --- examples/screens.nim | 5 ++--- src/windy/platforms/linux/x11.nim | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/examples/screens.nim b/examples/screens.nim index f8edd0b..9705d5d 100644 --- a/examples/screens.nim +++ b/examples/screens.nim @@ -1,8 +1,7 @@ import windy -when defined(windows) or defined(macosx): - # Screens API only currently supported on Windows and macOS - +when defined(windows) or defined(macosx) or defined(linux) or defined(emscripten): + # Screens API supported on all major platforms; Linux uses X11 fallback for now. let screens = getScreens() for screen in screens: echo screen diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index ac4e7bc..fad1f9e 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -566,6 +566,23 @@ proc `title=`*(window: Window, v: string) = window.handle.setProperty(xaNetWMIconName, xaUTF8String, 8, v) display.Xutf8SetWMProperties(window.handle, v, v, nil, 0, nil, nil, nil) +proc getScreens*(): seq[common.Screen] = + ## Minimal screen enumeration for X11. Returns the default screen as primary. + init() + + let s = display.screen(display.defaultScreen) + if s == nil: + return + + let size = s.size + result.add common.Screen( + left: 0, + top: 0, + right: size.x, + bottom: size.y, + primary: true + ) + proc contentScale*(window: Window): float32 = const defaultScreenDpi = 96.0 From fdd33275cf6a7f6425ee6e1562c536c6fe68b405 Mon Sep 17 00:00:00 2001 From: Richard Higgins Date: Thu, 4 Dec 2025 20:41:33 -0800 Subject: [PATCH 2/4] Add Wayland getScreens support --- src/windy/platforms/linux/platform.nim | 10 ++--- src/windy/platforms/linux/wayland.nim | 57 +++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/windy/platforms/linux/platform.nim b/src/windy/platforms/linux/platform.nim index d581b16..55d6c66 100644 --- a/src/windy/platforms/linux/platform.nim +++ b/src/windy/platforms/linux/platform.nim @@ -1,5 +1,5 @@ -# when defined(wayland): -# include wayland -# else: -include x11 -# include x11_2 +when defined(wayland): + include wayland +else: + include x11 + # include x11_2 diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index 0d198dd..744de9c 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -1,5 +1,12 @@ -import ../../common, ../../internal, vmath, wayland/egl, wayland/protocol, - wayland/sharedBuffer +import ../../common, vmath, wayland/egl, wayland/protocol + +type + OutputInfo = ref object + pos: IVec2 + mode: IVec2 + scale: int = 1 + +var outputs: seq[OutputInfo] var initialized: bool @@ -40,6 +47,31 @@ proc init* = shell.onPing: shell.pong(serial) + of Output.iface: + let outputObj = registry.bindInterface(Output, name, iface, min(version, 3)) + var info = OutputInfo() + outputs.add(info) + + outputObj.onGeometry: + info.pos = pos + + outputObj.onMode: + # Prefer the current mode, then preferred, otherwise first advertised. + if ModeFlag.current in flags: + info.mode = size + elif ModeFlag.prefered in flags and info.mode == ivec2(0, 0): + info.mode = size + elif info.mode == ivec2(0, 0): + info.mode = size + + outputObj.onScale: + if factor > 0: + info.scale = factor + + outputObj.onDone: + if info.scale <= 0: + info.scale = 1 + sync display if compositor == nil or shm == nil or shell == nil: @@ -56,6 +88,27 @@ proc init* = initialized = true +proc getScreens*(): seq[common.Screen] = + ## Enumerate Wayland outputs and return them as Screen records. + init() + # Pump events so that output geometry/mode/scale are populated. + display.sync + + if outputs.len == 0: + # Fallback: single 1920x1080 primary if no outputs were advertised. + return @[common.Screen(left: 0, top: 0, right: 1920, bottom: 1080, primary: true)] + + for i, o in outputs: + let mode = if o.mode == ivec2(0, 0): ivec2(1920, 1080) else: o.mode + let scale = max(o.scale, 1) + result.add common.Screen( + left: o.pos.x, + top: o.pos.y, + right: o.pos.x + mode.x * scale, + bottom: o.pos.y + mode.y * scale, + primary: i == 0 or (o.pos.x == 0 and o.pos.y == 0) + ) + when isMainModule: init() let srf = compositor.newSurface From bdc77fbc075eaee91d1bacea3d117f6551f19c9e Mon Sep 17 00:00:00 2001 From: Richard Higgins Date: Thu, 4 Dec 2025 20:59:09 -0800 Subject: [PATCH 3/4] more concise? --- src/windy/platforms/linux/wayland.nim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index 744de9c..785a8f2 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -6,6 +6,8 @@ type mode: IVec2 scale: int = 1 +const defaultMode = ivec2(1920, 1080) + var outputs: seq[OutputInfo] var @@ -96,17 +98,17 @@ proc getScreens*(): seq[common.Screen] = if outputs.len == 0: # Fallback: single 1920x1080 primary if no outputs were advertised. - return @[common.Screen(left: 0, top: 0, right: 1920, bottom: 1080, primary: true)] + return @[common.Screen(left: 0, top: 0, right: defaultMode.x, bottom: defaultMode.y, primary: true)] for i, o in outputs: - let mode = if o.mode == ivec2(0, 0): ivec2(1920, 1080) else: o.mode - let scale = max(o.scale, 1) + let mode = if o.mode == ivec2(0, 0): defaultMode else: o.mode + let scale = max(o.scale, 1) # normalize physical pixels into logical coords result.add common.Screen( left: o.pos.x, top: o.pos.y, - right: o.pos.x + mode.x * scale, - bottom: o.pos.y + mode.y * scale, - primary: i == 0 or (o.pos.x == 0 and o.pos.y == 0) + right: o.pos.x + mode.x div scale, + bottom: o.pos.y + mode.y div scale, + primary: i == 0 or o.pos == ivec2(0, 0) ) when isMainModule: From dd667e90b88499b93ad4d0e184eb1ad5f9ed60d8 Mon Sep 17 00:00:00 2001 From: Richard Higgins Date: Thu, 4 Dec 2025 21:06:14 -0800 Subject: [PATCH 4/4] more concise? --- src/windy/platforms/linux/wayland.nim | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/windy/platforms/linux/wayland.nim b/src/windy/platforms/linux/wayland.nim index 785a8f2..9b7aefe 100644 --- a/src/windy/platforms/linux/wayland.nim +++ b/src/windy/platforms/linux/wayland.nim @@ -58,12 +58,7 @@ proc init* = info.pos = pos outputObj.onMode: - # Prefer the current mode, then preferred, otherwise first advertised. - if ModeFlag.current in flags: - info.mode = size - elif ModeFlag.prefered in flags and info.mode == ivec2(0, 0): - info.mode = size - elif info.mode == ivec2(0, 0): + if ModeFlag.current in flags or info.mode == ivec2(0, 0): info.mode = size outputObj.onScale: @@ -102,7 +97,7 @@ proc getScreens*(): seq[common.Screen] = for i, o in outputs: let mode = if o.mode == ivec2(0, 0): defaultMode else: o.mode - let scale = max(o.scale, 1) # normalize physical pixels into logical coords + let scale = max(o.scale, 1) result.add common.Screen( left: o.pos.x, top: o.pos.y,