JAL-3911 HiDPISetting update. jalview.bin.* update. Attempt to detect SHIFT key ...
[jalview.git] / getdown / src / getdown / launcher / src / main / java / com / threerings / getdown / launcher / GetdownApp.java
1 //
2 // Getdown - application installer, patcher and launcher
3 // Copyright (C) 2004-2018 Getdown authors
4 // https://github.com/threerings/getdown/blob/master/LICENSE
5
6 package com.threerings.getdown.launcher;
7
8 import java.awt.Color;
9 import java.awt.Container;
10 import java.awt.Image;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.KeyListener;
13 import java.awt.event.KeyEvent;
14 import java.awt.event.WindowAdapter;
15 import java.awt.event.WindowEvent;
16 import java.io.BufferedOutputStream;
17 import java.io.File;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.PrintStream;
21 import java.util.ArrayList;
22 import java.util.List;
23
24 import javax.swing.AbstractAction;
25 import javax.swing.JComponent;
26 import javax.swing.JFrame;
27 import javax.swing.KeyStroke;
28 import javax.swing.WindowConstants;
29
30 import com.samskivert.swing.util.SwingUtil;
31 import com.threerings.getdown.data.Application;
32 import com.threerings.getdown.data.Build;
33 import com.threerings.getdown.data.EnvConfig;
34 import com.threerings.getdown.data.SysProps;
35 import com.threerings.getdown.util.LaunchUtil;
36 import com.threerings.getdown.util.StringUtil;
37 import static com.threerings.getdown.Log.log;
38 import jalview.bin.StartupNotificationListener;
39 import jalview.bin.HiDPISetting;
40
41 /**
42  * The main application entry point for Getdown.
43  */
44 public class GetdownApp
45 {
46   // try and set HiDPISetting property immediately
47   static {
48     HiDPISetting.setScaleProperty();
49   }
50
51   public static String startupFilesParameterString = "";
52   /**
53    * The main entry point of the Getdown launcher application.
54    */
55   public static void main (String[] argv) {
56     try {
57       start(argv);
58     } catch (Exception e) {
59       log.warning("main() failed.", e);
60     }
61   }
62
63   /**
64    * Runs Getdown as an application, using the arguments supplie as {@code argv}.
65    * @return the {@code Getdown} instance that is running. {@link Getdown#start} will have been
66    * called on it.
67    * @throws Exception if anything goes wrong starting Getdown.
68    */
69   public static Getdown start (String[] argv) throws Exception {
70     List<EnvConfig.Note> notes = new ArrayList<>();
71     EnvConfig envc = EnvConfig.create(argv, notes);
72     if (envc == null) {
73       if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message);
74       else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]");
75       System.exit(-1);
76     }
77
78     // pipe our output into a file in the application directory
79     if (!SysProps.noLogRedir()) {
80       File logFile = new File(envc.appDir, "launcher.log");
81       try {
82         PrintStream logOut = new PrintStream(
83                 new BufferedOutputStream(new FileOutputStream(logFile)), true);
84         System.setOut(logOut);
85         System.setErr(logOut);
86       } catch (IOException ioe) {
87         log.warning("Unable to redirect output to '" + logFile + "': " + ioe);
88       }
89     }
90
91     // report any notes from reading our env config, and abort if necessary
92     boolean abort = false;
93     for (EnvConfig.Note note : notes) {
94       switch (note.level) {
95       case INFO: log.info(note.message); break;
96       case WARN: log.warning(note.message); break;
97       case ERROR: log.error(note.message); abort = true; break;
98       }
99     }
100     if (abort) System.exit(-1);
101
102     log.info("Starting ...");
103     try
104     {
105       jalview.bin.StartupNotificationListener.setListener();
106     } catch (Exception e)
107     {
108       e.printStackTrace();
109     } catch (NoClassDefFoundError e)
110     {
111       log.warning("Starting without install4j classes");
112     } catch (Throwable t)
113     {
114       t.printStackTrace();
115     }
116     
117     // record a few things for posterity
118     log.info("------------------ VM Info ------------------");
119     log.info("-- OS Name: " + System.getProperty("os.name"));
120     log.info("-- OS Arch: " + System.getProperty("os.arch"));
121     log.info("-- OS Vers: " + System.getProperty("os.version"));
122     log.info("-- Java Vers: " + System.getProperty("java.version"));
123     log.info("-- Java Home: " + System.getProperty("java.home"));
124     log.info("-- Install4j Vers: " + Application.i4jVersion);
125     log.info("-- Install4j Template Vers: " + System.getProperty("installer_template_version"));
126     log.info("-- User Name: " + System.getProperty("user.name"));
127     log.info("-- User Home: " + System.getProperty("user.home"));
128     log.info("-- Cur dir: " + System.getProperty("user.dir"));
129     log.info("-- Launcher version: "+Build.version());
130     log.info("-- startupFilesParameterString: " + startupFilesParameterString);
131     log.info("---------------------------------------------");
132
133     Getdown app = new Getdown(envc) {
134       @Override
135       protected Container createContainer () {
136 System.out.println("0 Creating Container");
137         // create our user interface, and display it
138         if (_frame == null) {
139           _frame = new JFrame("");
140           _frame.addWindowListener(new WindowAdapter() {
141             @Override
142             public void windowClosing (WindowEvent evt) {
143               handleWindowClose();
144             }
145           });
146           
147           // keep_on_top
148           try {
149                   readConfig(false);
150           } catch (Exception e) {
151                   log.warning("Error reading config for keep_on_top");
152           }
153           // move window to top, always on top
154           if (_ifc.keepOnTop) {
155                   log.info("Keep on top set to ", "keep_on_top", _ifc.keepOnTop);
156                           _frame.setAlwaysOnTop(true);
157           }
158
159           // handle close on ESC
160           String cancelId = "Cancel"; // $NON-NLS-1$
161           _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
162                   KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId);
163           _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() {
164             public void actionPerformed (ActionEvent e) {
165               handleWindowClose();
166             }
167           });
168
169           // look for SHIFT key presses and set flag in Application
170           _frame.addKeyListener(
171             new KeyListener() {
172               @Override
173               public void keyPressed(KeyEvent e) {
174                 if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
175                   Application.setShiftKeyPressed(true);
176                   //_app.
177                 }
178               }
179               @Override
180               public void keyReleased(KeyEvent e) {
181               }
182               @Override
183               public void keyTyped(KeyEvent e) {
184               }
185             }
186           );
187
188           // this cannot be called in configureContainer as it is only allowed before the
189           // frame has been displayed for the first time
190           _frame.setUndecorated(_ifc.hideDecorations);
191           _frame.setResizable(false);
192         } else {
193           _frame.getContentPane().removeAll();
194         }
195         _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
196         return _frame.getContentPane();
197       }
198
199       @Override
200       protected void configureContainer () {
201         if (_frame == null) return;
202
203         _frame.setTitle(_ifc.name);
204
205         try {
206           _frame.setBackground(new Color(_ifc.background, true));
207         } catch (Exception e) {
208           log.warning("Failed to set background", "bg", _ifc.background, e);
209         }
210
211         if (_ifc.iconImages != null && _ifc.iconImages.size() > 0) {
212           ArrayList<Image> icons = new ArrayList<>();
213           for (String path : _ifc.iconImages) {
214             Image img = loadImage(path);
215             if (img == null) {
216               log.warning("Error loading icon image", "path", path);
217             } else {
218               icons.add(img);
219             }
220           }
221           if (icons.isEmpty()) {
222             log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
223           } else {
224             _frame.setIconImages(icons);
225           }
226         }
227       }
228
229       @Override
230       protected void showContainer () {
231         if (_frame != null) {
232           _frame.pack();
233           SwingUtil.centerWindow(_frame);
234           _frame.setVisible(true);
235         }
236         _shownContainer = true;
237       }
238
239       @Override
240       protected void disposeContainer () {
241         if (_frame != null) {
242           _frame.dispose();
243           _frame = null;
244         }
245       }
246
247       @Override
248       protected void showDocument (String url) {
249         if (!StringUtil.couldBeValidUrl(url)) {
250           // command injection would be possible if we allowed e.g. spaces and double quotes
251           log.warning("Invalid document URL.", "url", url);
252           return;
253         }
254         String[] cmdarray;
255         if (LaunchUtil.isWindows()) {
256           String osName = System.getProperty("os.name", "");
257           if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
258             cmdarray = new String[] {
259                 "command.com", "/c", "start", "\"" + url + "\"" };
260           } else {
261             cmdarray = new String[] {
262                 "cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
263           }
264         } else if (LaunchUtil.isMacOS()) {
265           cmdarray = new String[] { "open", url };
266         } else { // Linux, Solaris, etc.
267           cmdarray = new String[] { "firefox", url };
268         }
269         try {
270           Runtime.getRuntime().exec(cmdarray);
271         } catch (Exception e) {
272           log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
273         }
274       }
275
276       @Override
277       protected void exit (int exitCode) {
278         // if we're running the app in the same JVM, don't call System.exit, but do
279         // make double sure that the download window is closed.
280         if (invokeDirect()) {
281           disposeContainer();
282         } else {
283           System.exit(exitCode);
284         }
285       }
286
287       @Override
288       protected void fail (String message) {
289         super.fail(message);
290         // super.fail causes the UI to be created (if needed) on the next UI tick, so we
291         // want to wait until that happens before we attempt to redecorate the window
292         EQinvoke(new Runnable() {
293           @Override
294           public void run() {
295             // if the frame was set to be undecorated, make window decoration available
296             // to allow the user to close the window
297             if (_frame != null && _frame.isUndecorated()) {
298               _frame.dispose();
299               Color bg = _frame.getBackground();
300               if (bg != null && bg.getAlpha() < 255) {
301                 // decorated windows do not allow alpha backgrounds
302                 _frame.setBackground(
303                         new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));
304               }
305               _frame.setUndecorated(false);
306               showContainer();
307             }
308           }
309         });
310       }
311
312       protected JFrame _frame;
313     };
314     
315     String startupFile = getStartupFilesParameterString();
316     if (!StringUtil.isBlank(startupFile)) {
317       Application.setStartupFilesFromParameterString(startupFile);
318     }
319  
320     app.start();
321     return app;
322   }
323   
324   public static void setStartupFilesParameterString(String parameters) {
325     startupFilesParameterString = parameters;
326   }
327   
328   public static String getStartupFilesParameterString() {
329     return startupFilesParameterString;
330   }
331 }