diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java index 92d0547..f16f20a 100644 --- a/src/main/java/com/loomcom/symon/Simulator.java +++ b/src/main/java/com/loomcom/symon/Simulator.java @@ -23,6 +23,7 @@ package com.loomcom.symon; +import com.formdev.flatlaf.util.UIScale; import com.loomcom.symon.devices.Memory; import com.loomcom.symon.exceptions.*; import com.loomcom.symon.machines.Machine; @@ -53,7 +54,7 @@ public class Simulator { // UI constants 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; // Clock periods, in NS, for each speed. 0MHz, 1MHz, 2MHz, 3MHz, 4MHz, 5MHz, 6MHz, 7MHz, 8MHz. diff --git a/src/main/java/com/loomcom/symon/ui/StatusPanel.java b/src/main/java/com/loomcom/symon/ui/StatusPanel.java index b8878b6..da09367 100644 --- a/src/main/java/com/loomcom/symon/ui/StatusPanel.java +++ b/src/main/java/com/loomcom/symon/ui/StatusPanel.java @@ -23,6 +23,7 @@ package com.loomcom.symon.ui; +import com.formdev.flatlaf.util.UIScale; import com.loomcom.symon.Cpu; import com.loomcom.symon.CpuState; import com.loomcom.symon.machines.Machine; @@ -34,6 +35,7 @@ import javax.swing.plaf.metal.MetalTextFieldUI; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Objects; /** * 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 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 SMALL_TEXT_FIELD_SIZE = new Dimension(65, 22); @@ -99,20 +101,20 @@ public class StatusPanel extends JPanel { JPanel statusFlagsPanel = new JPanel(); statusFlagsPanel.setAlignmentX(LEFT_ALIGNMENT); - carryOn = new ImageIcon(this.getClass().getResource("/C_on.png")); - carryOff = new ImageIcon(this.getClass().getResource("/C_off.png")); - zeroOn = new ImageIcon(this.getClass().getResource("/Z_on.png")); - zeroOff = new ImageIcon(this.getClass().getResource("/Z_off.png")); - irqOn = new ImageIcon(this.getClass().getResource("/I_on.png")); - irqOff = new ImageIcon(this.getClass().getResource("/I_off.png")); - decimalOn = new ImageIcon(this.getClass().getResource("/D_on.png")); - decimalOff = new ImageIcon(this.getClass().getResource("/D_off.png")); - breakOn = new ImageIcon(this.getClass().getResource("/B_on.png")); - breakOff = new ImageIcon(this.getClass().getResource("/B_off.png")); - overflowOn = new ImageIcon(this.getClass().getResource("/O_on.png")); - overflowOff = new ImageIcon(this.getClass().getResource("/O_off.png")); - negativeOn = new ImageIcon(this.getClass().getResource("/N_on.png")); - negativeOff = new ImageIcon(this.getClass().getResource("/N_off.png")); + carryOn = loadScaledIcon("/C_on.png"); + carryOff = loadScaledIcon("/C_off.png"); + zeroOn = loadScaledIcon("/Z_on.png"); + zeroOff = loadScaledIcon("/Z_off.png"); + irqOn = loadScaledIcon("/I_on.png"); + irqOff = loadScaledIcon("/I_off.png"); + decimalOn = loadScaledIcon("/D_on.png"); + decimalOff = loadScaledIcon("/D_off.png"); + breakOn = loadScaledIcon("/B_on.png"); + breakOff = loadScaledIcon("/B_off.png"); + overflowOn = loadScaledIcon("/O_on.png"); + overflowOff = loadScaledIcon("/O_off.png"); + negativeOn = loadScaledIcon("/N_on.png"); + negativeOff = loadScaledIcon("/N_off.png"); // Initialize all to off carryFlagLabel = new JLabel(carryOff, JLabel.CENTER); @@ -387,9 +389,11 @@ public class StatusPanel extends JPanel { JTextField textField = new JTextField(""); textField.setAlignmentX(LEFT_ALIGNMENT); textField.setEditable(editable); - textField.setMinimumSize(size); - textField.setMaximumSize(size); - textField.setPreferredSize(size); + + Dimension scaleSize = UIScale.scale(size); + textField.setMinimumSize(scaleSize); + textField.setMaximumSize(scaleSize); + textField.setPreferredSize(scaleSize); return textField; } @@ -398,4 +402,14 @@ public class StatusPanel extends JPanel { String val = source.getText().replaceAll("[^0-9a-fA-F]", ""); 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); + } } diff --git a/src/main/java/com/loomcom/symon/ui/Ui.java b/src/main/java/com/loomcom/symon/ui/Ui.java index bb4bdee..ade8dd1 100644 --- a/src/main/java/com/loomcom/symon/ui/Ui.java +++ b/src/main/java/com/loomcom/symon/ui/Ui.java @@ -39,10 +39,13 @@ import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.util.SystemInfo; import com.jthemedetecor.OsThemeDetector; -import com.loomcom.symon.machines.BenEaterMachine; -import com.loomcom.symon.machines.MulticompMachine; -import com.loomcom.symon.machines.SimpleMachine; -import com.loomcom.symon.machines.SymonMachine; + +import java.awt.*; +import java.beans.PropertyChangeListener; +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 { 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: FlatLaf.registerCustomDefaultsSource("com.loomcom.symon.themes"); + logger.debug("AWT Toolkit class: {}", Toolkit.getDefaultToolkit().getClass().getName()); + // Enable automatic scaling, based on system DPI settings: - System.setProperty("sun.java2d.uiScale.enabled", "true"); - System.setProperty("sun.java2d.uiScale", "1x"); - System.setProperty("flatlaf.uiScale", "1x"); + //System.setProperty("sun.java2d.uiScale.enabled", "true"); + //System.setProperty("sun.java2d.uiScale", "1x"); + //System.setProperty("flatlaf.uiScale", "1x"); + + applyDetectedUiScale(); // OS-specific tweaking: @@ -124,4 +131,21 @@ public final class Ui { 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."); + } + } } diff --git a/src/main/java/com/loomcom/symon/ui/X11ResourceReader.java b/src/main/java/com/loomcom/symon/ui/X11ResourceReader.java new file mode 100644 index 0000000..644de92 --- /dev/null +++ b/src/main/java/com/loomcom/symon/ui/X11ResourceReader.java @@ -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); + } + } + } +}