Skip to content

[GTK] Anchor Wayland display coordinates to the monitor of the window - #3541

Open
vogella wants to merge 1 commit into
eclipse-platform:masterfrom
vogella:wayland-monitor-coordinates
Open

vogella wants to merge 1 commit into
eclipse-platform:masterfrom
vogella:wayland-monitor-coordinates

Conversation

@vogella

@vogella vogella commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

On Wayland a client is never told where its window is, so SWT returned window relative display coordinates while Monitor geometry stayed global. Anything that clips a location against a monitor was therefore mixing two coordinate spaces, which the JFace information controls do: on a multi-monitor layout whose client areas do not start near y=0, every candidate position is rejected, so javadoc hovers came up empty and the content assist javadoc popup opened on a different monitor.

Wayland does tell us which monitor a surface is on, so display coordinates are now anchored to that monitor origin and the offset is removed again when a window or menu is positioned. A whole shell tree uses the monitor of its root toplevel as the one anchor, since child shells are positioned relative to their parent and a hidden or edge-straddling popup reports no or a different monitor of its own. Display.getCursorLocation() is anchored the same way, since callers compare it against Control.toDisplay().

Fixes #3539
Fixes #538

The reporter of #3539 confirmed both the empty hovers and the misplaced content assist popup are gone on their three monitor setup. #538 is the same root cause reported from several setups, all of them with a second monitor whose top edge is not aligned with the primary, and all of them worked around by aligning the monitors. The window position within its monitor is still unknown, but popups are placed relative to their parent and unaffected by that. X11 and GTK4 are unchanged.

@vogella
vogella requested a review from akurtakov August 26, 2026 16:37
@vogella

vogella commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

@akurtakov in case you have time, please review, its in two commits to separate the two different changes, the first one is a protection against bad callers, the second the fix for the issue.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Anchors GTK3 Wayland coordinates to monitor origins and prevents invalid negative shell allocations.

Changes:

  • Aligns shell, control, cursor, and menu coordinates with monitor geometry.
  • Converts display coordinates back before native positioning.
  • Clamps shell content allocations to non-negative sizes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
Shell.java Anchors shell coordinates and hardens allocation sizing.
Menu.java Converts popup positions to shell-relative coordinates.
Display.java Anchors cursor coordinates to a monitor.
Control.java Adds monitor-aware coordinate conversion.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java Outdated
Comment thread bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java Outdated
@vogella
vogella force-pushed the wayland-monitor-coordinates branch from fdfe5fb to 11e33fc Compare August 26, 2026 16:59
@github-actions

github-actions Bot commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

 1 017 files  ± 0   1 017 suites  ±0   1h 24m 56s ⏱️ + 10m 48s
 5 059 tests + 2   5 031 ✅ +2     28 💤 ± 0  0 ❌ ±0 
29 719 runs  +12  27 177 ✅ +2  2 542 💤 +10  0 ❌ ±0 

Results for commit 966d12d. ± Comparison against base commit eeb3482.

♻️ This comment has been updated with latest results.

@vogella
vogella force-pushed the wayland-monitor-coordinates branch from c9af240 to cae94f1 Compare August 26, 2026 18:22
@akurtakov
akurtakov force-pushed the wayland-monitor-coordinates branch from cae94f1 to 14721d1 Compare August 31, 2026 21:07
@vogella
vogella force-pushed the wayland-monitor-coordinates branch 2 times, most recently from 785e864 to 02d3bf8 Compare September 15, 2026 13:27
@akurtakov

Copy link
Copy Markdown
Member

Needs manual rebase.

@vogella
vogella force-pushed the wayland-monitor-coordinates branch from 02d3bf8 to 4b6d43a Compare September 21, 2026 07:55
@vogella

vogella commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

I'm using this patch since weeks in my custom IDE build and plan to merge this tomorrow unless I hear otherwise.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Cursor anchoring mishandles zero-origin monitors and fails whenever no shell is active.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
Resolved since last review (2)

Comment thread bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java Outdated
@akurtakov

akurtakov commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

I don't have the multimonitor setup to properly test but my claude analysis pointed few things, it would be nice to try them just so we are sure:

