import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
+import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
+ public static boolean nosplash = false;
+
/**
* news reader - null if it was never started.
*/
checkURLLinks();
// Spawn a thread that shows the splashscreen
-
- SwingUtilities.invokeLater(new Runnable()
+ if (!nosplash)
{
- @Override
- public void run()
+ SwingUtilities.invokeLater(new Runnable()
{
- new SplashScreen(true);
- }
- });
+ @Override
+ public void run()
+ {
+ new SplashScreen(true);
+ }
+ });
+ }
// Thread off a new instance of the file chooser - this reduces the time
// it
}
});
desktop.addMouseListener(ma);
-
}
/**
10, getHeight() - fm.getHeight());
}
}
+
+ // output debug scale message. Important for jalview.bin.HiDPISettingTest2
+ Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
}
}
}
return result;
}
+
+ public static final String debugScaleMessage = "Desktop graphics transform scale=";
+
+ private static boolean debugScaleMessageDone = false;
+
+ public static void debugScaleMessage(Graphics g)
+ {
+ if (debugScaleMessageDone)
+ {
+ return;
+ }
+ // output used by tests to check HiDPI scaling settings in action
+ try
+ {
+ Graphics2D gg = (Graphics2D) g;
+ if (gg != null)
+ {
+ AffineTransform t = gg.getTransform();
+ double scaleX = t.getScaleX();
+ double scaleY = t.getScaleY();
+ Cache.debug(debugScaleMessage + scaleX + " (X)");
+ Cache.debug(debugScaleMessage + scaleY + " (Y)");
+ debugScaleMessageDone = true;
+ }
+ else
+ {
+ Cache.debug("Desktop graphics null");
+ }
+ } catch (Exception e)
+ {
+ Cache.debug(Cache.getStackTraceString(e));
+ }
+ }
}
--- /dev/null
+package jalview.bin;
+
+import static org.testng.Assert.assertEquals;
+
+import org.mockito.Mockito;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+
+/*
+ * Testing a HiDPI display is difficult without running in a HiDPI display.
+ * The gathering of screen size and resolution performed by jalview.bin.HiDPISetting
+ * is now farmed out into a separate class jalview.bin.ScreenInfo so it can be more
+ * easily Mocked in tests.
+ * Two sets of tests are performed.
+ * 1) testLinuxScalePropertyToActualTransform() sets the property that HiDPISetting
+ * uses (via jalview.bin.Launcher or getdown) to scale up Jalview, and then looks at
+ * the alignment panel graphics2d transform to see if it's been scaled by the same
+ * amount (in this case 4 -- unlikely to happen by accident!).
+ * 2) testHiDPISettingInit() which tests the calculation that HiDPISetting uses to
+ * decide from apparent screen information (mocked using Mockito in the tests) what
+ * scaling factor to set (it doesn't actually set it, just suggests it to
+ * jalview.bin.Launcher)
+ */
+public class HiDPISettingTest1
+{
+
+ AlignFrame af = null;
+
+ @BeforeMethod(alwaysRun = true)
+ public void setUp()
+ {
+ JvOptionPane.setInteractiveMode(false);
+ JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+ Cache.loadProperties("test/jalview/bin/hidpiTestProps.jvprops");
+ Jalview.main(
+ new String[]
+ { "-nosplash", "-nonews", "-noquestionnaire",
+ "-nowebservicediscovery" });
+
+ af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+ DataSourceType.FILE);
+
+ /*
+ * wait for Consensus thread to complete
+ */
+ do
+ {
+ try
+ {
+ Thread.sleep(50);
+ } catch (InterruptedException x)
+ {
+ }
+ } while (af.getViewport().getCalcManager().isWorking());
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void tearDown()
+ {
+ Desktop.instance.closeAll_actionPerformed(null);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testHiDPISettingInit()
+ {
+ String scalePropertyName = "sun.java2d.uiScale";
+ // ensure scale property is cleared (otherwise it will be re-used)
+ System.clearProperty(HiDPISetting.scalePropertyName);
+
+ { // keep the mock under lock
+ // Ancient monitor -- no property change set
+ setMockScreen(1024, 768, 72);
+ assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+ // Old monitor -- no property change set
+ setMockScreen(1200, 800, 96);
+ assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+ // HD screen -- no property change set
+ setMockScreen(1920, 1080, 96);
+ assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+ // 4K screen -- scale by 2
+ setMockScreen(3180, 2160, 80);
+ assertEquals(HiDPISetting.getScalePropertyArg(),
+ "-D" + scalePropertyName + "=2");
+
+ // 4K screen with high dpi -- scale by 3
+ setMockScreen(3180, 2160, 450);
+ assertEquals(HiDPISetting.getScalePropertyArg(),
+ "-D" + scalePropertyName + "=3");
+
+ // stupidly big screen -- scale by 8
+ setMockScreen(19200, 10800, 72);
+ assertEquals(HiDPISetting.getScalePropertyArg(),
+ "-D" + scalePropertyName + "=8");
+ }
+ }
+
+ private void setMockScreen(int width, int height, int dpi)
+ {
+ HiDPISetting.clear();
+ ScreenInfo mockScreenInfo = Mockito.spy(HiDPISetting.getScreenInfo());
+ Mockito.doReturn(height).when(mockScreenInfo).getScreenHeight();
+ Mockito.doReturn(width).when(mockScreenInfo).getScreenWidth();
+ Mockito.doReturn(dpi).when(mockScreenInfo).getScreenResolution();
+ HiDPISetting.setScreenInfo(mockScreenInfo);
+ }
+}
--- /dev/null
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ScanResult;
+import jalview.gui.Desktop;
+
+public class HiDPISettingTest2
+{
+ private static final int TIMEOUT = 10;
+
+ private static class Worker extends Thread
+ {
+ private final Process process;
+
+ private BufferedReader outputReader;
+
+ private BufferedReader errorReader;
+
+ private boolean exited;
+
+ private Worker(Process process)
+ {
+ this.process = process;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ exited = process.waitFor(TIMEOUT, TimeUnit.SECONDS);
+ } catch (InterruptedException ignore)
+ {
+ return;
+ }
+ this.interrupt();
+ this.process.destroy();
+ }
+
+ public BufferedReader getOutputReader()
+ {
+ return outputReader;
+ }
+
+ public void setOutputReader(BufferedReader outputReader)
+ {
+ this.outputReader = outputReader;
+ }
+
+ public BufferedReader getErrorReader()
+ {
+ return errorReader;
+ }
+
+ public void setErrorReader(BufferedReader errorReader)
+ {
+ this.errorReader = errorReader;
+ }
+ }
+
+ private static ClassGraph scanner = null;
+
+ private static String classpath = null;
+
+ private static String java_exe = null;
+
+ public synchronized static String getClassPath()
+ {
+ if (scanner == null)
+ {
+ scanner = new ClassGraph();
+ ScanResult scan = scanner.scan();
+ classpath = scan.getClasspath();
+ java_exe = System.getProperty("java.home") + File.separator + "bin"
+ + File.separator + "java";
+
+ }
+ while (classpath == null)
+ {
+ try
+ {
+ Thread.sleep(10);
+ } catch (InterruptedException x)
+ {
+
+ }
+ }
+ return classpath;
+ }
+
+ private Worker getJalviewDesktopRunner(String jvmArgs, String appArgs)
+ {
+ String classpath = getClassPath();
+ String cmd = java_exe + " " + " -classpath " + classpath + " " + jvmArgs
+ + " jalview.bin.Jalview " + " "
+ + "-props test/jalview/bin/hidpiTestProps.jvprops " + appArgs;
+ Process proc = null;
+ Worker worker = null;
+ try
+ {
+ proc = Runtime.getRuntime().exec(cmd);
+ } catch (Throwable e)
+ {
+ e.printStackTrace();
+ }
+ if (proc != null)
+ {
+ BufferedReader outputReader = new BufferedReader(
+ new InputStreamReader(proc.getInputStream()));
+ BufferedReader errorReader = new BufferedReader(
+ new InputStreamReader(proc.getErrorStream()));
+ worker = new Worker(proc);
+ worker.start();
+ worker.setOutputReader(outputReader);
+ worker.setErrorReader(errorReader);
+ }
+ return worker;
+ }
+
+ @BeforeTest(alwaysRun = true)
+ public void initialize()
+ {
+ new HiDPISettingTest2();
+ }
+
+ @Test(groups = { "Functional" }, dataProvider = "hidpiScaleArguments")
+ public void testHiDPISettings(int scale)
+ {
+ String jvmArgs = HiDPISetting.getScalePropertyArg(scale);
+
+ String appArgs = " -open examples/uniref50.fa -nosplash -nonews -noquestionnaire -nousagestats -nowebservicediscovery";
+
+ Worker worker = getJalviewDesktopRunner(jvmArgs, appArgs);
+ assertNotNull(worker, "worker is null");
+
+ String ln = null;
+ int count = 0;
+ boolean scaleFound = false;
+ try
+ {
+ while ((ln = worker.getErrorReader().readLine()) != null)
+ {
+ if (++count > 100)
+ {
+ break;
+ }
+ if (ln.contains(Desktop.debugScaleMessage))
+ {
+ String number = ln.substring(ln.indexOf(Desktop.debugScaleMessage)
+ + Desktop.debugScaleMessage.length());
+ number = number.substring(0, number.indexOf(' '));
+ try
+ {
+ double d = Double.valueOf(number);
+
+ assertEquals(d, scale * 1.0);
+ scaleFound = true;
+ } catch (NumberFormatException e)
+ {
+ e.printStackTrace();
+ Assert.fail(
+ "Debug scale message line '" + ln + "' gives number '"
+ + number + "' which could not be parsed");
+ }
+ break;
+ }
+ }
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ if (worker != null && worker.exited == false)
+ {
+ worker.interrupt();
+ worker.process.destroy();
+ }
+ if (!scaleFound)
+ {
+ Assert.fail("Did not find Debug scale message line '"
+ + Desktop.debugScaleMessage + "'");
+ }
+ }
+
+ @DataProvider(name = "hidpiScaleArguments")
+ public static Object[][] getHiDPIScaleArguments()
+ {
+ return new Object[][] { { 1 }, { 2 }, { 4 }, { 10 } };
+ }
+}
--- /dev/null
+#---JalviewX Properties File---
+# HiDPI screen
+SCREENGEOMETRY_WIDTH=3840
+SCREENGEOMETRY_HEIGHT=2160
+SCREEN_WIDTH=3000
+SCREEN_HEIGHT=2000
+USAGESTATS=false
+STARTUP_FILE=examples/uniref50.fa
+SHOW_STARTUP_FILE=false
+FONT_STYLE=plain
+FONT_SIZE=10
+ANTI_ALIAS=false
+SHOW_JAVA_CONSOLE=false
+DAS_REGISTRY_URL=http\://www.ebi.ac.uk/das-srv/registry/das/
+logs.Jalview.level=DEBUG