Skip to content

[win32] fallback for missing DPI change event #1863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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 @@ -16,7 +16,6 @@
import static org.junit.Assert.assertEquals;

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
Expand All @@ -30,30 +29,26 @@ class GCWin32Tests {

@Test
public void gcZoomLevelMustChangeOnShellZoomChange() {
Shell shell = new Shell(Display.getDefault());
checkGcZoomLevelOnCanvas(DPIUtil.getNativeDeviceZoom());
checkGcZoomLevelOnCanvas(DPIUtil.getNativeDeviceZoom()*2);
}

private void checkGcZoomLevelOnCanvas(int expectedZoom) {
Display display = Display.getDefault();
Shell shell = new Shell(display);
CompletableFuture<Integer> gcNativeZoom = new CompletableFuture<>();
CompletableFuture<Integer> scaledGcNativeZoom = new CompletableFuture<>();
int zoom = DPIUtil.getDeviceZoom();
AtomicBoolean isScaled = new AtomicBoolean(false);
shell.addListener(SWT.Paint, event -> {
if (isScaled.get()) {
scaledGcNativeZoom.complete(event.gc.getGCData().nativeZoom);
} else {
gcNativeZoom.complete(event.gc.getGCData().nativeZoom);
}
});

shell.open();
assertEquals("GCData must have a zoom level equal to the actual zoom level of the widget/shell", DPIUtil.getNativeDeviceZoom(), (int) gcNativeZoom.join());

int newSWTZoom = zoom * 2;
DPITestUtil.changeDPIZoom(shell, newSWTZoom);
isScaled.set(true);
shell.setVisible(false);
shell.setVisible(true);
Canvas canvas = new Canvas(shell, SWT.NONE);
canvas.setSize(20, 20);
shell.open ();
canvas.addPaintListener(event -> {
gcNativeZoom.complete(event.gc.getGCData().nativeZoom);
});

assertEquals("GCData must have a zoom level equal to the actual zoom level of the widget/shell on zoomChanged event", newSWTZoom, (int) scaledGcNativeZoom.join());
DPITestUtil.changeDPIZoom(shell, expectedZoom);
canvas.update();
assertEquals("GCData must have a zoom level equal to the actual zoom level of the widget/shell", expectedZoom, (int) gcNativeZoom.join());
shell.dispose();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() {
DPIUtil.setMonitorSpecificScaling(true);
Display display = Display.getDefault();
Shell shell = new Shell(display);
DPITestUtil.changeDPIZoom(shell, 175);

Button button = new Button(shell, SWT.PUSH);
button.setText("Widget Test");
button.setBounds(new Rectangle(0, 47, 200, 47));
shell.open();
DPITestUtil.changeDPIZoom(shell, 175);

button.setBounds(new Rectangle(0, 47, 200, 47));
assertEquals("Control::setBounds(Rectangle) doesn't scale up correctly",
new Rectangle(0, 82, 350, 83), button.getBoundsInPixels());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4936,21 +4936,22 @@ LRESULT WM_DESTROY (long wParam, long lParam) {
return null;
}

void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) {
float scalingFactor = 1f * DPIUtil.getZoomForAutoscaleProperty(newNativeZoom) / DPIUtil.getZoomForAutoscaleProperty(nativeZoom);
DPIUtil.setDeviceZoom (newNativeZoom);
DPIZoomChangeRegistry.applyChange(this, newNativeZoom, scalingFactor);
this.setBoundsInPixels(newBoundsInPixels.x, newBoundsInPixels.y, newBoundsInPixels.width, newBoundsInPixels.height);
}

LRESULT WM_DPICHANGED (long wParam, long lParam) {
// Map DPI to Zoom and compare
int newNativeZoom = DPIUtil.mapDPIToZoom (OS.HIWORD (wParam));
if (getDisplay().isRescalingAtRuntime()) {
Device.win32_destroyUnusedHandles(getDisplay());
int oldNativeZoom = nativeZoom;
if (newNativeZoom != oldNativeZoom) {
DPIUtil.setDeviceZoom (newNativeZoom);

float scalingFactor = 1f * DPIUtil.getZoomForAutoscaleProperty(newNativeZoom) / DPIUtil.getZoomForAutoscaleProperty(oldNativeZoom);
DPIZoomChangeRegistry.applyChange(this, newNativeZoom, scalingFactor);

if (newNativeZoom != nativeZoom) {
RECT rect = new RECT ();
COM.MoveMemory(rect, lParam, RECT.sizeof);
this.setBoundsInPixels(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top);
handleMonitorSpecificDpiChange(newNativeZoom, new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top));
return LRESULT.ZERO;
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2685,6 +2685,26 @@ LRESULT WM_WINDOWPOSCHANGING (long wParam, long lParam) {
return result;
}

@Override
LRESULT WM_WINDOWPOSCHANGED (long wParam, long lParam) {
LRESULT result = super.WM_WINDOWPOSCHANGED(wParam, lParam);
// When the process is started with System DPI awareness and
// only the thread is PerMonitorV2 aware, there are some scenarios, when the
// OS does not send a DPI change event when a child Shell is positioned and
// opened on another monitor as its parent Shell. To work around that limitation
// this check is added to trigger a dpi change event if an unexpected DPI value is
// detected.
if (display.isRescalingAtRuntime()) {
int dpiForWindow = DPIUtil.mapDPIToZoom(OS.GetDpiForWindow(getShell().handle));
if (dpiForWindow != nativeZoom) {
WINDOWPOS lpwp = new WINDOWPOS ();
OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
handleMonitorSpecificDpiChange(dpiForWindow, new Rectangle(lpwp.x, lpwp.y, lpwp.cx, lpwp.cy));
}
}
return result;
}

private static void handleDPIChange(Widget widget, int newZoom, float scalingFactor) {
if (!(widget instanceof Shell shell)) {
return;
Expand Down
Loading