diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 27b6ab84..5757a44a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,6 +54,17 @@ jobs: - run: nim c examples/openurl.nim - run: nim c examples/property_changes.nim - run: nim c examples/screens.nim + - name: Check two X11 monitors + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update -qq + sudo apt-get install -y x11-xserver-utils + xvfb-run -a -s "-noreset -screen 0 1600x600x24" sh -ec ' + xrandr --setmonitor LEFT 800/200x600/150+0+0 screen + xrandr --setmonitor RIGHT 800/200x600/150+800+0 none + nim r --parallelBuild:1 tests/test_screens.nim + ' + - run: nim c examples/scrollwheel.nim - run: nim c examples/system_cursors.nim - run: nim c examples/tray.nim diff --git a/examples/screens.nim b/examples/screens.nim index f8edd0be..1b7c22af 100644 --- a/examples/screens.nim +++ b/examples/screens.nim @@ -1,8 +1,4 @@ import windy -when defined(windows) or defined(macosx): - # Screens API only currently supported on Windows and macOS - - let screens = getScreens() - for screen in screens: - echo screen +for screen in getScreens(): + echo screen diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index 5b29e3cd..b884ee17 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -2,7 +2,7 @@ import std/[os, osproc, sequtils, sets, strformat, strutils, times, unicode, uri, pathnorm], ../../[common, internal], vmath, pixie, - x11/[glx, keysym, x, xevent, xlib, xcursor] + x11/[glx, keysym, x, xevent, xlib, xcursor, xrandr] import ../../http export http @@ -625,6 +625,27 @@ 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] = + ## Returns active X11 monitors and their desktop positions (RandR 1.5). + init() + var major, minor, count: cint + if display.XRRQueryVersion(major.addr, minor.addr) == 0 or + (major == 1 and minor < 5) or major < 1: + raise WindyError.newException("Screen enumeration requires RandR 1.5") + let monitors = display.XRRGetMonitors(display.defaultRootWindow, 1, count.addr) + if monitors == nil: + return + defer: XRRFreeMonitors(monitors) + for i in 0 ..< count: + let monitor = monitors[i] + result.add common.Screen( + left: monitor.x, + top: monitor.y, + right: monitor.x + monitor.width, + bottom: monitor.y + monitor.height, + primary: monitor.primary != 0 + ) + proc contentScale*(window: Window): float32 = const defaultScreenDpi = 96.0 diff --git a/src/windy/platforms/linux/x11/xrandr.nim b/src/windy/platforms/linux/x11/xrandr.nim new file mode 100644 index 00000000..ccd0c941 --- /dev/null +++ b/src/windy/platforms/linux/x11/xrandr.nim @@ -0,0 +1,15 @@ +import x, xlib + +type + XRRMonitorInfo* {.bycopy.} = object + name*: Atom + primary*, automatic*, noutput*: cint + x*, y*, width*, height*, mwidth*, mheight*: cint + outputs*: ptr culong + +{.push cdecl, dynlib: "libXrandr.so.2", importc.} +proc XRRQueryVersion*(display: Display, major, minor: ptr cint): cint +proc XRRGetMonitors*(display: Display, window: Window, active: cint, + count: ptr cint): ptr UncheckedArray[XRRMonitorInfo] +proc XRRFreeMonitors*(monitors: ptr UncheckedArray[XRRMonitorInfo]) +{.pop.} diff --git a/tests/test_screens.nim b/tests/test_screens.nim new file mode 100644 index 00000000..84816c23 --- /dev/null +++ b/tests/test_screens.nim @@ -0,0 +1,10 @@ +## Run on the two-monitor Xvfb layout configured by the build workflow. +import windy, std/algorithm +var screens = getScreens() +screens.sort(proc(a, b: Screen): int = cmp(a.left, b.left)) +doAssert screens.len == 2, "each monitor must be returned separately" +doAssert screens[0].left == 0 and screens[0].right == 800 +doAssert screens[1].left == 800 and screens[1].right == 1600 +for screen in screens: + doAssert screen.top == 0 and screen.bottom == 600 +echo "Two distinct X11 monitor bounds passed"