Skip to content

Commit 07fa0f8

Browse files
committed
[win32] fallback for missing DPI change event
This commit adds a fallback mechanism if the OS doesn't send a DPI change as expected. 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 a check is added to Shell::WM_WINDOWPOSCHANGED to trigger a dpi change event if an unexpected DPI value is detected.
1 parent 3cd5b9e commit 07fa0f8

File tree

4 files changed

+48
-32
lines changed

4 files changed

+48
-32
lines changed

bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import static org.junit.Assert.assertEquals;
1717

1818
import java.util.concurrent.*;
19-
import java.util.concurrent.atomic.*;
2019

2120
import org.eclipse.swt.*;
2221
import org.eclipse.swt.internal.*;
@@ -30,30 +29,26 @@ class GCWin32Tests {
3029

3130
@Test
3231
public void gcZoomLevelMustChangeOnShellZoomChange() {
33-
Shell shell = new Shell(Display.getDefault());
32+
checkGcZoomLevelOnCanvas(DPIUtil.getNativeDeviceZoom());
33+
checkGcZoomLevelOnCanvas(DPIUtil.getNativeDeviceZoom()*2);
34+
}
3435

36+
private void checkGcZoomLevelOnCanvas(int expectedZoom) {
37+
Display display = Display.getDefault();
38+
Shell shell = new Shell(display);
3539
CompletableFuture<Integer> gcNativeZoom = new CompletableFuture<>();
36-
CompletableFuture<Integer> scaledGcNativeZoom = new CompletableFuture<>();
37-
int zoom = DPIUtil.getDeviceZoom();
38-
AtomicBoolean isScaled = new AtomicBoolean(false);
39-
shell.addListener(SWT.Paint, event -> {
40-
if (isScaled.get()) {
41-
scaledGcNativeZoom.complete(event.gc.getGCData().nativeZoom);
42-
} else {
43-
gcNativeZoom.complete(event.gc.getGCData().nativeZoom);
44-
}
45-
});
46-
47-
shell.open();
48-
assertEquals("GCData must have a zoom level equal to the actual zoom level of the widget/shell", DPIUtil.getNativeDeviceZoom(), (int) gcNativeZoom.join());
4940

50-
int newSWTZoom = zoom * 2;
51-
DPITestUtil.changeDPIZoom(shell, newSWTZoom);
52-
isScaled.set(true);
53-
shell.setVisible(false);
54-
shell.setVisible(true);
41+
Canvas canvas = new Canvas(shell, SWT.NONE);
42+
canvas.setSize(20, 20);
43+
shell.open ();
44+
canvas.addPaintListener(event -> {
45+
gcNativeZoom.complete(event.gc.getGCData().nativeZoom);
46+
});
5547

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

5954
@Test

bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() {
6060
DPIUtil.setMonitorSpecificScaling(true);
6161
Display display = Display.getDefault();
6262
Shell shell = new Shell(display);
63-
DPITestUtil.changeDPIZoom(shell, 175);
64-
6563
Button button = new Button(shell, SWT.PUSH);
6664
button.setText("Widget Test");
67-
button.setBounds(new Rectangle(0, 47, 200, 47));
6865
shell.open();
66+
DPITestUtil.changeDPIZoom(shell, 175);
67+
68+
button.setBounds(new Rectangle(0, 47, 200, 47));
6969
assertEquals("Control::setBounds(Rectangle) doesn't scale up correctly",
7070
new Rectangle(0, 82, 350, 83), button.getBoundsInPixels());
7171

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4928,21 +4928,24 @@ LRESULT WM_DESTROY (long wParam, long lParam) {
49284928
return null;
49294929
}
49304930

4931+
void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) {
4932+
DPIUtil.setDeviceZoom (newNativeZoom);
4933+
4934+
float scalingFactor = 1f * DPIUtil.getZoomForAutoscaleProperty(newNativeZoom) / DPIUtil.getZoomForAutoscaleProperty(nativeZoom);
4935+
DPIZoomChangeRegistry.applyChange(this, newNativeZoom, scalingFactor);
4936+
4937+
this.setBoundsInPixels(newBoundsInPixels.y, newBoundsInPixels.y, newBoundsInPixels.width, newBoundsInPixels.height);
4938+
}
4939+
49314940
LRESULT WM_DPICHANGED (long wParam, long lParam) {
49324941
// Map DPI to Zoom and compare
49334942
int newNativeZoom = DPIUtil.mapDPIToZoom (OS.HIWORD (wParam));
49344943
if (getDisplay().isRescalingAtRuntime()) {
49354944
Device.win32_destroyUnusedHandles(getDisplay());
4936-
int oldNativeZoom = nativeZoom;
4937-
if (newNativeZoom != oldNativeZoom) {
4938-
DPIUtil.setDeviceZoom (newNativeZoom);
4939-
4940-
float scalingFactor = 1f * DPIUtil.getZoomForAutoscaleProperty(newNativeZoom) / DPIUtil.getZoomForAutoscaleProperty(oldNativeZoom);
4941-
DPIZoomChangeRegistry.applyChange(this, newNativeZoom, scalingFactor);
4942-
4945+
if (newNativeZoom != nativeZoom) {
49434946
RECT rect = new RECT ();
49444947
COM.MoveMemory(rect, lParam, RECT.sizeof);
4945-
this.setBoundsInPixels(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top);
4948+
handleMonitorSpecificDpiChange(newNativeZoom, new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top));
49464949
return LRESULT.ZERO;
49474950
}
49484951
} else {

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,6 +2685,24 @@ LRESULT WM_WINDOWPOSCHANGING (long wParam, long lParam) {
26852685
return result;
26862686
}
26872687

2688+
@Override
2689+
LRESULT WM_WINDOWPOSCHANGED (long wParam, long lParam) {
2690+
LRESULT result = super.WM_WINDOWPOSCHANGED(wParam, lParam);
2691+
// When the process is started with System DPI awareness and
2692+
// only the thread is PerMonitorV2 aware, there are some scenarios, when the
2693+
// OS does not send a DPI change event when a child Shell is positioned and
2694+
// opened on another monitor as its parent Shell. To work around that limitation
2695+
// this check is added to trigger a dpi change event if an unexpected DPI value is
2696+
// detected.
2697+
if (display.isRescalingAtRuntime()) {
2698+
int dpiForWindow = DPIUtil.mapDPIToZoom(OS.GetDpiForWindow(getShell().handle));
2699+
if (dpiForWindow != nativeZoom) {
2700+
handleMonitorSpecificDpiChange(dpiForWindow, DPIUtil.scaleUp(getBounds(), dpiForWindow));
2701+
}
2702+
}
2703+
return result;
2704+
}
2705+
26882706
private static void handleDPIChange(Widget widget, int newZoom, float scalingFactor) {
26892707
if (!(widget instanceof Shell shell)) {
26902708
return;

0 commit comments

Comments
 (0)