Skip to content

Commit 4b7c450

Browse files
committed
[Win32] Process DPI change events via OS instead of Display#asyncExec
Asynchronous processing of DPI change events is currently done by processing the event for each control via Display#asyncExec. This has several drawbacks: - the events are processed rather late (even after earlier scheduled, lower-priority business events - the events may not be processed while dragging a shell to another monitor in case a browser currently has focus - the events are not processed at all if a Synchronizer is used that does not process async events (such as for an Eclipse workspace during startup) This change replaces the usage of Display#asyncExec for asynchronous processing of DPI events with the a timer message beings sent with a callback to the processed operation via the OS. WM_TIMER events are used as they are processed with low priority by the OS but still with higher priority by SWT than any business-logic-side tasks scheduled at a Display instance.
1 parent de56a75 commit 4b7c450

File tree

1 file changed

+45
-1
lines changed
  • bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets

1 file changed

+45
-1
lines changed

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.eclipse.swt.widgets;
1616

1717

18+
import java.lang.reflect.*;
1819
import java.util.*;
1920
import java.util.concurrent.atomic.*;
2021
import java.util.stream.*;
@@ -5992,7 +5993,7 @@ private void process(Control control, Runnable operation) {
59925993
asyncExec &= (comp.layout != null);
59935994
}
59945995
if (asyncExec) {
5995-
control.getDisplay().asyncExec(operation::run);
5996+
DPIChangeProcessingCallback.schedule(control, operation);
59965997
} else {
59975998
operation.run();
59985999
}
@@ -6010,6 +6011,49 @@ private boolean decrement() {
60106011
}
60116012
}
60126013

6014+
private static class DPIChangeProcessingCallback {
6015+
private final Runnable operation;
6016+
private final long address;
6017+
6018+
private DPIChangeProcessingCallback(Control control, Runnable dpiChangeProcessing) {
6019+
// Has to have TIMERPROC signature, see https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-timerproc
6020+
Callback callback = new Callback(this, "run", void.class, new Type[] { int.class, int.class, int.class, int.class} );
6021+
this.operation = () -> {
6022+
if (!control.isDisposed()) {
6023+
dpiChangeProcessing.run();
6024+
}
6025+
callback.dispose();
6026+
};
6027+
this.address = callback.getAddress();
6028+
}
6029+
6030+
@SuppressWarnings("unused") // Executed as callback method referenced by signature description
6031+
public void run(int hwnd, int msg , int idEvent, int dwTime) {
6032+
OS.KillTimer(hwnd, idEvent);
6033+
operation.run();
6034+
}
6035+
6036+
private long getAddress() {
6037+
return address;
6038+
}
6039+
6040+
static void schedule(Control control, Runnable runnable) {
6041+
try {
6042+
DPIChangeProcessingCallback callback = new DPIChangeProcessingCallback(control, runnable);
6043+
OS.SetTimer(0, 0, 0, callback.getAddress());
6044+
} catch (SWTError error) {
6045+
// In case of too many callbacks, fall back to running the operation synchronously
6046+
if (error.code == SWT.ERROR_NO_MORE_CALLBACKS) {
6047+
runnable.run();
6048+
} else {
6049+
throw error;
6050+
}
6051+
}
6052+
6053+
}
6054+
6055+
}
6056+
60136057
void sendZoomChangedEvent(Event event, Shell shell) {
60146058
if (event.data instanceof DPIChangeExecution dpiExecData) {
60156059
dpiExecData.increment();

0 commit comments

Comments
 (0)