source: josm/trunk/src/org/openstreetmap/josm/tools/PlatformHook.java@ 15922

Last change on this file since 15922 was 15922, checked in by Don-vip, 6 years ago

see #18720 - Headless mode is obviously not an HIDPI screen, as Jenkins wisely reminds us

  • Property svn:eol-style set to native
File size: 12.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.awt.GraphicsEnvironment;
5import java.awt.Toolkit;
6import java.awt.event.KeyEvent;
7import java.io.BufferedReader;
8import java.io.File;
9import java.io.IOException;
10import java.io.InputStreamReader;
11import java.nio.charset.StandardCharsets;
12import java.security.KeyStoreException;
13import java.security.NoSuchAlgorithmException;
14import java.security.cert.CertificateException;
15import java.security.cert.X509Certificate;
16import java.text.DateFormat;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.Date;
20import java.util.List;
21
22import org.openstreetmap.josm.data.projection.datum.NTV2Proj4DirGridShiftFileSource;
23import org.openstreetmap.josm.io.CertificateAmendment.NativeCertAmend;
24import org.openstreetmap.josm.spi.preferences.Config;
25import org.openstreetmap.josm.tools.date.DateUtils;
26
27/**
28 * This interface allows platform (operating system) dependent code
29 * to be bundled into self-contained classes.
30 * @since 1023
31 */
32public interface PlatformHook {
33
34 /**
35 * Visitor to construct a PlatformHook from a given {@link Platform} object.
36 */
37 PlatformVisitor<PlatformHook> CONSTRUCT_FROM_PLATFORM = new PlatformVisitor<PlatformHook>() {
38 @Override
39 public PlatformHook visitUnixoid() {
40 return new PlatformHookUnixoid();
41 }
42
43 @Override
44 public PlatformHook visitWindows() {
45 return new PlatformHookWindows();
46 }
47
48 @Override
49 public PlatformHook visitOsx() {
50 return new PlatformHookOsx();
51 }
52 };
53
54 /**
55 * Get the platform corresponding to this platform hook.
56 * @return the platform corresponding to this platform hook
57 */
58 Platform getPlatform();
59
60 /**
61 * The preStartupHook will be called extremely early. It is
62 * guaranteed to be called before the GUI setup has started.
63 *
64 * Reason: On OSX we need to inform the Swing libraries
65 * that we want to be integrated with the OS before we setup our GUI.
66 */
67 default void preStartupHook() {
68 // Do nothing
69 }
70
71 /**
72 * The afterPrefStartupHook will be called early, but after
73 * the preferences have been loaded and basic processing of
74 * command line arguments is finished.
75 * It is guaranteed to be called before the GUI setup has started.
76 */
77 default void afterPrefStartupHook() {
78 // Do nothing
79 }
80
81 /**
82 * The startupHook will be called early, but after the GUI
83 * setup has started.
84 *
85 * Reason: On OSX we need to register some callbacks with the
86 * OS, so we'll receive events from the system menu.
87 * @param callback Java expiration callback, providing GUI feedback
88 * @since 12270 (signature)
89 */
90 default void startupHook(JavaExpirationCallback callback) {
91 // Do nothing
92 }
93
94 /**
95 * The openURL hook will be used to open an URL in the
96 * default web browser.
97 * @param url The URL to open
98 * @throws IOException if any I/O error occurs
99 */
100 void openUrl(String url) throws IOException;
101
102 /**
103 * The initSystemShortcuts hook will be called by the
104 * Shortcut class after the modifier groups have been read
105 * from the config, but before any shortcuts are read from
106 * it or registered from within the application.
107 *
108 * Please note that you are not allowed to register any
109 * shortuts from this hook, but only "systemCuts"!
110 *
111 * BTW: SystemCuts should be named "system:&lt;whatever&gt;",
112 * and it'd be best if sou'd recycle the names already used
113 * by the Windows and OSX hooks. Especially the later has
114 * really many of them.
115 *
116 * You should also register any and all shortcuts that the
117 * operation system handles itself to block JOSM from trying
118 * to use them---as that would just not work. Call setAutomatic
119 * on them to prevent the keyboard preferences from allowing the
120 * user to change them.
121 */
122 void initSystemShortcuts();
123
124 /**
125 * Returns the default LAF to be used on this platform to look almost as a native application.
126 * @return The default native LAF for this platform
127 */
128 String getDefaultStyle();
129
130 /**
131 * Determines if the platform allows full-screen.
132 * @return {@code true} if full screen is allowed, {@code false} otherwise
133 */
134 default boolean canFullscreen() {
135 return !GraphicsEnvironment.isHeadless() &&
136 GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().isFullScreenSupported();
137 }
138
139 /**
140 * Determines if the default screen is a high-dpi device such as a mac Retina display.
141 * @return {@code true} if the default screen is a high-dpi device such as a mac Retina display
142 * @since 15918
143 */
144 default boolean isHighDpiDisplay() {
145 // https://stackoverflow.com/a/49770313
146 return !GraphicsEnvironment.isHeadless() &&
147 !GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()
148 .getDefaultTransform().isIdentity();
149 }
150
151 /**
152 * Renames a file.
153 * @param from Source file
154 * @param to Target file
155 * @return {@code true} if the file has been renamed, {@code false} otherwise
156 */
157 default boolean rename(File from, File to) {
158 return from.renameTo(to);
159 }
160
161 /**
162 * Returns a detailed OS description (at least family + version).
163 * @return A detailed OS description.
164 * @since 5850
165 */
166 String getOSDescription();
167
168 /**
169 * Returns OS build number.
170 * @return OS build number.
171 * @since 12217
172 */
173 default String getOSBuildNumber() {
174 return "";
175 }
176
177 /**
178 * Returns the {@code X509Certificate} matching the given certificate amendment information.
179 * @param certAmend certificate amendment
180 * @return the {@code X509Certificate} matching the given certificate amendment information, or {@code null}
181 * @throws KeyStoreException in case of error
182 * @throws IOException in case of error
183 * @throws CertificateException in case of error
184 * @throws NoSuchAlgorithmException in case of error
185 * @since 13450
186 */
187 default X509Certificate getX509Certificate(NativeCertAmend certAmend)
188 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
189 return null;
190 }
191
192 /**
193 * Executes a native command and returns the first line of standard output.
194 * @param command array containing the command to call and its arguments.
195 * @return first stripped line of standard output
196 * @throws IOException if an I/O error occurs
197 * @since 12217
198 */
199 default String exec(String... command) throws IOException {
200 Process p = Runtime.getRuntime().exec(command);
201 try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
202 return Utils.strip(input.readLine());
203 }
204 }
205
206 /**
207 * Returns the platform-dependent default cache directory.
208 * @return the platform-dependent default cache directory
209 * @since 7829
210 */
211 File getDefaultCacheDirectory();
212
213 /**
214 * Returns the platform-dependent default preferences directory.
215 * @return the platform-dependent default preferences directory
216 * @since 7831
217 */
218 File getDefaultPrefDirectory();
219
220 /**
221 * Returns the platform-dependent default user data directory.
222 * @return the platform-dependent default user data directory
223 * @since 7834
224 */
225 File getDefaultUserDataDirectory();
226
227 /**
228 * Returns the list of platform-dependent default datum shifting directories for the PROJ.4 library.
229 * @return the list of platform-dependent default datum shifting directories for the PROJ.4 library
230 * @since 11642
231 */
232 default List<File> getDefaultProj4NadshiftDirectories() {
233 return getPlatform().accept(NTV2Proj4DirGridShiftFileSource.getInstance());
234 }
235
236 /**
237 * Determines if the JVM is OpenJDK-based.
238 * @return {@code true} if {@code java.home} contains "openjdk", {@code false} otherwise
239 * @since 12219
240 */
241 default boolean isOpenJDK() {
242 String javaHome = Utils.getSystemProperty("java.home");
243 return javaHome != null && javaHome.contains("openjdk");
244 }
245
246 /**
247 * Returns extended modifier key used as the appropriate accelerator key for menu shortcuts.
248 * It is advised everywhere to use {@link Toolkit#getMenuShortcutKeyMask()} to get the cross-platform modifier, but:
249 * <ul>
250 * <li>it returns KeyEvent.CTRL_MASK instead of KeyEvent.CTRL_DOWN_MASK. We used the extended
251 * modifier for years, and Oracle recommends to use it instead, so it's best to keep it</li>
252 * <li>the method throws a HeadlessException ! So we would need to handle it for unit tests anyway</li>
253 * </ul>
254 * @return extended modifier key used as the appropriate accelerator key for menu shortcuts
255 * @since 12748 (as a replacement to {@code GuiHelper.getMenuShortcutKeyMaskEx()})
256 */
257 default int getMenuShortcutKeyMaskEx() {
258 // To remove when switching to Java 10+, and use Toolkit.getMenuShortcutKeyMaskEx instead
259 return KeyEvent.CTRL_DOWN_MASK;
260 }
261
262 /**
263 * Called when an outdated version of Java is detected at startup.
264 * @since 12270
265 */
266 @FunctionalInterface
267 interface JavaExpirationCallback {
268 /**
269 * Asks user to update its version of Java.
270 * @param updVersion target update version
271 * @param url download URL
272 * @param major true for a migration towards a major version of Java (8:9), false otherwise
273 * @param eolDate the EOL/expiration date
274 */
275 void askUpdateJava(String updVersion, String url, String eolDate, boolean major);
276 }
277
278 /**
279 * Checks if the running version of Java has expired, proposes to user to update it if needed.
280 * @param callback Java expiration callback
281 * @since 12270 (signature)
282 * @since 12219
283 */
284 default void checkExpiredJava(JavaExpirationCallback callback) {
285 Date expiration = Utils.getJavaExpirationDate();
286 if (expiration != null && expiration.before(new Date())) {
287 String latestVersion = Utils.getJavaLatestVersion();
288 String currentVersion = Utils.getSystemProperty("java.version");
289 // #17831 WebStart may be launched with an expired JRE but then launching JOSM with up-to-date JRE
290 if (latestVersion == null || !latestVersion.equalsIgnoreCase(currentVersion)) {
291 callback.askUpdateJava(latestVersion != null ? latestVersion : "latest",
292 Config.getPref().get("java.update.url", "https://www.java.com/download"),
293 DateUtils.getDateFormat(DateFormat.MEDIUM).format(expiration), false);
294 }
295 }
296 }
297
298 /**
299 * Called when interfacing with native OS functions. Currently only used with macOS.
300 * The callback must perform all GUI-related tasks associated to an OS request.
301 * The non-GUI, platform-specific tasks, are usually performed by the {@code PlatformHook}.
302 * @since 12695
303 */
304 interface NativeOsCallback {
305 /**
306 * macOS: Called when JOSM is asked to open a list of files.
307 * @param files list of files to open
308 */
309 void openFiles(List<File> files);
310
311 /**
312 * macOS: Invoked when JOSM is asked to quit.
313 * @return {@code true} if JOSM has been closed, {@code false} if the user has cancelled the operation.
314 */
315 boolean handleQuitRequest();
316
317 /**
318 * macOS: Called when JOSM is asked to show it's about dialog.
319 */
320 void handleAbout();
321
322 /**
323 * macOS: Called when JOSM is asked to show it's preferences UI.
324 */
325 void handlePreferences();
326 }
327
328 /**
329 * Registers the native OS callback. Currently only needed for macOS.
330 * @param callback the native OS callback
331 * @since 12695
332 */
333 default void setNativeOsCallback(NativeOsCallback callback) {
334 // To be implemented if needed
335 }
336
337 /**
338 * Resolves a file link to its destination file.
339 * @param file file (link or regular file)
340 * @return destination file in case of a file link, file if regular
341 * @since 13691
342 */
343 default File resolveFileLink(File file) {
344 // Override if needed
345 return file;
346 }
347
348 /**
349 * Returns a set of possible platform specific directories where resources could be stored.
350 * @return A set of possible platform specific directories where resources could be stored.
351 * @since 14144
352 */
353 default Collection<String> getPossiblePreferenceDirs() {
354 return Collections.emptyList();
355 }
356}
Note: See TracBrowser for help on using the repository browser.