Fix scaling issues on Linux

This commit is contained in:
RuiFPB committed 2026-09-25 00:38:28 +01:00
1 parent 87217afa6e
commit f152ed9df5
4 files changed
+119 -26

No files matched your search

@@ -23,6 +23,7 @@
package com.loomcom.symon; package com.loomcom.symon;
import com.formdev.flatlaf.util.UIScale;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.*; import com.loomcom.symon.exceptions.*;
import com.loomcom.symon.machines.Machine; import com.loomcom.symon.machines.Machine;
@@ -53,7 +54,7 @@ public class Simulator {
// UI constants // UI constants
private static final int DEFAULT_FONT_SIZE = 12; private static final int DEFAULT_FONT_SIZE = 12;
private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE); private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, UIScale.scale(DEFAULT_FONT_SIZE));
private static final int CONSOLE_BORDER_WIDTH = 10; private static final int CONSOLE_BORDER_WIDTH = 10;
// Clock periods, in NS, for each speed. 0MHz, 1MHz, 2MHz, 3MHz, 4MHz, 5MHz, 6MHz, 7MHz, 8MHz. // Clock periods, in NS, for each speed. 0MHz, 1MHz, 2MHz, 3MHz, 4MHz, 5MHz, 6MHz, 7MHz, 8MHz.
@@ -23,6 +23,7 @@
package com.loomcom.symon.ui; package com.loomcom.symon.ui;
import com.formdev.flatlaf.util.UIScale;
import com.loomcom.symon.Cpu; import com.loomcom.symon.Cpu;
import com.loomcom.symon.CpuState; import com.loomcom.symon.CpuState;
import com.loomcom.symon.machines.Machine; import com.loomcom.symon.machines.Machine;
@@ -34,6 +35,7 @@ import javax.swing.plaf.metal.MetalTextFieldUI;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.Objects;
/** /**
* UI component that displays the current state of the simulated CPU. * UI component that displays the current state of the simulated CPU.
@@ -74,7 +76,7 @@ public class StatusPanel extends JPanel {
private static final int EMPTY_BORDER = 10; private static final int EMPTY_BORDER = 10;
private static final Border LABEL_BORDER = BorderFactory.createEmptyBorder(0, 5, 0, 0); private static final Border LABEL_BORDER = BorderFactory.createEmptyBorder(0, 5, 0, 0);
private static final Font LABEL_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 12); private static final Font LABEL_FONT = new Font(Font.SANS_SERIF, Font.BOLD, UIScale.scale(12));
private static final Dimension LARGE_TEXT_FIELD_SIZE = new Dimension(134, 22); private static final Dimension LARGE_TEXT_FIELD_SIZE = new Dimension(134, 22);
private static final Dimension SMALL_TEXT_FIELD_SIZE = new Dimension(65, 22); private static final Dimension SMALL_TEXT_FIELD_SIZE = new Dimension(65, 22);
@@ -99,20 +101,20 @@ public class StatusPanel extends JPanel {
JPanel statusFlagsPanel = new JPanel(); JPanel statusFlagsPanel = new JPanel();
statusFlagsPanel.setAlignmentX(LEFT_ALIGNMENT); statusFlagsPanel.setAlignmentX(LEFT_ALIGNMENT);
carryOn = new ImageIcon(this.getClass().getResource("/C_on.png")); carryOn = loadScaledIcon("/C_on.png");
carryOff = new ImageIcon(this.getClass().getResource("/C_off.png")); carryOff = loadScaledIcon("/C_off.png");
zeroOn = new ImageIcon(this.getClass().getResource("/Z_on.png")); zeroOn = loadScaledIcon("/Z_on.png");
zeroOff = new ImageIcon(this.getClass().getResource("/Z_off.png")); zeroOff = loadScaledIcon("/Z_off.png");
irqOn = new ImageIcon(this.getClass().getResource("/I_on.png")); irqOn = loadScaledIcon("/I_on.png");
irqOff = new ImageIcon(this.getClass().getResource("/I_off.png")); irqOff = loadScaledIcon("/I_off.png");
decimalOn = new ImageIcon(this.getClass().getResource("/D_on.png")); decimalOn = loadScaledIcon("/D_on.png");
decimalOff = new ImageIcon(this.getClass().getResource("/D_off.png")); decimalOff = loadScaledIcon("/D_off.png");
breakOn = new ImageIcon(this.getClass().getResource("/B_on.png")); breakOn = loadScaledIcon("/B_on.png");
breakOff = new ImageIcon(this.getClass().getResource("/B_off.png")); breakOff = loadScaledIcon("/B_off.png");
overflowOn = new ImageIcon(this.getClass().getResource("/O_on.png")); overflowOn = loadScaledIcon("/O_on.png");
overflowOff = new ImageIcon(this.getClass().getResource("/O_off.png")); overflowOff = loadScaledIcon("/O_off.png");
negativeOn = new ImageIcon(this.getClass().getResource("/N_on.png")); negativeOn = loadScaledIcon("/N_on.png");
negativeOff = new ImageIcon(this.getClass().getResource("/N_off.png")); negativeOff = loadScaledIcon("/N_off.png");
// Initialize all to off // Initialize all to off
carryFlagLabel = new JLabel(carryOff, JLabel.CENTER); carryFlagLabel = new JLabel(carryOff, JLabel.CENTER);
@@ -387,9 +389,11 @@ public class StatusPanel extends JPanel {
JTextField textField = new JTextField(""); JTextField textField = new JTextField("");
textField.setAlignmentX(LEFT_ALIGNMENT); textField.setAlignmentX(LEFT_ALIGNMENT);
textField.setEditable(editable); textField.setEditable(editable);
textField.setMinimumSize(size);
textField.setMaximumSize(size); Dimension scaleSize = UIScale.scale(size);
textField.setPreferredSize(size); textField.setMinimumSize(scaleSize);
textField.setMaximumSize(scaleSize);
textField.setPreferredSize(scaleSize);
return textField; return textField;
} }
@@ -398,4 +402,14 @@ public class StatusPanel extends JPanel {
String val = source.getText().replaceAll("[^0-9a-fA-F]", ""); String val = source.getText().replaceAll("[^0-9a-fA-F]", "");
return Integer.parseInt(val, 16); return Integer.parseInt(val, 16);
} }
private ImageIcon loadScaledIcon(String resourcePath) {
ImageIcon icon = new ImageIcon(Objects.requireNonNull(this.getClass().getResource(resourcePath)));
int scaledWidth = UIScale.scale(icon.getIconWidth());
int scaledHeight = UIScale.scale(icon.getIconHeight());
Image scaleImage = icon.getImage().getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
return new ImageIcon(scaleImage);
}
} }
+31 -7
View File
@@ -39,10 +39,13 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.jthemedetecor.OsThemeDetector; import com.jthemedetecor.OsThemeDetector;
import com.loomcom.symon.machines.BenEaterMachine;
import com.loomcom.symon.machines.MulticompMachine; import java.awt.*;
import com.loomcom.symon.machines.SimpleMachine; import java.beans.PropertyChangeListener;
import com.loomcom.symon.machines.SymonMachine; import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public final class Ui { public final class Ui {
private final static Logger logger = LoggerFactory.getLogger(Ui.class.getName()); private final static Logger logger = LoggerFactory.getLogger(Ui.class.getName());
@@ -51,10 +54,14 @@ public final class Ui {
// Location of properties resources for app-specific UI defaults: // Location of properties resources for app-specific UI defaults:
FlatLaf.registerCustomDefaultsSource("com.loomcom.symon.themes"); FlatLaf.registerCustomDefaultsSource("com.loomcom.symon.themes");
logger.debug("AWT Toolkit class: {}", Toolkit.getDefaultToolkit().getClass().getName());
// Enable automatic scaling, based on system DPI settings: // Enable automatic scaling, based on system DPI settings:
System.setProperty("sun.java2d.uiScale.enabled", "true"); //System.setProperty("sun.java2d.uiScale.enabled", "true");
System.setProperty("sun.java2d.uiScale", "1x"); //System.setProperty("sun.java2d.uiScale", "1x");
System.setProperty("flatlaf.uiScale", "1x"); //System.setProperty("flatlaf.uiScale", "1x");
applyDetectedUiScale();
// OS-specific tweaking: // OS-specific tweaking:
@@ -124,4 +131,21 @@ public final class Ui {
return theme; return theme;
} }
private static void applyDetectedUiScale() {
Double rawScale = X11ResourceReader.readXftDpiScale(); // ex: 1.5, 2.0, 2.5...
if (rawScale != null && rawScale > 1.0) {
// flatlaf.uiScale aceita frações — mantém a precisão original
System.setProperty("flatlaf.uiScale", rawScale + "x");
// sun.java2d.uiScale só aceita inteiros — arredonda para cima
int javaScale = (int) Math.ceil(rawScale);
//System.setProperty("sun.java2d.uiScale", String.valueOf(javaScale));
logger.debug("Detected scale {}x, applying flatlaf.uiScale={}x, sun.java2d.uiScale={}x",
rawScale, rawScale, javaScale);
} else {
logger.debug("No X11 HiDPI scale detected, using default.");
}
}
} }
@@ -0,0 +1,54 @@
package com.loomcom.symon.ui;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class X11ResourceReader {
private final static Logger logger = LoggerFactory.getLogger(X11ResourceReader.class.getName());
public interface X11 extends Library {
X11 INSTANCE = Native.load("X11", X11.class);
Pointer XOpenDisplay(String display);
Pointer XResourceManagerString(Pointer display);
void XCloseDisplay(Pointer display);
}
public static Double readXftDpiScale() {
Pointer display = null;
try {
display = X11.INSTANCE.XOpenDisplay(null);
if (display == null) {
return null; // não conseguimos ligar ao X server (ex: Wayland puro sem XWayland)
}
Pointer resourceString = X11.INSTANCE.XResourceManagerString(display);
if (resourceString == null) {
return null;
}
String resources = resourceString.getString(0);
for (String line : resources.split("\n")) {
if (line.startsWith("Xft.dpi:")) {
int dpi = Integer.parseInt(line.split(":")[1].trim());
return Math.round((dpi / 96.0) * 4) / 4.0;
}
}
return null;
} catch (UnsatisfiedLinkError | Exception e) {
logger.debug("Could not read X11 resource manager (libX11 unavailable?)", e);
return null;
} finally {
if (display != null) {
X11.INSTANCE.XCloseDisplay(display);
}
}
}
}