adoptium / adoptium-support

For end-user problems reported with our binary distributions
Apache License 2.0
45 stars 15 forks source link

HiDPI position handling on Ubuntu 18.04 #172

Closed tresf closed 4 years ago

tresf commented 5 years ago

Platform: Ubuntu 18.04 + HiDPI monitor

Architecture: x86-64

Related: SR 3-17051327481 (Private)

Description Screen coordinates are not properly translatable using OpenJDK11 and Ubuntu 18.04 on HiDPI. This appears to be caused by the need for scaling information when doing absolute window positioning, but the lack of ability of the Java framework to provide this information.

Use-Case Use coordinates from a web browser (JavaScript) to correctly position a Java element (e.g. on the same screen).

Known Caveats IE11 and Edge 44 do not expose coordinates. Chrome, Firefox, Opera and Edge 76 do.

Workaround Current workaround is to use setLocationRelativeTo(null), which will center it on the main display.

Test results:

jdk8 jdk11 Details
macOS ✅* ✅* * = Workaround available through platform detection. Only works (JDK8 and JDK11) if hard-code dpiScale to 1.
Windows ✅* * = Workaround available through Java version detection. JDK11 requires dpiScale to be 1, JDK8 works with a calculated dpiScale.
Linux ⛔️* * = No known workaround available. JDK11 requires dpiScale for this platform for correct positioning, but JDK always returns 1, so it positions it too-high and too-left on HiDPI displays

JavaScript

// Get screen coordinates from JavaScript (useful for positioning something on the same screen that someone is working from)
[(screen.availWidth || screen.width) / 2 + (screen.left || screen.availLeft), (screen.availHeight || screen.height) / 2 + (screen.top || screen.availTop)];

Java

// Position a Java component centered on the same screen as the web browser
Point chromePoint = new Point(2880,520); // <--- ####### Replace with values from JS
JPopupMenu menu = new JPopupMenu();
JMenuItem item = new JMenuItem("Hello World");
item.addActionListener(e -> System.exit(0));
menu.add(item);
menu.pack();

double dpiScale = Toolkit.getDefaultToolkit().getScreenResolution() / 96;
Point javaPoint = new Point((int)(chromePoint.getX() * dpiScale), (int)(chromePoint.getY() * dpiScale));

// Account for own size when centering
javaPoint.translate((int)(-menu.getWidth() / 2.0), (int)(-menu.getHeight() / 2.0));
System.out.println("Point provided by Chrome: " + chromePoint.x + "," + chromePoint.y);
System.out.println("Calculated Java component centered at: " + javaPoint.x + "," + javaPoint.y);

menu.setLocation(javaPoint);
menu.setVisible(true); // <--- This should be centered on the same monitor as the web browser
karianna commented 5 years ago

I've set up the code example on my local Mac OS X (Retina) and can confirm that it works as expected.

Next step is to run an Ubuntu 18.04 under Virtual Box and reproduce the error

karianna commented 5 years ago

Hmm or maybe not - looks like Mac OS X new protections stop VirtualBox from executing - I'll find a new machine to work on.

karianna commented 5 years ago

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8160194 - might be relevant

tresf commented 5 years ago

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8160194 - might be relevant

Agreed but the title is misleading... "User-defined scale" is actually set by default to 200% on Retina on boot (Live CD) or install, so this is broken out-of-the box for HiDPI. For a bug report to call this "User-defined" implies it's set by preference, but in the case of a 4K display, the system decides a sane default for you.

tresf commented 5 years ago

looks like Mac OS X new protections stop VirtualBox from executing - I'll find a new machine to work on.

Offtopic, but Parallels can run it, and you'll never go back to VirtualBox on MacOS once you use it. ❤️ They offer a 14-day trial.

tresf commented 5 years ago

With help from an Eclipse bug report, I've written a small Python program to get the scale factor from Ubuntu...

On Ubuntu 18.04, this gets the scale factor set from System Preferences. 100% returns 1, 200% returns 2.

The bug does warn about compatibility:

The gtk patch uses gdk_monitor_get_scale_factor for GTK >= 3.22 and gdk_screen_get_monitor_scale_factor for older versions [...]

# sudo apt-get install python-gi python3-pyside
from gi.repository import Gdk

display = Gdk.Display.get_default()
monitor = display.get_primary_monitor()
scale_factor = monitor.get_scale_factor()

print scale_factor

I will attempt to produce a JNA (NOT JNI) binding that is capable of this as a workaround.

karianna commented 5 years ago

@tresf Oh nice! I was still bumbling down the JNI route - JNA - interesting idea.

tresf commented 5 years ago

JNA - interesting idea.

The project we use has JNA as a dependency for other components, so it would serve as a stop-gap until the JDK offers it.

tresf commented 5 years ago

FYI, a downstream workaround using JNA is available here, for anyone that needs it: https://github.com/qzind/tray/commit/7ff0d043117a36218c43e005c6d777123245d08e#diff-1aeef540797c8551c39d0e5bcd12df43

This code is intended to cover compatibility with Gtk2/Gtk3 for detecting the scaling factor.

Worth noting, some other scaling issues with the Java interface are present suggesting that the underlying issue may be that Linux should do this all under the covers.

For example:

... so the issue may be less of the ability to grab scale, but the poor handling of sizing in general on Linux. The scale factor may be the symptom, not the problem.

aahlenst commented 4 years ago

@tresf What should we do with this issue? If this is for informational purposes, a blog post would be more appropriate. If it should result in an enhancement request at OpenJDK, you know the drill 😉

tresf commented 4 years ago

@aahlenst it should probably be tracked upstream and escalated to our vendor. I may choose to do that in the future and reopen but for now I'll close it out since it's an edge-case issue and a viable workaround exists.