Skip to content

Commit b1803cb

Browse files
committed
add documentation about this resolution-checking code
1 parent c78f22a commit b1803cb

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

build/windows/fenster/README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Fenster
2+
3+
This is code to detect the current settings for Windows scaling (Display Settings → Scale and layout → Change the size of text, apps, and other items.)
4+
5+
Generally speaking, this is a nightmare to deal with. The solution, starting in Processing 4.0 beta 6 has two parts:
6+
7+
* When running from the PDE, pass the `--ui-scale` parameter to `PApplet` (see the code for `processing.mode.java.Runner`) based on what comes back from `Toolkit.getScreenResolution()`.
8+
* When running independently, check for that parameter, and if it's not set, launch a tiny helper app that just returns the DPI value.
9+
10+
With those values in hand, the sketch sets the `sun.java2d.uiScale` to either 1 or 2. Using fractional values produces [ugly results](https://github.com/processing/processing4/issues/378). Similarly, we do not set uiScale to 3 when scaling is at 300%. If you want larger sketches, use `scale()` in your code.
11+
12+
13+
# Approaches
14+
15+
## Using AWT
16+
17+
The [Toolkit.getScreenResolution()](https://docs.oracle.com/javase/8/docs/api/java/awt/Toolkit.html#getScreenResolution--) method does what we want, but as soon as any AWT calls are made, it's no longer possible to set the property for `sun.java2d.uiScale` once AWT calls have been made.
18+
19+
20+
## Use a helper application
21+
22+
This is the solution we're using, which feels a little fragile, but it's working. No need to extract files, adds only 8 Kb, if it fails it doesn't bring down the entire app. (See below for downsides with the other approaches.)
23+
24+
This was done by first doing the JNI setup with MSYS2, and then adding a line to the `Makefile` to just create the tiny `.exe`.
25+
26+
27+
## Use JNI
28+
29+
* A long explanation of a lot of this that might be helpful for someone, though didn't use it
30+
* <https://mariusbancila.ro/blog/2021/05/19/how-to-build-high-dpi-aware-native-desktop-applications/>
31+
32+
33+
### Building the JNI code
34+
35+
* Install MSYS2 from <https://www.msys2.org/>.
36+
37+
* Within an MSYS shell, run updates and install `gcc`
38+
39+
pacman -Syu
40+
pacman -S base-devel gcc
41+
42+
* To get your Windows `PATH` to work (i.e. to find Java), you'll probably also need:
43+
44+
MSYS2_PATH_TYPE=inherit
45+
46+
* Had to edit `$JAVA_HOME/include/win32/jni_md.h` to modify the definition of `jlong`
47+
* <https://stackoverflow.com/a/51775636>
48+
* other approaches for it <https://gist.github.com/ssfang/e52c00cd446081cd72a982a2dd8634d4#file-readme-md> (section under “jni with cygwin gcc”)
49+
50+
51+
### Windows Reference
52+
53+
Resources for the necessary API calls on Windows
54+
55+
* GetDeviceCaps function (wingdi.h)
56+
* <https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getdevicecaps>
57+
* DPI-related APIs and registry settings
58+
* <https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dpi-related-apis-and-registry-settings>
59+
* Browse code samples for “dpi”
60+
* <https://docs.microsoft.com/en-us/samples/browse/?redirectedfrom=TechNet-Gallery&terms=dpi>
61+
62+
63+
## Use JNA
64+
65+
This is a fairly clean approach with a couple major downsides. One is adding 3 MB of JARs to each application. That is larger than all of `processing.core`, but you could make an argument that core is already large because of JOGL.
66+
67+
The more serious problems are:
68+
69+
* Unpacking the native libraries for JNA on Windows is notoriously finicky. It can kick off the virus checker with unpredictable results—either refusing to run, or delaying startup by a full 60 seconds, or other confusing behaviors.
70+
71+
* JNA version conflicts are a nightmare. If JNA is used by the sketch, or a library it depends on, you'll have a major headache on your hands.
72+
73+
That said, this approach is possible. Here's working code:
74+
75+
```java
76+
import com.sun.jna.platform.win32.GDI32;
77+
import com.sun.jna.platform.win32.User32;
78+
import com.sun.jna.platform.win32.WinDef;
79+
80+
WinDef.HDC desktopDc = User32.INSTANCE.GetDC(null);
81+
82+
// if we want to add error handling later
83+
//if (desktopDc == null) {
84+
// throw new Win32Exception(Native.getLastError());
85+
86+
//INT horizontalDPI = GetDeviceCaps(desktopDc, LOGPIXELSX);
87+
//INT verticalDPI = GetDeviceCaps(desktopDc, LOGPIXELSY);
88+
int x = GDI32.INSTANCE.GetDeviceCaps(desktopDc, 88);
89+
int y = GDI32.INSTANCE.GetDeviceCaps(desktopDc, 90);
90+
println(x, y);
91+
println("scaling: " + x / 96f);
92+
```
93+
94+
Constants were pulled from <https://github.com/tpn/winsdk-7/blob/master/v7.1A/Include/WinGDI.h>
95+
96+
#define LOGPIXELSX 88 /* Logical pixels/inch in X */
97+
#define LOGPIXELSY 90 /* Logical pixels/inch in Y */
98+
99+
Based in part on [this gist](https://gist.github.com/tresf/00a8ed7c9860e3bd73cebf764a49789f), but rewritten to use the default device rather than first creating a device.
100+
101+
102+
## Use a Registry Key
103+
104+
This would be a simple method to make a single command line call to `reg query` or similar, but was unable to find a suitable property that was reliable enough.
105+
106+
It's also possible that calling `reg query` would kick off User Access Control headaches, but that was not confirmed.
107+
108+
* `reg query` reference
109+
* <https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/reg-query>
110+
111+
* returns `0xC0` (192, or 200%) on my machine even though it's set at 225%
112+
* `Reg Query "HKCU\Control Panel\Desktop" /v LogPixels`
113+
* though looking at this now, if this is gonna return 1x or 2x, it could be an option
114+
115+
* iterating through monitors
116+
* `HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GraphicsDrivers\ScaleFactors\%MonitorID%`
117+
* `HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings\%MonitorID%`
118+
119+
* how to *set* the dpi scale (includes iterating through monitors)
120+
* <https://gist.github.com/itsho/cc4f0c66d3283a6b54582fde31b70a26>
121+
122+
* DPI-related APIs and registry settings
123+
* <https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dpi-related-apis-and-registry-settings?view=windows-11>

0 commit comments

Comments
 (0)