Skip to content

Commit 7530f08

Browse files
olivezaneGlavo
andauthored
修复 KDE Wayland 下 HMCL 不跟随缩放的问题 (#6736)
Co-authored-by: Glavo <zjx001202@gmail.com>
1 parent 15a983d commit 7530f08

2 files changed

Lines changed: 72 additions & 23 deletions

File tree

HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@
5757
import javax.swing.*;
5858
import java.awt.*;
5959
import java.awt.datatransfer.StringSelection;
60+
import java.io.BufferedReader;
6061
import java.io.File;
6162
import java.io.IOException;
63+
import java.io.InputStreamReader;
6264
import java.lang.management.ManagementFactory;
6365
import java.lang.management.MemoryPoolMXBean;
6466
import java.net.CookieHandler;
@@ -68,16 +70,19 @@
6870
import java.nio.file.Paths;
6971
import java.text.DecimalFormat;
7072
import java.util.ArrayList;
73+
import java.util.List;
7174
import java.util.Objects;
7275
import java.util.Optional;
7376
import java.util.concurrent.TimeUnit;
77+
import java.util.stream.Stream;
7478

7579
import static org.jackhuang.hmcl.setting.SettingsManager.settings;
7680
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
7781
import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES;
7882
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
7983
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
8084

85+
/// The main JavaFX application entry point.
8186
public final class Launcher extends Application {
8287
public static final CookieManager COOKIE_MANAGER = new CookieManager();
8388

@@ -396,6 +401,8 @@ private static void setupWindowsAppUserModelID() {
396401
}
397402
}
398403

404+
/// Sets JavaFX VM options before the toolkit starts.
405+
/// On Wayland, the UI scale is auto-detected when the user did not configure one explicitly.
399406
private static void setupJavaFXVMOptions() {
400407
if ("true".equalsIgnoreCase(System.getenv("HMCL_FORCE_GPU"))) {
401408
LOG.info("HMCL_FORCE_GPU: true");
@@ -451,47 +458,85 @@ private static void setupJavaFXVMOptions() {
451458
}
452459
}
453460

461+
float uiScaleLowerBound;
462+
float uiScaleUpperBound;
463+
464+
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
465+
// JavaFX behavior may be abnormal when the DPI scaling factor is too high
466+
uiScaleLowerBound = 0.25f;
467+
uiScaleUpperBound = 4f;
468+
} else {
469+
uiScaleLowerBound = 0.01f;
470+
uiScaleUpperBound = 10f;
471+
}
472+
454473
String uiScale = System.getProperty("hmcl.uiScale", System.getenv("HMCL_UI_SCALE"));
455-
if (uiScale != null) {
456-
uiScale = uiScale.trim();
457474

475+
float scaleValue = Float.NaN;
476+
if (uiScale != null && !(uiScale = uiScale.trim()).isEmpty()) {
458477
LOG.info("HMCL_UI_SCALE: " + uiScale);
459478

460479
try {
461-
float scaleValue;
462480
if (uiScale.endsWith("%")) {
463481
scaleValue = Integer.parseInt(uiScale.substring(0, uiScale.length() - 1)) / 100.0f;
464482
} else if (uiScale.endsWith("dpi") || uiScale.endsWith("DPI")) {
465483
scaleValue = Integer.parseInt(uiScale.substring(0, uiScale.length() - 3)) / 96.0f;
466484
} else {
467485
scaleValue = Float.parseFloat(uiScale);
468486
}
487+
} catch (Throwable e) {
488+
LOG.warning("Invalid UI scale: " + uiScale);
489+
}
490+
} else if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
491+
String xdgSessionType = Objects.requireNonNullElse(System.getenv("XDG_SESSION_TYPE"), "");
492+
String xdgCurrentDesktop = Objects.requireNonNullElse(System.getenv("XDG_CURRENT_DESKTOP"), "");
469493

470-
float lowerBound;
471-
float upperBound;
494+
if ("wayland".equals(xdgSessionType)
495+
&& StringUtils.startsWithIgnoreCase(xdgCurrentDesktop, "KDE")) {
496+
try {
497+
@Nullable Path xrdb = SystemUtils.which("xrdb");
498+
@Nullable String xftDpi = xrdb != null ? SystemUtils.run(List.of(FileUtils.getAbsolutePath(xrdb), "-query"), inputStream -> {
499+
@Nullable String dpiLine;
500+
501+
try (var reader = new InputStreamReader(inputStream, OperatingSystem.NATIVE_CHARSET);
502+
BufferedReader bufferedReader = new BufferedReader(reader);
503+
Stream<String> lines = bufferedReader.lines()) {
504+
dpiLine = lines.filter(line -> line.trim().startsWith("Xft.dpi:"))
505+
.findFirst().orElse(null);
506+
}
472507

473-
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
474-
// JavaFX behavior may be abnormal when the DPI scaling factor is too high
475-
lowerBound = 0.25f;
476-
upperBound = 4f;
477-
} else {
478-
lowerBound = 0.01f;
479-
upperBound = 10f;
480-
}
508+
return dpiLine != null
509+
? dpiLine.substring("Xft.dpi:".length()).trim()
510+
: null;
511+
}, java.time.Duration.ofSeconds(1)) : null;
512+
513+
if (xftDpi != null) {
514+
float dpiValue = Float.parseFloat(xftDpi);
515+
float scale = dpiValue / 96f;
481516

482-
if (scaleValue >= lowerBound && scaleValue <= upperBound) {
483-
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
484-
System.getProperties().putIfAbsent("glass.win.uiScale", uiScale);
485-
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
486-
LOG.warning("macOS does not support setting UI scale, so it will be ignored");
487-
} else {
488-
System.getProperties().putIfAbsent("glass.gtk.uiScale", uiScale);
517+
if (scale > 1 && scale <= 10) {
518+
LOG.info("Detected Xft.dpi: %s (%.1fx)".formatted(dpiValue, scale));
519+
scaleValue = scale;
520+
uiScale = Float.toString(scale);
521+
}
489522
}
523+
} catch (Exception e) {
524+
LOG.warning("Failed to read Xft.dpi from xrdb", e);
525+
}
526+
}
527+
}
528+
529+
if (Float.isFinite(scaleValue)) {
530+
if (scaleValue >= uiScaleLowerBound && scaleValue <= uiScaleUpperBound) {
531+
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
532+
System.getProperties().putIfAbsent("glass.win.uiScale", uiScale);
533+
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
534+
LOG.warning("macOS does not support setting UI scale, so it will be ignored");
490535
} else {
491-
LOG.warning("UI scale out of range: " + uiScale);
536+
System.getProperties().putIfAbsent("glass.gtk.uiScale", uiScale);
492537
}
493-
} catch (Throwable e) {
494-
LOG.warning("Invalid UI scale: " + uiScale);
538+
} else {
539+
LOG.warning("UI scale out of range: " + uiScale);
495540
}
496541
}
497542
}

HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ public static boolean endsWithAny(String str, Collection<String> suffixes) {
280280
return suffixes.stream().anyMatch(str::endsWith);
281281
}
282282

283+
public static boolean startsWithIgnoreCase(String str, String prefix) {
284+
return str.regionMatches(true, 0, prefix, 0, prefix.length());
285+
}
286+
283287
public static Predicate<@Nullable String> compileQuery(String queryString) throws PatternSyntaxException {
284288
Predicate<@Nullable String> predicate;
285289
if (queryString.startsWith("regex:")) {

0 commit comments

Comments
 (0)