netbeans icon indicating copy to clipboard operation
netbeans copied to clipboard

Auto-detect HiDPI fractional scaling settings on Linux

Open basil opened this issue 6 months ago • 8 comments

The current logic to auto-detect HiDPI settings on Linux from #3113 only handles the 2x case. This PR expands it to handle fractional scaling, as described in this thread.

Taking https://github.com/apache/netbeans/issues/7780#issuecomment-2657192464 into consideration, the launcher does not touch the scaling settings if one or both of the GDK_SCALE and J2D_UISCALE environment variables is set.

This PR was tested with both Java 17 and 21 (Eclipse Temurin) on Fedora 42 with fractional scaling enabled in KDE Plasma. Before this PR, no scaling was applied; after this PR, fractional scaling is applied and the following is logged:

Detected 158 DPI in Xft.dpi setting; using explicit setting for Java UI scaling (-J-Dsun.java2d.uiScale=2) and FlatLaf UI scaling (-J-Dflatlaf.uiScale=0.822917)

PR approval and merge checklist:

  1. [ ] Was this PR correctly labeled, did the right tests run? When did they run?
  2. [x] Is this PR squashed?
  3. [x] Are author name / email address correct? Are co-authors correctly listed? Do the commit messages need updates?
  4. [ ] Does the PR title and description still fit after the Nth iteration? Is the description sufficient to appear in the release notes?

If this PR targets the delivery branch: don't merge. (full wiki article)

basil avatar Jun 15 '25 18:06 basil

Thanks for this PR! So, to understand correctly, this handles fractional scaling (e.g. 150%) by using the 2x scaling at the AWT level (sun.java2d.uiScale), but then using FlatLAF's own scaling method (flatlaf.uiScale) to scale many, though not all, UI components down again by a bit less?

The downside is that the UI still looks somewhat wonky, because only some components observe flatlaf.uiScale.

On MacOS, fractional scalings are handled by the OS by scaling everything at 2x and then scaling down and anti-aliasing the entire image of the window, at the graphics card level. That tends to look much nicer. Is there no equivalent of that mechanism on GNOME/X that we can tap into?

eirikbakke avatar Jun 16 '25 20:06 eirikbakke

The downside is that the UI still looks somewhat wonky

It does not look wonky to me with this PR.

Is there no equivalent of that mechanism on GNOME/X that we can tap into?

I don't know.

basil avatar Jun 16 '25 20:06 basil

I emailed a GNOME developer, I will see if there's a way to do the down-scaling step at the compositing level instead. That would also have the advantage of working with multiple monitors, even if they have different desired scaling levels.

eirikbakke avatar Jun 16 '25 20:06 eirikbakke

I am using KDE Plasma, not GNOME. I don't have any interest in expanding the scope of this PR as described. This PR is a net improvement over the status quo, which doesn't support fractional scaling at all. It follows a method that has been tested and reported as successful by myself and two other users. I'm not willing to try implementing and/or testing a different approach.

basil avatar Jun 16 '25 20:06 basil

Sorry, I suspect the relevant settings are actually at the Wayland level, not specific to GNOME or KDE... I just suspect there exist people who might be able to immediately tell me if the compositing approach would work, and how to test it.

eirikbakke avatar Jun 16 '25 21:06 eirikbakke

I just suspect there exist people who might be able to immediately tell me if the compositing approach would work, and how to test it.

If such an approach exists, I think that would be great information to share with anyone who is willing to try implementing and/or testing a different approach. As mentioned above, I am not willing to do so.

basil avatar Jun 16 '25 21:06 basil

Sorry, I suspect the relevant settings are actually at the Wayland level,

@eirikbakke well, also XWayland level, which I believe has different configuration options for handling scaling too - automatic and application level? Given the range of parameters, and need to handle this right across X11 and XWayland, with potentially different settings, I have concerns about putting too much logic into the launchers themselves. See also #6185 etc.

neilcsmith-net avatar Jun 19 '25 09:06 neilcsmith-net

I have concerns about putting too much logic into the launchers themselves.

Perhaps, if fractional DPI is detected, we could just print a notice with a link to a Wiki page or such, listing options that have been tried and are known to work for different setups?

also XWayland level, which I believe has different configuration options for handling scaling too - automatic and application level?

Yes, I was in the slow process of learning more about this. OpenJDK is indeed XWayland, not a full wayland app (yet).

eirikbakke avatar Jun 19 '25 16:06 eirikbakke

Any update on this PR?

basil avatar Jul 23 '25 18:07 basil

What is the next action item for this PR?

basil avatar Aug 12 '25 03:08 basil

I had a chat with the developer who authored https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3567 . Excerpt:

My question:

Java apps such as NetBeans can support a 2x HiDPI scaling quite easily on Linux, due to underlying support in the OpenJDK platform (which currently runs through Xwayland, as I understand it). Fractional scaling factors, however, are not supported directly by OpenJDK. However, fractional scaling could be supported quite easily if there was a way to tell Mutter to scale a given application's windows down by a factor of less than one. Then OpenJDK could render its windows at 2x scaling and mutter could scale the resulting bitmap down by e.g. a factor of 0.75 to get a final, sharp scaling factor of 2*0.75 = 150%. This is how fractional scaling works on MacOS, and it works very well there. This technique also works with multiple monitors, which can have different compositor-applied scale-down factors.

Is there a way to achieve something like this with Mutter and Xwayland? E.g. allow an individual application to declare that it will draw itself at 2x scaling, and then have mutter scale by e.g. 0.75 instead of 1.5 whenever the window is on a monitor that has a desired scale factor of 150%?

Jonas Dreßler replied to me:

Happy to let you know that in fact the MacOS way is exactly how fractional scaling works on Mutter as well :)

However, I was not yet able to make this work.

eirikbakke avatar Aug 12 '25 11:08 eirikbakke

However, I was not yet able to make this work.

@eirikbakke Thanks for sharing your inability to make this work. I'm still not clear on the next action item for the merge and release of this PR, which does work and contains no known regressions.

Before this PR, everything on my system (which is set to scale to 165%) is way too small:

before

Compare that to Kate, which just works:

kate

With the changes from this PR, NetBeans just works too:

after

I haven't had any issues using this launcher for the past several months. With this PR, I see the following log message as expected:

Detected 158 DPI in Xft.dpi setting; using explicit setting for Java UI scaling (-J-Dsun.java2d.uiScale=2) and FlatLaf UI scaling (-J-Dflatlaf.uiScale=0.822917)

basil avatar Aug 12 '25 18:08 basil

The potential problem with the flatlaf.uiScale method is that it will scale some UI items but not others.

(Sorry, I'm out of time to work on this at the moment... if others want to pick up the thread, feel free.)

eirikbakke avatar Aug 12 '25 19:08 eirikbakke

The potential problem with the flatlaf.uiScale method is that it will scale some UI items but not others.

Is a potential problem a reason not to merge and release this PR? I could see an actual problem being such a reason, but I don't see why a potential problem should delay the merge and release of this PR.

If there is an actual regression, I am happy to fix it.

basil avatar Aug 12 '25 19:08 basil

I can report that on GNOME 48 (on e.g. Ubuntu 25.04), fractional scaling works out-of-the-box with NetBeans 26 and no changes to the launcher script. This is due to the xwayland-native-scaling feature, which was introduced in GNOME 47. NetBeans paints itself at 2x, and Mutter scales it down by 25%, for a final factor of 150%.

NetBeans 26 at 150% scaling on GNOME 46 (blurry text): scale 1 5 without xwayland-native-scaling

NetBeans 26 at 150% scaling on GNOME 48 (sharp text): scale1 5 with xwayland-native-scaling

If you could find an equivalent feature in KDE/KWin, or a way to force KWin to scale a single application down by a given factor, it would be preferable to tweaking flatlaf.uiScale.

EDIT: Adding a screenshot with GDK_SCALE=2 and flatlaf.uiScale=0.75 (and no scaling on the desktop environment) for comparison. The flatlaf.uiScale parameter is not actually handled anywhere in the NetBeans GUI code; it is only a FlatLAF feature. Thus some components are affected by it, and others not.

image

eirikbakke avatar Aug 16 '25 16:08 eirikbakke

@eirikbakke The only relevant setting in KDE/KWin (I'm running the latest rolling version of Fedora, Fedora 42) is "Legacy applications (X11): Apply scaling themselves" or "Scaled by the system." With "Scaled by the system" NetBeans displays at the correct size but it is blurry, and all other X11 applications on my system also look blurry (including my web browser, Brave). Sadly, the KDE/KWin version of this feature isn't implemented as well as Mutter's, and can't be configured per-application. With "Apply scaling themselves" (the default), NetBeans only ever appears as 100% or 200% (no fractional values: GDK_SCALE, J2D_UISCALE, and sun.java2d.uiScale only have an effect when set to integers, and GDK_DPI_SCALE has no effect). I won't break my web browser for NetBeans (which I am very close to abandoning entirely), so it needs to work correctly with the default "Apply scaling themselves" option.

If you could find an equivalent feature in KDE/KWin, or a way to force KWin to scale a single application down by a given factor, it would be preferable to tweaking flatlaf.uiScale.

There is no directly equivalent feature in KDE/KWin, and no way to force KWin to scale a single application down by a given factor. Adding an equivalent feature to KDE/KWin would be more of a large undertaking than I can offer. As such, I think the only practical solution today for KDE/KWin users running with "Apply scaling themselves" is to tweak flatlaf.uiScale.

Fractional scaling users of KDE/KWin / "Apply scaling themselves" (the default on Fedora 42 KDE Edition), as well as those running Mutter without the xwayland-native-scaling feature you mention above (which only came out very recently), are currently getting a bad out of box experience (OOBE) with NetBeans running at obviously the wrong scale factor. Tweaking flatlaf.uiScale in those situations might not be ideal, but it is an improvement over the status quo (if it can be done without regressions) and does not block a more robust solution later, should Java improve its Wayland story or should KDE/KWin improve its X11 story.

basil avatar Aug 16 '25 18:08 basil

Thanks for checking!

I think for GNOME we should probably prefer to use the official fractional scaling support. Users with HiDPI displays may have the incentive to upgrade to the latest version in any case. What will this PR's version of the launcher do on GNOME?

For KDE I can see the benefit of the flatlaf.uiScale trick, even if it's imperfect. I just hope we won't start littering the NetBeans codebase with "adjust size for flatlaf.uiScale" patches.

I do share Neil's concern that the launcher is getting a bit complex at this point. It's hard to test the various settings and configurations thoroughly.

An alternative to actually changing the settings automatically is to print suggestions on the terminal. E.g.

  • "We see you are running fractional scaling on KDE; consider setting flatlaf.uiScale in netbeans.conf."
  • "We see you are running fractional scaling on an older GNOME version; consider upgrading to GNOME 48."

Overall I'm somewhat neutral on the pros and cons of the patch. I'll let others chime in...

eirikbakke avatar Aug 16 '25 18:08 eirikbakke

I think for GNOME we should probably prefer to use the official fractional scaling support. Users with HiDPI displays may have the incentive to upgrade to the latest version in any case.

I have changed this PR so that it does not set flatlaf.uiScale when running on GNOME.

What will this PR's version of the launcher do on GNOME?

This PR's version of the launcher, as of the latest revision, will only replace GDK_SCALE=2 with -Dsun.java2d.uiScale=2 on GNOME, but it will not set -Dflatlaf.uiScale on GNOME. This should result in no change in existing behavior. Note that setting GDK_SCALE=2 or -Dsun.java2d.uiScale=2 on GNOME 48 with xwayland-native-scaling enabled may (?) be inappropriate. If so, that potential problem would have existed before this PR and should not be made any better or worse after this PR. Since I don't use GNOME, I am not motivated to work on this potential problem; my motivation is only not to cause any regressions on GNOME.

I just hope we won't start littering the NetBeans codebase with "adjust size for flatlaf.uiScale" patches.

I do not intend to do so myself. This workaround at the launcher level is good enough for me to get a usable application.

I do share Neil's concern that the launcher is getting a bit complex at this point. It's hard to test the various settings and configurations thoroughly.

Agreed, but that is a consequence of the rapidly increasing complexity of the Linux ecosystem as a whole caused by the migration from X11 to Wayland. This migration is as painful for users as it is for developers, but I don't think there is anything NetBeans can do about this.

An alternative to actually changing the settings automatically is to print suggestions on the terminal.

To me, this is a bad user experience (UX). Competing Java IDEs "just work" out of the box, and NetBeans should as well, or at least it should work as well as it can. Right now HiDPI support is obviously broken in my configuration in KDE. I was annoyed that I had to research these obscure parameters, calculate the right numbers to put into them, and then edit my configuration just to get a usable scaling level. I created this PR so that other users would not have to suffer my pain.

"We see you are running fractional scaling on KDE; consider setting flatlaf.uiScale in netbeans.conf."

This PR effectively automates this, which I consider a better UX. I did not enjoy learning about all these scaling parameters, and other users shouldn't have to understand this obscure stuff either.

"We see you are running fractional scaling on an older GNOME version; consider upgrading to GNOME 48."

It would be nice to print this for GNOME users. I am not a GNOME user and have no immediate reason to improve GNOME support, so I will leave this improvement to someone else who has such motivations.

basil avatar Aug 16 '25 20:08 basil

I am not sure if its a good idea to split scaling into two stages handled by two dependencies. There are already enough edge cases with one scale factor (multi screen setup with different resolutions). Platform apps might also choose different LAF, in which case the FlatLaf scaling stage is gone. (e.g VisualVM uses system LAF I believe. A quick test showed that sun.java2d.uiScale seems to be ignored by GTK LAF at least, but there are more plausible combinations)

I am wondering if a guide for KDE users would be sufficient here (probably a combination of --fontsize and the two scaling flags, dependent on the situation). The launcher should also not overwrite user config, e.g some might already set sun.java2d.uiScale via netbeans.conf. Exporting GDK_SCALE is something we should probably stop doing too (https://github.com/apache/netbeans/issues/8243 etc).

Some wayland impls do need a bit longer to mature (my dist can't even switch keyboard layout when wayland is active), there is a good chance that this problem will fix itself over time when more impls are feature complete.

mbien avatar Aug 16 '25 20:08 mbien

I am not sure if its a good idea to split scaling into two stages handled by two dependencies.

It's the best idea that works for me in my current environment. No better working solution has been presented.

I am wondering if a guide for KDE users would be sufficient here (probably a combination of --fontsize and the two scaling flags, dependent on the situation).

That would be a worse UX for the reasons explained in my previous message.

The launcher should also not overwrite user config, e.g some might already set sun.java2d.uiScale via netbeans.conf.

Nor is it in this PR: extra automatic options are added before default options, to allow system property definitions from the configuration file to take precedence.

Exporting GDK_SCALE is something we should probably stop doing too (#8243 etc).

This PR stops exporting GDK_SCALE.

Some wayland impls do need a bit longer to mature (my dist can't even switch keyboard layout when wayland is active), there is a good chance that this problem will fix itself over time when more impls are feature complete.

I would sooner switch to a competing IDE where this works today rather than continue to discuss this with maintainers who clearly do not want to improve the user experience.

basil avatar Aug 16 '25 20:08 basil

Some wayland impls do need a bit longer to mature (my dist can't even switch keyboard layout when wayland is active), there is a good chance that this problem will fix itself over time when more impls are feature complete.

I would sooner switch to a competing IDE where this works today rather than continue to discuss this with maintainers who clearly do not want to improve the user experience.

This might not calm you, but let me tell you a story about an IDE that decided to work around a problem, that it should not need fix. The developers of that IDE decided that instead of fixing the issue at the root (in that case the JDK) they invented a mechanism to replace a core subsystem with an "fixed" version.

Now, years later the problem in the JDK remained unfixed, but NetBeans (said IDE) can use its mechanism anymore because the hack into the JVM is not possible anymore.

The fix in the IDE only masked the problem and did that often badly (#3962 was reported 3 years ago). The masking though was good enough to stop work on fixing the real problem.

The fix in the IDE is a big chunk of code, trying to fix different things and now this has to be dismantled. I took months to get the fix into the JDK, but now the fix is at the right place and the whole "workaround" should never have been there. The maintenance cost of the added code is still there though and somebody has to step up to clean the mess.

So yes, adding complexity for something that might be obsolete in a year has to be considered carefully. And if you look into the discussion #7051 you can see that fixing things at the roots can be hard, but it is the better option.

matthiasblaesing avatar Aug 16 '25 21:08 matthiasblaesing

@matthiasblaesing I've spent my entire career working on technical debt reduction and elimination, including completing decades-unfinished migrations, so I know very well that workarounds carry a long-term cost and often disincentivize work on the root cause. Your story does not give me any new insights that I did not have already. NetBeans is, of course, free to accept or reject any proposal, and I am equally free to find a competing product that meets my current needs.

basil avatar Aug 16 '25 21:08 basil

It's the best idea that works for me in my current environment.

works-for-me is a good starting point but the hard part is to make sure that it keeps working for others too (platform apps etc). We try to avoid merging things to master and stabilize them later - ideally problems are found and solved before that as part of the review.

Nor is it in this PR: extra automatic options are added before default options

Its more than one option and they are not independent. How would the user know that two options would have to be overwritten when a certain screen is plugged in because of launcher magic? The risk of adding too much magic is that it becomes no longer predictable from the outside. There are solutions for that of course, another flag could be added which disables auto configuration - or things are kept simple.

This PR stops exporting GDK_SCALE.

I saw. Thats why I mentioned this would be something we should consider doing for NB 28 assuming form designer previews etc still work as expected. There might have been a reason why it was originally implemented via the exported env var instead of a JVM property.

I would sooner switch to a competing IDE where this works today rather than continue to discuss this with maintainers who clearly do not want to improve the user experience.

:)

originally I wanted to stay out of this discussion since I had the feeling this will end in drama. But I have to say that someone announcing to switch IDEs via a PR and mailing list is a super weird thing to do esp when dealing with free/community maintained projects. (its also extra funny to someone like me who uses multiple IDEs and switches based on project/customer, sometimes several times a day)

switching to zed as protest

mbien avatar Aug 16 '25 22:08 mbien

But I have to say that someone announcing to switch IDEs via a PR and mailing list is a super weird thing to do

@mbien Why is it weird?

its also extra funny to someone like me who uses multiple IDEs and switches based on project/customer, sometimes several times a day

Why is it funny?

basil avatar Aug 16 '25 22:08 basil

I had it in mind to take another look at this PR when I got back from holiday. It seems to have warmed up a bit! :smile:

For the record, I'm -1 to this approach. There is a valid issue here. The "fix" is wrong, incomplete and in the wrong place.

In my opinion we should probably be looking to move all of the existing scaling and font settings out of the launcher itself anyway, not adding to them.

Also, comments like -

I'm not willing to try implementing and/or testing a different approach.

are not helpful. Drive-by, take-it-or-leave-it, PRs are often not useful, even damaging, to open-source projects. We have a handful of volunteer maintainers looking after the UI aspects of the IDE and platform for hundreds of thousands of users. This affects a narrow subset of those users. A contribution that fixed this issue in a better way with less risk would be welcomed.

neilcsmith-net avatar Aug 17 '25 10:08 neilcsmith-net