Skip to content

Commit fdfe5fb

Browse files
committed
[GTK] Anchor Wayland display coordinates to the monitor of the window
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() gets the same treatment, since callers compare it against Control.toDisplay(). The window's position within its monitor is still unknown. Popups are placed relative to their parent and unaffected by that remaining error. X11 and GTK4 are unchanged. Fixes #3539
1 parent b8cded7 commit fdfe5fb

4 files changed

Lines changed: 69 additions & 9 deletions

File tree

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,8 +1691,9 @@ public Point toControl(int x, int y) {
16911691
origin_x[0] = origin.x;
16921692
origin_y[0] = origin.y;
16931693
} else {
1694-
long window = eventWindow();
1695-
GDK.gdk_window_get_origin(window, origin_x, origin_y);
1694+
Point origin = getWindowOrigin();
1695+
origin_x[0] = origin.x;
1696+
origin_y[0] = origin.y;
16961697
}
16971698

16981699
x -= origin_x[0];
@@ -1756,8 +1757,9 @@ public Point toDisplay(int x, int y) {
17561757
origin_x[0] = origin.x;
17571758
origin_y[0] = origin.y;
17581759
} else {
1759-
long window = eventWindow();
1760-
GDK.gdk_window_get_origin(window, origin_x, origin_y);
1760+
Point origin = getWindowOrigin();
1761+
origin_x[0] = origin.x;
1762+
origin_y[0] = origin.y;
17611763
}
17621764

17631765
if ((style & SWT.MIRRORED) != 0) x = getClientWidth() - x;
@@ -6919,9 +6921,39 @@ Point getWindowOrigin () {
69196921
long window = eventWindow ();
69206922
GDK.gdk_window_get_origin (window, x, y);
69216923

6924+
Point monitorOrigin = monitorOrigin ();
6925+
if (monitorOrigin != null) {
6926+
x [0] += monitorOrigin.x;
6927+
y [0] += monitorOrigin.y;
6928+
}
6929+
69226930
return new Point (x [0], y [0]);
69236931
}
69246932

6933+
/**
6934+
* Offset that maps window relative GDK coordinates into the space of the monitor showing the
6935+
* receiver, or <code>null</code> when none is needed. Wayland reports no global position, so
6936+
* without it {@link Monitor} geometry and control coordinates cannot be compared.
6937+
*/
6938+
Point monitorOrigin () {
6939+
if (GTK.GTK4 || !OS.isWayland ()) return null;
6940+
long displayHandle = GDK.gdk_display_get_default ();
6941+
if (displayHandle == 0) return null;
6942+
// An unmapped Shell has no GdkWindow; the nearest ancestor that has one is on the same monitor.
6943+
long window = 0;
6944+
for (Control control = this; control != null; control = control.parent) {
6945+
window = gtk_widget_get_window (control.getShell ().topHandle ());
6946+
if (window != 0) break;
6947+
}
6948+
if (window == 0) return null;
6949+
long monitor = GDK.gdk_display_get_monitor_at_window (displayHandle, window);
6950+
if (monitor == 0) return null;
6951+
GdkRectangle geometry = new GdkRectangle ();
6952+
GDK.gdk_monitor_get_geometry (monitor, geometry);
6953+
if (geometry.x == 0 && geometry.y == 0) return null;
6954+
return new Point (geometry.x, geometry.y);
6955+
}
6956+
69256957
/**
69266958
* Gets the position of the top left corner of the control in root window (display) coordinates.
69276959
* GTK4 only, do not call on GTK3.

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,6 +2045,8 @@ public Point getCursorLocation() {
20452045
y[0]+= offsetY[0];
20462046
tempShell = tempShell.getParent().getShell();
20472047
}
2048+
// Callers compare this against Control.toDisplay(), so use the same space.
2049+
tempShell.applyMonitorOrigin (x, y);
20482050
}
20492051
}
20502052

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,8 +447,12 @@ void _setVisible (boolean visible) {
447447
GTK3.memmove (eventPtr, event, GdkEventButton.sizeof);
448448
// Bug in GTK?: testing with SWT_MENU_LOCATION_DEBUGGING=1 shows final_rect.x and
449449
// final_rect.y popup menu position is off by 1 compared to this.x and this.y
450-
rect.x = this.x + 1;
451-
rect.y = this.y + 1;
450+
// The rectangle is relative to the shell, so the monitor origin has to go.
451+
Point monitorOrigin = getShell ().monitorOrigin ();
452+
int originX = monitorOrigin != null ? monitorOrigin.x : 0;
453+
int originY = monitorOrigin != null ? monitorOrigin.y : 0;
454+
rect.x = this.x + 1 - originX;
455+
rect.y = this.y + 1 - originY;
452456
}
453457
// Popup the menu and pin it at the top left corner of the GdkRectangle relative to the GdkWindow
454458
GTK3.gtk_menu_popup_at_rect(handle, event.window, rect, GDK.GDK_GRAVITY_NORTH_WEST,

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,10 +1348,26 @@ public Point getLocation() {
13481348
// TODO: GTK4 GtkWindow no longer has the ability to get position
13491349
} else {
13501350
GTK3.gtk_window_get_position (shellHandle, x, y);
1351+
applyMonitorOrigin (x, y);
13511352
}
13521353
return new Point (x [0], y [0]);
13531354
}
13541355

1356+
@Override
1357+
Point monitorOrigin () {
1358+
// A child Shell is positioned while still hidden, before its own monitor is known.
1359+
if (parent != null) return parent.monitorOrigin ();
1360+
return super.monitorOrigin ();
1361+
}
1362+
1363+
/** Shifts a window relative GTK position into display coordinates. */
1364+
void applyMonitorOrigin (int [] x, int [] y) {
1365+
Point origin = monitorOrigin ();
1366+
if (origin == null) return;
1367+
x [0] += origin.x;
1368+
y [0] += origin.y;
1369+
}
1370+
13551371
@Override
13561372
public boolean getMaximized () {
13571373
checkWidget();
@@ -1601,6 +1617,7 @@ long gtk3_button_press_event (long widget, long event) {
16011617
long gtk_configure_event (long widget, long event) {
16021618
int [] x = new int [1], y = new int [1];
16031619
GTK3.gtk_window_get_position (shellHandle, x, y);
1620+
applyMonitorOrigin (x, y);
16041621

16051622
if (!isVisible ()) {
16061623
return 0; //We shouldn't handle move/resize events if shell is hidden.
@@ -2417,9 +2434,13 @@ int setBounds (int x, int y, int width, int height, boolean move, boolean resize
24172434
}
24182435
if (mapped) positionPopover();
24192436
} else if (!GTK.GTK4) {
2437+
// GTK positions windows in its own space; x and y arrive in display coordinates.
2438+
Point origin = monitorOrigin ();
2439+
int gtkX = origin != null ? x - origin.x : x;
2440+
int gtkY = origin != null ? y - origin.y : y;
24202441
int [] x_pos = new int [1], y_pos = new int [1];
24212442
GTK3.gtk_window_get_position(shellHandle, x_pos, y_pos);
2422-
GTK3.gtk_window_move(shellHandle, x, y);
2443+
GTK3.gtk_window_move(shellHandle, gtkX, gtkY);
24232444
/*
24242445
* Bug in GTK: gtk_window_get_position () is not always up-to-date right after
24252446
* gtk_window_move (). The random delays cause problems like bug 445900.
@@ -2431,11 +2452,11 @@ int setBounds (int x, int y, int width, int height, boolean move, boolean resize
24312452
for (int i = 0; i < 1000; i++) {
24322453
int [] x2_pos = new int [1], y2_pos = new int [1];
24332454
GTK3.gtk_window_get_position(shellHandle, x2_pos, y2_pos);
2434-
if (x2_pos[0] == x && y2_pos[0] == y) {
2455+
if (x2_pos[0] == gtkX && y2_pos[0] == gtkY) {
24352456
break;
24362457
}
24372458
}
2438-
if (x_pos [0] != x || y_pos [0] != y) {
2459+
if (x_pos [0] != gtkX || y_pos [0] != gtkY) {
24392460
moved = true;
24402461
oldX = x;
24412462
oldY = y;
@@ -3613,6 +3634,7 @@ Rectangle getBoundsInPixels () {
36133634
GDK.gdk_window_get_root_origin(GTK3.gtk_widget_get_window(shellHandle), x, y);
36143635
}
36153636
}
3637+
if (!GTK.GTK4) applyMonitorOrigin (x, y);
36163638
GtkAllocation allocation = new GtkAllocation ();
36173639
GTK.gtk_widget_get_allocation (vboxHandle, allocation);
36183640
int width = allocation.width;

0 commit comments

Comments
 (0)