Skip to content

Fix: flip the _NET_WORKAREA origin and count monitors, not outputs - #226

Open
DTW-Thalion wants to merge 5 commits into
gnustep:masterfrom
DTW-Thalion:fix/x11-workarea-origin
Open

DTW-Thalion wants to merge 5 commits into
gnustep:masterfrom
DTW-Thalion:fix/x11-workarea-origin

Conversation

@DTW-Thalion

Copy link
Copy Markdown
Contributor

_NET_WORKAREA is used as the screen frame for a single monitor. Two things are
wrong with that.

The property is in X coordinates, where y grows downwards, and the origin was
set to zero rather than flipped. A panel at the top and one at the bottom
reserve the same rows and report the same height, so only the origin tells
them apart. Zeroing it suits a panel at the top and is wrong by the height of
the panel for one at the bottom.

The single monitor test read monitorsCount while it still held
screen_res->noutput, so on a driver reporting an output per connector the work
area was not used.

Measured on a 1920x1080 screen with 40 rows reserved at the bottom: a bar
there was mapped at X row 1030, over a panel at rows 1040 to 1079, and is now
at 990. On the Xorg dummy driver, with 16 outputs, the frame was 1920x1080 and
is now 1920x1040 at y 40.

Tests/x11 gives 259 passed 0 failed against 257 passed 2 failed, and two
monitors are unchanged. The count needs several outputs, which CI has not.

_NET_WORKAREA holds the area a window manager leaves usable, in X
coordinates, where y grows downwards from the top of the screen.  A screen
frame is in OpenStep coordinates, where y grows upwards, so the rectangle has
to be flipped.  The origin was set to zero instead.

The origin says which end of the screen the reserved rows are at.  A panel at
the top and a panel at the bottom reserve the same number of rows and report
the same height, so zeroing the origin gives both the same frame.  That frame
is right for a panel at the top and wrong by the height of the panel for one
at the bottom, where a window placed at the bottom of the screen was drawn
under the panel and the rows at the top could not be reached.

Measured on a 1920x1080 screen with 40 rows reserved at the bottom: a 50
pixel bar placed at the bottom of the screen was mapped at X row 1030, over a
panel occupying rows 1040 to 1079, and is now mapped at row 990.  A bar
placed at the top was mapped at row 40 and is now at row 0.
_NET_WORKAREA refers to the composite display formed by all the monitors, so
it is only used as a screen frame when there is a single monitor.  That test
read monitorsCount while it still held screen_res->noutput, the number of
RandR outputs.  monitorsCount holds the number of monitors only after the
outputs have been walked, since an output the display is not using has no
CRTC and is not a monitor.

A driver reports an output for every connector it supports, so the count
matched only on a server that reports exactly one.  Measured with the Xorg
dummy driver, which reports 16 outputs: with a single monitor and a work area
40 rows shorter than the screen, the screen frame was the full 1920x1080 and
is now 1920x1040 at y 40, which is the frame Xvfb already gave through its
single output.
A panel at the top of the screen and a panel at the bottom reserve the same
number of rows, so they report the same work area height and differ only in
the origin.  They have to give different screen frames.

The test publishes _NET_WORKAREA on the root window and reads the frame back
through boundsForScreen:.  It creates the display server directly, since
nothing here draws, and skips when there is no display or when more than one
monitor is present, which is the case the property is not used for.

Tests/x11 now links the gui library, which is where GSDisplayServer lives.
@DTW-Thalion
DTW-Thalion requested a review from fredkiefer as a code owner August 6, 2026 14:50
fredkiefer pushed a commit to gnustep/libs-gui that referenced this pull request Aug 9, 2026
-[NSMenu _setGeometry] placed the main menu at x 0, and the vertical branch
placed it at visibleFrame.size.height less the menu height. Both drop the
frame's origin, which is not zero when a window manager reserves part of the
screen and the backend reports the remainder.

Measured on a 1920x1080 screen. With a dock 64 columns wide down the left
edge the screen frame is 64,0 1856x1080 and the menu was mapped at X column 0,
underneath the dock; it is now mapped at column 64. With a panel 40 rows deep
at the bottom the frame is 0,40 1920x1040 and the menu top edge was 1040
against a frame top of 1080, leaving usable rows empty above it; it is now
flush with the top.

The horizontal branch already added screenFrame.origin.y, and NSMaxY keeps
that result while correcting x. A work area origin in y needs the backend
change in gnustep/libs-back#226; the x case is reached without it.
…igin

# Conflicts:
#	Tests/x11/GNUmakefile.preamble
XGServerWindow.m keeps the two comments master already had.  The header of
Tests/x11/workarea.m states what the test covers and no more.
@probonopd

Copy link
Copy Markdown
Contributor

Confirming both halves of this from a desktop that reserves space at the top and at the bottom, and adding what the origin means for the code that is about to consume the work area.

With a 22 pixel menu bar at the top and a dock at the bottom of an 800x600 screen, the window manager publishes _NET_WORKAREA = 0, 22, 800, 450. Read as this PR does, that is 0 128 800 450 in screen coordinates: 22 reserved at the top, 128 at the bottom. Read as master does, with the origin zeroed, it becomes 0 0 800 450, which says the bottom 128 pixels are free and the top 150 are not. The two readings differ by 128 pixels in the opposite direction, and only the first one describes the screen.

That matters beyond the screen frame. gnustep/libs-gui#953 builds -[NSScreen visibleFrame] from the work area, so with the origin zeroed an application would be told to keep out of the wrong edge - it would avoid a menu bar that is not there and place its windows under the dock.

The output count is the same story here as on the dummy driver: an ordinary laptop lists eDP-1 plus five disconnected connectors, so screen_res->noutput is 6 and the single monitor test never fires, while the same machine under Xvfb reports one output and behaves differently. Counting outputs that have a CRTC makes the two agree.

We carry both fixes downstream and have seen no fallout from either.

probonopd added a commit to probonopd/libs-back that referenced this pull request Sep 20, 2026
The frame of a screen was replaced by _NET_WORKAREA whenever there was a
single monitor, so NSScreen described what the window manager leaves over
rather than the display.  An application could not learn the geometry of
the monitor, and one that reserves space itself, a menu bar or a dock,
could not place itself in the strip it had just reserved, since that strip
lies outside every frame it can see.

The frame is the monitor again, the work area is kept beside it, and
-workAreaForScreen: answers it.  The other servers answer their whole
screen, which is what they reserve nothing of.

This is the backend half of the pair discussed in gnustep#217; the gui half,
which builds -[NSScreen visibleFrame] from the new method, is
gnustep/libs-gui#953.  It sits on top of gnustep#226, whose flip of the
_NET_WORKAREA origin and count of monitors rather than outputs it needs
and does not repeat.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants