Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1691,8 +1691,9 @@ public Point toControl(int x, int y) {
origin_x[0] = origin.x;
origin_y[0] = origin.y;
} else {
long window = eventWindow();
GDK.gdk_window_get_origin(window, origin_x, origin_y);
Point origin = getWindowOrigin();
origin_x[0] = origin.x;
origin_y[0] = origin.y;
}

x -= origin_x[0];
Expand Down Expand Up @@ -1756,8 +1757,9 @@ public Point toDisplay(int x, int y) {
origin_x[0] = origin.x;
origin_y[0] = origin.y;
} else {
long window = eventWindow();
GDK.gdk_window_get_origin(window, origin_x, origin_y);
Point origin = getWindowOrigin();
origin_x[0] = origin.x;
origin_y[0] = origin.y;
}

if ((style & SWT.MIRRORED) != 0) x = getClientWidth() - x;
Expand Down Expand Up @@ -6919,9 +6921,31 @@ Point getWindowOrigin () {
long window = eventWindow ();
GDK.gdk_window_get_origin (window, x, y);

Point monitorOrigin = monitorOrigin ();
if (monitorOrigin != null) {
x [0] += monitorOrigin.x;
y [0] += monitorOrigin.y;
}

return new Point (x [0], y [0]);
}

/**
* Offset that maps window relative GDK coordinates into the space of the monitor showing the
* receiver, or <code>null</code> when none is needed. Wayland reports no global position, so
* without it {@link Monitor} geometry and control coordinates cannot be compared.
*/
Point monitorOrigin () {
if (GTK.GTK4 || !OS.isWayland ()) return null;
// An unmapped Shell has no GdkWindow; the nearest ancestor that has one is on the same monitor.
long window = 0;
for (Control control = this; control != null; control = control.parent) {
window = gtk_widget_get_window (control.getShell ().topHandle ());
if (window != 0) break;
}
return display.monitorOrigin (window);
}

/**
* Gets the position of the top left corner of the control in root window (display) coordinates.
* GTK4 only, do not call on GTK3.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2002,6 +2002,22 @@ boolean filters (int eventType) {
return filterTable.hooks (eventType);
}

/**
* Origin of the monitor showing the given GdkWindow, or <code>null</code> when no offset is
* needed. See {@link Control#monitorOrigin()}.
*/
Point monitorOrigin (long window) {
if (GTK.GTK4 || !OS.isWayland () || window == 0) return null;
long displayHandle = GDK.gdk_display_get_default ();
if (displayHandle == 0) return null;
long monitor = GDK.gdk_display_get_monitor_at_window (displayHandle, window);
if (monitor == 0) return null;
GdkRectangle geometry = new GdkRectangle ();
GDK.gdk_monitor_get_geometry (monitor, geometry);
if (geometry.x == 0 && geometry.y == 0) return null;
return new Point (geometry.x, geometry.y);
}

/**
* Returns the location of the on-screen pointer relative
* to the top left corner of the screen.
Expand All @@ -2024,7 +2040,7 @@ public Point getCursorLocation() {
x[0] = (int)xDouble[0];
y[0] = (int)yDouble[0];
} else {
getWindowPointerPosition(0, x, y, null);
long pointerWindow = getWindowPointerPosition(0, x, y, null);

/*
* Wayland feature: There is no global x/y coordinates in Wayland for security measures, so they
Expand All @@ -2045,6 +2061,17 @@ public Point getCursorLocation() {
y[0]+= offsetY[0];
tempShell = tempShell.getParent().getShell();
}
/*
* Callers compare this against Control.toDisplay(), so use the same space.
* Prefer the monitor of the window under the pointer, which is the one the
* coordinates are relative to; the active shell is only a fallback.
*/
Point origin = monitorOrigin (pointerWindow);
if (origin == null) origin = tempShell.monitorOrigin ();
if (origin != null) {
x[0] += origin.x;
y[0] += origin.y;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,12 @@ void _setVisible (boolean visible) {
GTK3.memmove (eventPtr, event, GdkEventButton.sizeof);
// Bug in GTK?: testing with SWT_MENU_LOCATION_DEBUGGING=1 shows final_rect.x and
// final_rect.y popup menu position is off by 1 compared to this.x and this.y
rect.x = this.x + 1;
rect.y = this.y + 1;
// The rectangle is relative to the shell, so the monitor origin has to go.
Point monitorOrigin = getShell ().monitorOrigin ();
int originX = monitorOrigin != null ? monitorOrigin.x : 0;
int originY = monitorOrigin != null ? monitorOrigin.y : 0;
rect.x = this.x + 1 - originX;
rect.y = this.y + 1 - originY;
}
// Popup the menu and pin it at the top left corner of the GdkRectangle relative to the GdkWindow
GTK3.gtk_menu_popup_at_rect(handle, event.window, rect, GDK.GDK_GRAVITY_NORTH_WEST,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1348,10 +1348,26 @@ public Point getLocation() {
// TODO: GTK4 GtkWindow no longer has the ability to get position
} else {
GTK3.gtk_window_get_position (shellHandle, x, y);
applyMonitorOrigin (x, y);
}
return new Point (x [0], y [0]);
}

@Override
Point monitorOrigin () {
// A child Shell is positioned while still hidden, before its own monitor is known.
if (parent != null) return parent.monitorOrigin ();
return super.monitorOrigin ();
}

/** Shifts a window relative GTK position into display coordinates. */
void applyMonitorOrigin (int [] x, int [] y) {
Point origin = monitorOrigin ();
if (origin == null) return;
x [0] += origin.x;
y [0] += origin.y;
}

@Override
public boolean getMaximized () {
checkWidget();
Expand Down Expand Up @@ -1601,6 +1617,7 @@ long gtk3_button_press_event (long widget, long event) {
long gtk_configure_event (long widget, long event) {
int [] x = new int [1], y = new int [1];
GTK3.gtk_window_get_position (shellHandle, x, y);
applyMonitorOrigin (x, y);

if (!isVisible ()) {
return 0; //We shouldn't handle move/resize events if shell is hidden.
Expand Down Expand Up @@ -2361,8 +2378,10 @@ void resizeBounds (int width, int height, boolean notify) {
GDK.gdk_window_resize (enableWindow, width, height);
}
}
int boxWidth = width - 2*border;
int boxHeight = height - 2*border;
// GTK rejects negative allocations; a shell smaller than its own border must not
// leak a negative size into gtk_widget_size_allocate().
int boxWidth = Math.max (0, width - 2*border);
int boxHeight = Math.max (0, height - 2*border);
if ((style & SWT.RESIZE) == 0) {
GTK.gtk_widget_set_size_request (vboxHandle, boxWidth, boxHeight);
}
Expand Down Expand Up @@ -2415,9 +2434,13 @@ int setBounds (int x, int y, int width, int height, boolean move, boolean resize
}
if (mapped) positionPopover();
} else if (!GTK.GTK4) {
// GTK positions windows in its own space; x and y arrive in display coordinates.
Point origin = monitorOrigin ();
int gtkX = origin != null ? x - origin.x : x;
int gtkY = origin != null ? y - origin.y : y;
int [] x_pos = new int [1], y_pos = new int [1];
GTK3.gtk_window_get_position(shellHandle, x_pos, y_pos);
GTK3.gtk_window_move(shellHandle, x, y);
GTK3.gtk_window_move(shellHandle, gtkX, gtkY);
/*
* Bug in GTK: gtk_window_get_position () is not always up-to-date right after
* gtk_window_move (). The random delays cause problems like bug 445900.
Expand All @@ -2429,11 +2452,11 @@ int setBounds (int x, int y, int width, int height, boolean move, boolean resize
for (int i = 0; i < 1000; i++) {
int [] x2_pos = new int [1], y2_pos = new int [1];
GTK3.gtk_window_get_position(shellHandle, x2_pos, y2_pos);
if (x2_pos[0] == x && y2_pos[0] == y) {
if (x2_pos[0] == gtkX && y2_pos[0] == gtkY) {
break;
}
}
if (x_pos [0] != x || y_pos [0] != y) {
if (x_pos [0] != gtkX || y_pos [0] != gtkY) {
moved = true;
oldX = x;
oldY = y;
Expand Down Expand Up @@ -3611,6 +3634,7 @@ Rectangle getBoundsInPixels () {
GDK.gdk_window_get_root_origin(GTK3.gtk_widget_get_window(shellHandle), x, y);
}
}
if (!GTK.GTK4) applyMonitorOrigin (x, y);
GtkAllocation allocation = new GtkAllocation ();
GTK.gtk_widget_get_allocation (vboxHandle, allocation);
int width = allocation.width;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1054,4 +1054,63 @@ public void test_setLocationII() {
}
super.test_setLocationII();
}

@Test
public void test_degenerateSizeDoesNotExpandShell() {
// A bordered popup asked for a height of 0 used to be allocated -1, which GTK rejects,
// so it fell back to its natural size with its content never laid out. See issue 3539.
// GTK4 backs these shells with a GtkPopover, which never went through that box maths
// and enforces a minimum height of its own.
assumeTrue(SwtTestUtil.isGTK && !SwtTestUtil.isGTK4());
Shell popup = new Shell(shell, SWT.TOOL | SWT.ON_TOP);
popup.setLayout(new FillLayout());
Button child = new Button(popup, SWT.PUSH);
child.setText("some content that is clearly taller than two pixels");
popup.setSize(228, 0);
popup.setVisible(true);
SwtTestUtil.processEvents();
Rectangle clientArea = popup.getClientArea();
popup.dispose();
assertTrue(clientArea.height >= 0, "negative client area: " + clientArea);
assertTrue(clientArea.height <= 2, "shell expanded to its natural size: " + clientArea);
}

@Test
public void test_toDisplayIsOnTheShellsMonitor() {
// Display coordinates and Monitor geometry have to share one coordinate space, otherwise
// callers that clip a location against a monitor discard it. See issue 3539.
// GTK4 has no root coordinates yet, so toDisplay() is still shell relative there.
assumeTrue(SwtTestUtil.isGTK && !SwtTestUtil.isGTK4());
shell.setSize(300, 200);
shell.open();
SwtTestUtil.processEvents();
Rectangle monitor = shell.getMonitor().getBounds();
Point origin = shell.toDisplay(0, 0);
assertTrue(monitor.contains(origin), "toDisplay " + origin + " is outside monitor " + monitor);
}

@Test
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());
shell.setSize(400, 300);
shell.open();
SwtTestUtil.processEvents();
Shell popup = new Shell(shell, SWT.TOOL | SWT.ON_TOP);
popup.setSize(120, 80);
try {
for (int pass = 0; pass < 3; pass++) {
Point location = shell.toDisplay(40 + pass * 10, 40 + pass * 10);
popup.setLocation(location);
popup.setVisible(true);
SwtTestUtil.processEvents();
assertEquals(location, popup.getLocation(), "pass " + pass);
popup.setVisible(false);
SwtTestUtil.processEvents();
}
} finally {
popup.dispose();
}
}
}
Loading