diff --git a/README.md b/README.md index b5da8c0..51e851f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Unified desktop integration for SciJava applications. ## Features -The scijava-desktop component provides three kinds of desktop integration: +The scijava-desktop component provides four kinds of desktop integration: 1. **URI Link Scheme Registration & Handling** - Register custom URI schemes (e.g., `myapp://`) with the operating system @@ -22,6 +22,17 @@ The scijava-desktop component provides three kinds of desktop integration: - Associate file types with your application - Platform-specific MIME type handling +4. **Single-Instance Enforcement** + - Prevents multiple application instances when the OS re-launches the binary + - Uses a TCP socket listener via the `SingleInstance` feature of the + [SciJava App Launcher](https://github.com/scijava/app-launcher) to let a + secondary transient instance hand off its arguments to the already-running + primary instance and exit immediately -- before the splash screen appears + - Enabled via the `scijava.app.single-instance` system property + - Useful in conjunction with file type associations and URI scheme + registration, both of which trigger the OS to launch another copy of the + application binary when a registered file type or link is clicked + ## Platform Support - **Linux**: Full support for URI schemes and desktop icons via `.desktop` files diff --git a/pom.xml b/pom.xml index 6446688..7912073 100644 --- a/pom.xml +++ b/pom.xml @@ -104,10 +104,16 @@ 11 bsd_2 SciJava developers. + 2.4.0-SNAPSHOT + true + + org.scijava + app-launcher + org.scijava scijava-common diff --git a/src/main/java/org/scijava/desktop/DefaultDesktopService.java b/src/main/java/org/scijava/desktop/DefaultDesktopService.java index 35f9ba1..cd2b221 100644 --- a/src/main/java/org/scijava/desktop/DefaultDesktopService.java +++ b/src/main/java/org/scijava/desktop/DefaultDesktopService.java @@ -28,9 +28,12 @@ */ package org.scijava.desktop; +import org.scijava.console.ConsoleService; import org.scijava.event.ContextCreatedEvent; import org.scijava.event.EventHandler; +import org.scijava.launcher.SingleInstance; import org.scijava.log.LogService; +import org.scijava.main.MainService; import org.scijava.object.LazyObjects; import org.scijava.object.ObjectService; import org.scijava.platform.PlatformService; @@ -39,6 +42,7 @@ import org.scijava.prefs.PrefService; import org.scijava.service.AbstractService; import org.scijava.service.Service; +import org.scijava.startup.StartupService; import org.scijava.thread.ThreadService; import java.io.BufferedReader; @@ -68,6 +72,15 @@ public class DefaultDesktopService extends AbstractService implements DesktopSer @Parameter private ThreadService threadService; + @Parameter + private ConsoleService consoleService; + + @Parameter + private MainService mainService; + + @Parameter + private StartupService startupService; + @Parameter(required = false) private PrefService prefs; @@ -165,6 +178,15 @@ public String getDescription(final String extension) { return descriptions.get(extension); } + // -- Service methods -- + + @Override + public void initialize() { + if (isSingleInstanceEnabled()) { + startupService.addOperation(() -> SingleInstance.listen(0, this::receiveArgs)); + } + } + // -- Event handlers -- @EventHandler @@ -279,6 +301,16 @@ private Stream desktopPlatforms() { .map(p -> (DesktopIntegrationProvider) p); } + /** + * Receives arguments from a secondary transient application instance + * and handles them via standard SciJava Common service methods. + */ + private void receiveArgs(String[] args) { + consoleService.processArgs(args); + mainService.execMains(); + startupService.executeOperations(); + } + private void maybeAutoInstallDesktopIntegrations() { // Auto-install desktop integrations on first run. diff --git a/src/main/java/org/scijava/desktop/DesktopService.java b/src/main/java/org/scijava/desktop/DesktopService.java index 879328f..5608711 100644 --- a/src/main/java/org/scijava/desktop/DesktopService.java +++ b/src/main/java/org/scijava/desktop/DesktopService.java @@ -41,6 +41,10 @@ */ public interface DesktopService extends SciJavaService { + default boolean isSingleInstanceEnabled() { + return Boolean.getBoolean("scijava.app.single-instance"); + } + /** * Applies desktop integration settings. *