Blocking

  1. Drag-and-drop into Tree/Table breaks on a non-primary monitor
  • DropTarget.java:~879 still builds event.x/y from the raw gdk_window_get_origin() + local, with no anchor.
  • Everything that uses those coordinates converts back with toControl(), which now subtracts the anchored origin:
    • DropTargetEffect.getItem() (lines 100 and 118)
    • TableDropTargetEffect:138
    • TreeDropTargetEffect:159
    • ViewerDropAdapter and anything else in user code that calls toControl(event.x, event.y)
  • Result: on a monitor at (1920,0), getItem looks up x−1920. That gives no item (or the wrong one), and the insert/scroll feedback goes wrong. The Package Explorer and other viewer drag-and-drop would be affected.
  • Fix: DropTarget should use control.toDisplay(x, y), or apply the same anchor.
  1. Dialogs get the wrong anchor
  • Shell.monitorOrigin() does if (parent != null) return parent.monitorOrigin() for every child shell. The reasoning ("a child Shell is positioned relative to its parent") only holds for ON_TOP child shells, which are GTK_WINDOW_POPUP (Shell.java:775).
  • Every other child shell (dialogs, wizards, SWT.TOOL without ON_TOP) is a GTK_WINDOW_TOPLEVEL with transient_for. On Wayland each of those has its own coordinate system, and the compositor can put it on another monitor.
  • In that case toDisplay() inside the dialog uses the parent's monitor while dialog.getMonitor() returns the dialog's own. That is the same kind of mismatch this PR is fixing, now in dialogs.
  • Fix: use the parent's anchor only for popup shells (or while the shell is unmapped); otherwise use the shell's own window.

Should fix

  1. getCursorLocation() uses a different anchor than toDisplay()
  • It prefers monitorOrigin(pointerWindow), the monitor of whatever window is under the pointer.
  • The PR's own reasoning says to avoid that: a popup straddling a monitor edge reports a different monitor than its root toplevel.
  • So with the pointer over such a popup, getCursorLocation() and toDisplay() differ by a whole monitor offset. That breaks JFace hover "is the mouse still inside" checks.
  • Fix: map pointerWindow to its SWT control (gdk_window_get_user_data → display.getWidget) and use getShell().monitorOrigin().
  1. Other callers of gtk_window_move weren't updated
  • ToolTip (SWT.BALLOON, a GTK_WINDOW_POPUP, lines ~212–284 and 594) passes display coordinates straight to gtk_window_move. It will now be off by the monitor origin when callers use toDisplay().
  • Tracker (Tracker.java:826) draws with rectangles in display coordinates but compares them against raw pointer positions. Same kind of drift, lower impact.
  • Accessibility extents in AccessibleObject.java (lines 4537 and 4557) also stay unanchored. That matters less, since screen readers on Wayland can't use global coordinates anyway.
  1. The anchor can change between set and get
  • setBounds subtracts the anchor that applies when it's called. If the parent (or the shell itself) isn't realized yet, there is no anchor, but getLocation() after mapping adds one. setLocation(p) then getLocation() no longer returns p.
  • The anchor also changes when the root window moves to another monitor, and no SWT.Move event is sent. Every cached display coordinate becomes stale.
  • Neither is necessarily a blocker, but both should at least be written down in the code or commit message.

@vogella
vogella force-pushed the wayland-monitor-coordinates branch from 2cc53e9 to 2739f90 Compare September 29, 2026 13:56
@vogella

vogella commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks, updated the commit.

  1. Fixed. DropTarget now adds the monitor origin of the shell tree, so toControl() in the drop effects matches again. I did not use control.toDisplay(x, y): for Tree and Table toDisplay() is relative to the bin window, which would shift the drop position by the header height.
  2. Agreed on the analysis, but it is not a regression: dialogs on a monitor with a non-zero origin were broken before this PR as well. I'd like to handle transient toplevels in a follow-up so this one stays focused on popups.
  3. Fixed. getCursorLocation() now takes the anchor from the SWT shell tree under the pointer, falling back to the active shell, so it matches toDisplay(). This also fixes the zero-origin monitor issue Copilot flagged.
  4. Fixed for the balloon ToolTip, the Tracker overlay and pointer positions, and also for the IME preedit window, which had the same problem (gtk_window_move() with toDisplay() coordinates). Accessibility needs no change: the extents only go out to AT-SPI and are never converted back by SWT, so they cannot mismatch with toDisplay().
  5. Documented in the commit message.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Several cursor, tooltip, and tracker paths still mix anchored and unanchored Wayland coordinates.

Review effort: Balanced
Findings: 4 Medium severity · 1 Low severity

Open (5)

Comment thread bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java Outdated
Comment thread bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolTip.java Outdated
Comment thread bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tracker.java Outdated
@vogella
vogella force-pushed the wayland-monitor-coordinates branch from c6326ea to f637c0f Compare September 29, 2026 14:26
@vogella

vogella commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the Copilot findings in the updated commit:

  • Zero-origin monitor in getCursorLocation(): fixed, the anchor now comes from the SWT shell tree under the pointer, so a monitor at 0,0 is no longer mistaken for a failed lookup.
  • activeShell == null: the anchor lookup now runs without an active shell, which is only needed for the popup offsets.
  • Balloon ToolTip at the pointer position: the monitor lookup now adds the anchor for every location except the tray item.
  • Tracker(Display): all pointer samples, including the key and resize paths, go through one helper that adds the anchor.
  • Regression test with a non-zero monitor origin: a test cannot choose the monitor the compositor maps a window on, so the tests only bite on a multi-monitor Wayland session, as noted in the commit message.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Tracker overlays can become invisible on monitors positioned left of or above the primary display.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (5)

