|
| 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