2 // Getdown - application installer, patcher and launcher
3 // Copyright (C) 2004-2018 Getdown authors
4 // https://github.com/threerings/getdown/blob/master/LICENSE
6 package com.threerings.getdown.launcher;
9 import java.awt.Container;
10 import java.awt.EventQueue;
11 import java.awt.Image;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.KeyEvent;
14 import java.awt.event.WindowAdapter;
15 import java.awt.event.WindowEvent;
16 import java.io.BufferedOutputStream;
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;
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;
30 import com.install4j.api.launcher.StartupNotification;
31 import com.samskivert.swing.util.SwingUtil;
32 import com.threerings.getdown.data.Application;
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;
40 * The main application entry point for Getdown.
42 public class GetdownApp
44 public static String startupFilesParameterString = "";
46 * The main entry point of the Getdown launcher application.
48 public static void main (String[] argv) {
51 } catch (Exception e) {
52 log.warning("main() failed.", e);
57 * Runs Getdown as an application, using the arguments supplie as {@code argv}.
58 * @return the {@code Getdown} instance that is running. {@link Getdown#start} will have been
60 * @throws Exception if anything goes wrong starting Getdown.
62 public static Getdown start (String[] argv) throws Exception {
63 List<EnvConfig.Note> notes = new ArrayList<>();
64 EnvConfig envc = EnvConfig.create(argv, notes);
66 if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message);
67 else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]");
71 // pipe our output into a file in the application directory
72 if (!SysProps.noLogRedir()) {
73 File logFile = new File(envc.appDir, "launcher.log");
75 PrintStream logOut = new PrintStream(
76 new BufferedOutputStream(new FileOutputStream(logFile)), true);
77 System.setOut(logOut);
78 System.setErr(logOut);
79 } catch (IOException ioe) {
80 log.warning("Unable to redirect output to '" + logFile + "': " + ioe);
84 // report any notes from reading our env config, and abort if necessary
85 boolean abort = false;
86 for (EnvConfig.Note note : notes) {
88 case INFO: log.info(note.message); break;
89 case WARN: log.warning(note.message); break;
90 case ERROR: log.error(note.message); abort = true; break;
93 if (abort) System.exit(-1);
97 StartupNotification.registerStartupListener(
98 new StartupNotification.Listener() {
100 public void startupPerformed(String parameters)
102 log.warning("startupPerformed: '"+parameters+"'");
103 setStartupFilesParameterString(parameters);
106 } catch (Exception e)
113 // record a few things for posterity
114 log.info("------------------ VM Info ------------------");
115 log.info("-- OS Name: " + System.getProperty("os.name"));
116 log.info("-- OS Arch: " + System.getProperty("os.arch"));
117 log.info("-- OS Vers: " + System.getProperty("os.version"));
118 log.info("-- Java Vers: " + System.getProperty("java.version"));
119 log.info("-- Java Home: " + System.getProperty("java.home"));
120 log.info("-- User Name: " + System.getProperty("user.name"));
121 log.info("-- User Home: " + System.getProperty("user.home"));
122 log.info("-- Cur dir: " + System.getProperty("user.dir"));
123 log.info("-- startupFilesParameterString: " + startupFilesParameterString);
124 log.info("---------------------------------------------");
126 Getdown app = new Getdown(envc) {
128 protected Container createContainer () {
129 // create our user interface, and display it
130 if (_frame == null) {
131 _frame = new JFrame("");
132 _frame.addWindowListener(new WindowAdapter() {
134 public void windowClosing (WindowEvent evt) {
138 // handle close on ESC
139 String cancelId = "Cancel"; // $NON-NLS-1$
140 _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
141 KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId);
142 _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() {
143 public void actionPerformed (ActionEvent e) {
147 // this cannot be called in configureContainer as it is only allowed before the
148 // frame has been displayed for the first time
149 _frame.setUndecorated(_ifc.hideDecorations);
150 _frame.setResizable(false);
152 _frame.getContentPane().removeAll();
154 _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
155 return _frame.getContentPane();
159 protected void configureContainer () {
160 if (_frame == null) return;
162 _frame.setTitle(_ifc.name);
165 _frame.setBackground(new Color(_ifc.background, true));
166 } catch (Exception e) {
167 log.warning("Failed to set background", "bg", _ifc.background, e);
170 if (_ifc.iconImages != null) {
171 ArrayList<Image> icons = new ArrayList<>();
172 for (String path : _ifc.iconImages) {
173 Image img = loadImage(path);
175 log.warning("Error loading icon image", "path", path);
180 if (icons.isEmpty()) {
181 log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
183 _frame.setIconImages(icons);
189 protected void showContainer () {
190 if (_frame != null) {
192 SwingUtil.centerWindow(_frame);
193 _frame.setVisible(true);
198 protected void disposeContainer () {
199 if (_frame != null) {
206 protected void showDocument (String url) {
207 if (!StringUtil.couldBeValidUrl(url)) {
208 // command injection would be possible if we allowed e.g. spaces and double quotes
209 log.warning("Invalid document URL.", "url", url);
213 if (LaunchUtil.isWindows()) {
214 String osName = System.getProperty("os.name", "");
215 if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
216 cmdarray = new String[] {
217 "command.com", "/c", "start", "\"" + url + "\"" };
219 cmdarray = new String[] {
220 "cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
222 } else if (LaunchUtil.isMacOS()) {
223 cmdarray = new String[] { "open", url };
224 } else { // Linux, Solaris, etc.
225 cmdarray = new String[] { "firefox", url };
228 Runtime.getRuntime().exec(cmdarray);
229 } catch (Exception e) {
230 log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
235 protected void exit (int exitCode) {
236 // if we're running the app in the same JVM, don't call System.exit, but do
237 // make double sure that the download window is closed.
238 if (invokeDirect()) {
241 System.exit(exitCode);
246 protected void fail (String message) {
248 // super.fail causes the UI to be created (if needed) on the next UI tick, so we
249 // want to wait until that happens before we attempt to redecorate the window
250 EventQueue.invokeLater(new Runnable() {
253 // if the frame was set to be undecorated, make window decoration available
254 // to allow the user to close the window
255 if (_frame != null && _frame.isUndecorated()) {
257 Color bg = _frame.getBackground();
258 if (bg != null && bg.getAlpha() < 255) {
259 // decorated windows do not allow alpha backgrounds
260 _frame.setBackground(
261 new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));
263 _frame.setUndecorated(false);
270 protected JFrame _frame;
273 log.warning("Startup file?",
274 "paramstring", '"'+getStartupFilesParameterString()+'"',
275 "isWindows", LaunchUtil.isWindows(),
276 "argv.length", argv.length,
277 "argv[0]", argv.length>0?argv[0]:"NULL",
278 "argv[1]", argv.length>1?argv[1]:"NULL",
279 "argv[2]", argv.length>2?argv[2]:"NULL",
280 "argv[3]", argv.length>3?argv[3]:"NULL"
283 if (getStartupFilesParameterString() != null && getStartupFilesParameterString().length() > 0) {
284 app.setStartupFilesFromParameterString(getStartupFilesParameterString());
286 getStartupFilesParameterString().length() == 0
287 && LaunchUtil.isWindows()
289 && argv[0].equals(".")
290 && argv[1].equals("noappid")
291 && argv[2].endsWith("."+Application.locatorFileExtension)
293 log.info("Jalview Version Locator in args: "+argv[2]);
294 app.setStartupFilesFromParameterString(argv[2]);
295 String[] newArgv = new String[argv.length - 1];
296 System.arraycopy(argv, 0, newArgv, 0, 2);
297 System.arraycopy(argv, 3, newArgv, 2, argv.length - 3);
304 public static void setStartupFilesParameterString(String parameters) {
305 startupFilesParameterString = parameters;
308 public static String getStartupFilesParameterString() {
309 return startupFilesParameterString;