@vogella
vogella force-pushed the wayland-monitor-coordinates branch from f637c0f to 8be39a7 Compare September 29, 2026 14:59
@vogella

vogella commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

All findings are handled in 8be39a7:

  • Tracker overlay with negative monitor origins: the overlay now spans the union of all monitors and the region is offset to match.
  • getCursorLocation without an active shell: the lookup of the window under the pointer no longer depends on activeShell, which is now only the fallback.
  • ToolTip default location: the monitor origin is applied to every location except a status item.
  • Tracker pointer samples: all pointer readings go through getPointerPosition(), which anchors them.
  • Monitor at (0,0): the anchor comes from the control under the pointer, so null only means "no offset".
  • Single-monitor test: correct, as the commit message notes. Making it fail against the old code needs a lookup that can be swapped out, which is out of scope here.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Cursor offsets can still mix active and pointer shell trees, and the added tests do not exercise non-zero monitor origins.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java Outdated
@vogella
vogella force-pushed the wayland-monitor-coordinates branch from 8be39a7 to 61a8799 Compare September 30, 2026 12:58
@vogella

vogella commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

Fixed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Tracker changes affect X11 and GTK4 despite the stated backend scope.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (2)

@vogella
vogella force-pushed the wayland-monitor-coordinates branch from 61a8799 to 9910235 Compare October 2, 2026 07:11
@akurtakov
akurtakov force-pushed the wayland-monitor-coordinates branch from 9910235 to 2273e4e Compare October 7, 2026 05:07
@akurtakov
akurtakov requested a balanced review from Copilot October 7, 2026 05:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The new tests do not deterministically exercise Wayland with a nonzero monitor origin, so they cannot catch the reported regression on standard CI layouts.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

public void test_popupLocationRoundTrip() {
// JFace reuses one popup: position, show, hide, reposition. Every pass has to round-trip,
// including the ones where the shell is still mapped. See issue 3539.
assumeTrue(SwtTestUtil.isGTK && !SwtTestUtil.isGTK4());
@vogella
vogella force-pushed the wayland-monitor-coordinates branch 4 times, most recently from 945ee64 to 1767421 Compare October 16, 2026 21:19
Wayland never tells a client where its window is, so gdk_window_get_origin
returns window relative values while Monitor geometry is global. Code that
clips a location against a monitor then mixes two coordinate spaces.

The JFace information controls do exactly that. With a monitor layout whose
client areas do not start near y=0, every anchor is rejected: hovers come up
empty and the content assist javadoc popup lands on a different monitor.

Wayland does report which monitor a surface is on. Anchor display
coordinates to that origin, and remove it again when positioning a window or
a menu. Display.getCursorLocation(), the MenuDetect event and DropTarget
event coordinates get the same treatment, since callers compare them against
Control.toDisplay() or convert them with toControl(). The cursor location
takes its anchor from the shell under the pointer and no longer adds popup
positions, which the pointer lookup already includes. Balloon ToolTips, the
IME preedit window and the Tracker overlay remove the origin before
gtk_window_move(), and Tracker(Display) adds it to its pointer positions.
The Tracker overlay spans all monitors on GTK3 Wayland, including those at a
negative origin. The accessibility callbacks that translate ATK window
coordinates for the Accessible listeners add the same anchor to the toplevel
origin, since the listeners answer with toDisplay() coordinates.

Each toplevel window is anchored to its own monitor. An ON_TOP popup shares
the anchor of its parent, since it is positioned relative to it and one that
is hidden or straddles a monitor edge gets a guessed or a different monitor
of its own. A child dialog uses its parent's anchor until it is shown.

The window's position within its monitor is still unknown. Popups are placed
relative to their parent and unaffected by that remaining error. The anchor
is also not known before the root shell is realized and changes without an
SWT.Move event when the root shell moves to another monitor, so display
coordinates taken across either of these are off by the monitor origin.

X11 and GTK4 are unchanged.

The two tests pass on a single monitor whose origin is 0,0, so they only bite
on a multi-monitor Wayland session, but test_popupLocationRoundTrip covers the
reuse cycle that JFace performs, which is where a wrong anchor shows up. Both
are GTK3 only, since GTK4 toDisplay() is still shell relative.

Fixes eclipse-platform#3539
Fixes eclipse-platform#538

Assisted-by: multiple AI agents and layers of automated tooling 🤖
@vogella
vogella force-pushed the wayland-monitor-coordinates branch from 1767421 to 966d12d Compare October 16, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty content in popup/info windows using Wayland (Forcing X11 backend cause Eclipse IDE to hang) Tooltip popup locations are wrong (dual monitor)

3 participants