From 66c8e2d0d5a695baac2e86af19e9b1ff53696acd Mon Sep 17 00:00:00 2001 From: crschnick Date: Fri, 8 Aug 2025 19:57:08 +0000 Subject: [PATCH] Rework install --- .../io/xpipe/app/beacon/AppBeaconServer.java | 7 +- .../impl/OpenFileWithActionProvider.java | 2 +- .../browser/file/BrowserFileSystemHelper.java | 2 +- .../menu/impl/OpenFileWithMenuProvider.java | 2 +- .../menu/impl/RunFileMenuProvider.java | 2 +- .../compress/BaseUnzipUnixMenuProvider.java | 2 +- .../BaseUnzipWindowsActionProvider.java | 2 +- .../xpipe/app/core/AppDesktopIntegration.java | 2 +- .../xpipe/app/core/AppExtensionManager.java | 18 +- .../java/io/xpipe/app/core/AppI18nData.java | 5 +- .../io/xpipe/app/core/AppInstallation.java | 312 ++++++++++++++++++ .../java/io/xpipe/app/core/AppInstance.java | 5 +- .../java/io/xpipe/app/core/AppLocations.java | 97 ++++++ .../java/io/xpipe/app/core/AppRestart.java | 29 +- .../main/java/io/xpipe/app/core/AppSid.java | 2 +- .../java/io/xpipe/app/core/AppTrayIcon.java | 2 +- .../app/core/check/AppCertutilCheck.java | 2 +- .../core/check/AppHomebrewCoreutilsCheck.java | 2 +- .../app/core/check/AppSystemFontCheck.java | 4 +- .../io/xpipe/app/core/check/AppTempCheck.java | 2 +- .../app/core/check/AppTestCommandCheck.java | 6 +- .../java/io/xpipe/app/core/mode/TrayMode.java | 2 +- .../xpipe/app/core/window/AppMainWindow.java | 2 +- .../io/xpipe/app/ext/ExtensionException.java | 4 +- .../app/prefs/ExternalApplicationType.java | 4 +- .../xpipe/app/prefs/ExternalEditorType.java | 14 +- .../xpipe/app/prefs/TroubleshootCategory.java | 12 +- .../app/prefs/WorkspaceCreationDialog.java | 10 +- .../io/xpipe/app/rdp/ExternalRdpClient.java | 6 +- .../xpipe/app/terminal/CmdTerminalType.java | 2 +- .../app/terminal/CustomTerminalType.java | 2 +- .../app/terminal/ExternalTerminalType.java | 8 +- .../xpipe/app/terminal/KittyTerminalType.java | 5 +- .../app/terminal/PowerShellTerminalType.java | 2 +- .../xpipe/app/terminal/TabbyTerminalType.java | 2 +- .../terminal/TerminalLaunchConfiguration.java | 4 +- .../xpipe/app/terminal/TerminalLauncher.java | 5 +- .../xpipe/app/terminal/WaveTerminalType.java | 5 +- .../app/terminal/WindowsTerminalType.java | 7 +- .../io/xpipe/app/test/LocalExtensionTest.java | 2 +- .../xpipe/app/update/AppDistributionType.java | 16 +- .../io/xpipe/app/update/AppInstaller.java | 14 +- .../io/xpipe/app/update/ChocoUpdater.java | 6 +- .../io/xpipe/app/update/WingetUpdater.java | 6 +- .../io/xpipe/app/util/DesktopShortcuts.java | 15 +- .../java/io/xpipe/app/util/FileOpener.java | 4 +- .../java/io/xpipe/app/util/NativeBridge.java | 4 +- .../java/io/xpipe/app/util/ScriptHelper.java | 2 +- .../java/io/xpipe/app/util/ShellTemp.java | 6 +- .../io/xpipe/app/util/SshLocalBridge.java | 5 +- .../io/xpipe/app/util/StoreStateFormat.java | 2 +- beacon/README.md | 18 - .../java/io/xpipe/beacon/BeaconClient.java | 3 +- .../java/io/xpipe/beacon/BeaconConfig.java | 55 +-- .../java/io/xpipe/beacon/BeaconServer.java | 109 +----- .../beacon/test/BeaconDaemonController.java | 99 ------ .../test/BeaconDaemonExtensionTest.java | 23 -- beacon/src/main/java/module-info.java | 1 - .../main/java/io/xpipe/core/Deobfuscator.java | 2 +- .../java/io/xpipe/core/XPipeInstallation.java | 298 ----------------- .../identity/SshIdentityStateManager.java | 2 +- .../base/identity/SshIdentityStrategy.java | 6 +- .../gradle_scripts/local_junit_suite.gradle | 2 - .../gradle_scripts/remote_junit_suite.gradle | 2 - 64 files changed, 566 insertions(+), 739 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/core/AppInstallation.java create mode 100644 app/src/main/java/io/xpipe/app/core/AppLocations.java delete mode 100644 beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonController.java delete mode 100644 beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonExtensionTest.java delete mode 100644 core/src/main/java/io/xpipe/core/XPipeInstallation.java diff --git a/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java b/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java index b932233fe..72efc6bdd 100644 --- a/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java +++ b/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java @@ -7,7 +7,6 @@ import io.xpipe.app.util.DocumentationLink; import io.xpipe.beacon.BeaconConfig; import io.xpipe.beacon.BeaconInterface; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpServer; @@ -54,7 +53,7 @@ public class AppBeaconServer { port = BeaconConfig.getUsedPort(); propertyPort = true; } else { - port = XPipeInstallation.getDefaultBeaconPort(); + port = BeaconConfig.getDefaultBeaconPort(); propertyPort = false; } INSTANCE = new AppBeaconServer(port, propertyPort); @@ -120,7 +119,7 @@ public class AppBeaconServer { } private void initAuthSecret() throws IOException { - var file = XPipeInstallation.getLocalBeaconAuthFile(); + var file = BeaconConfig.getLocalBeaconAuthFile(); var id = UUID.randomUUID().toString(); Files.writeString(file, id); if (OsType.getLocal() != OsType.WINDOWS) { @@ -130,7 +129,7 @@ public class AppBeaconServer { } private void deleteAuthSecret() { - var file = XPipeInstallation.getLocalBeaconAuthFile(); + var file = BeaconConfig.getLocalBeaconAuthFile(); try { Files.delete(file); } catch (IOException ignored) { diff --git a/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileWithActionProvider.java b/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileWithActionProvider.java index 10c93efee..f6f94eedf 100644 --- a/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileWithActionProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileWithActionProvider.java @@ -34,7 +34,7 @@ public class OpenFileWithActionProvider implements BrowserActionProvider { @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - return OsType.getLocal().equals(OsType.WINDOWS) + return OsType.getLocal() == OsType.WINDOWS && entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE); } diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemHelper.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemHelper.java index 4fdf8effd..f6a69f945 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemHelper.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemHelper.java @@ -35,7 +35,7 @@ public class BrowserFileSystemHelper { return path; } - if (shell.get().getOsType().equals(OsType.WINDOWS) && path.length() == 2 && path.endsWith(":")) { + if (shell.get().getOsType() == OsType.WINDOWS && path.length() == 2 && path.endsWith(":")) { return path + "\\"; } diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenFileWithMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenFileWithMenuProvider.java index 2dd143d49..af698500b 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenFileWithMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenFileWithMenuProvider.java @@ -47,7 +47,7 @@ public class OpenFileWithMenuProvider implements BrowserMenuLeafProvider { @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - return OsType.getLocal().equals(OsType.WINDOWS) + return OsType.getLocal() == OsType.WINDOWS && entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE); } diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/RunFileMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/RunFileMenuProvider.java index 99b382f32..787c7be91 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/RunFileMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/RunFileMenuProvider.java @@ -35,7 +35,7 @@ public class RunFileMenuProvider extends MultiExecuteMenuProvider { } var os = shell.get().getOsType(); - if (os.equals(OsType.WINDOWS) + if (os == OsType.WINDOWS && Stream.of("exe", "bat", "ps1", "cmd") .anyMatch(s -> e.getPath().toString().endsWith(s))) { return true; diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java index 2892479eb..90985893f 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java @@ -66,6 +66,6 @@ public abstract class BaseUnzipUnixMenuProvider implements BrowserMenuLeafProvid return entries.stream() .allMatch(entry -> entry.getRawFileEntry().getPath().toString().endsWith(".zip")) - && !model.getFileSystem().getShell().orElseThrow().getOsType().equals(OsType.WINDOWS); + && model.getFileSystem().getShell().orElseThrow().getOsType() != OsType.WINDOWS; } } diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java index 5d353c825..4bb9d2e17 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java @@ -60,6 +60,6 @@ public abstract class BaseUnzipWindowsActionProvider implements BrowserMenuLeafP return entries.stream() .allMatch(entry -> entry.getRawFileEntry().getPath().toString().endsWith(".zip")) - && model.getFileSystem().getShell().orElseThrow().getOsType().equals(OsType.WINDOWS); + && model.getFileSystem().getShell().orElseThrow().getOsType() == OsType.WINDOWS; } } diff --git a/app/src/main/java/io/xpipe/app/core/AppDesktopIntegration.java b/app/src/main/java/io/xpipe/app/core/AppDesktopIntegration.java index d15ed0c1d..9b742ee37 100644 --- a/app/src/main/java/io/xpipe/app/core/AppDesktopIntegration.java +++ b/app/src/main/java/io/xpipe/app/core/AppDesktopIntegration.java @@ -44,7 +44,7 @@ public class AppDesktopIntegration { // This will initialize the toolkit on macOS and create the dock icon // macOS does not like applications that run fully in the background, so always do it - if (OsType.getLocal().equals(OsType.MACOS) && Desktop.isDesktopSupported()) { + if (OsType.getLocal() == OsType.MACOS && Desktop.isDesktopSupported()) { Desktop.getDesktop().setPreferencesHandler(e -> { if (PlatformState.getCurrent() != PlatformState.RUNNING) { return; diff --git a/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java b/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java index b03fc6944..0b49eab1a 100644 --- a/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java +++ b/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java @@ -6,8 +6,6 @@ import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.util.ModuleAccess; import io.xpipe.core.ModuleLayerLoader; -import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import lombok.Getter; @@ -70,15 +68,16 @@ public class AppExtensionManager { private void determineExtensionDirectories() throws Exception { if (!AppProperties.get().isFullVersion()) { - var localInstallation = XPipeInstallation.getLocalDefaultInstallationBasePath( - AppProperties.get().isStaging() || AppProperties.get().isLocatePtb()); - Path p = localInstallation; + var localInstallation = !AppProperties.get().isStaging() && AppProperties.get().isLocatePtb() ? + AppInstallation.ofDefault(true) + : AppInstallation.ofCurrent(); + Path p = localInstallation.getBaseInstallationPath(); if (!Files.exists(p)) { throw new IllegalStateException( "Required local XPipe installation was not found but is required for development. See https://github.com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup"); } - var iv = getLocalInstallVersion(); + var iv = getLocalInstallVersion(localInstallation); var installVersion = AppVersion.parse(iv) .orElseThrow(() -> new IllegalArgumentException("Invalid installation version: " + iv)); var sv = !AppProperties.get().isImage() @@ -92,14 +91,13 @@ public class AppExtensionManager { + "\n\nPlease try to check out the matching release version in the repository. See https://github.com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup"); } - var extensions = XPipeInstallation.getLocalExtensionsDirectory(p); + var extensions = localInstallation.getExtensionsPath(); extensionBaseDirectories.add(extensions); } } - private static String getLocalInstallVersion() throws Exception { - var localInstallation = XPipeInstallation.getLocalDefaultInstallationBasePath(); - var exec = localInstallation.resolve(XPipeInstallation.getDaemonExecutablePath(OsType.getLocal())); + private static String getLocalInstallVersion(AppInstallation localInstallation) throws Exception { + var exec = localInstallation.getDaemonExecutablePath(); var fc = new ProcessBuilder(exec.toString(), "version").redirectError(ProcessBuilder.Redirect.DISCARD); var proc = fc.start(); var out = new String(proc.getInputStream().readAllBytes()); diff --git a/app/src/main/java/io/xpipe/app/core/AppI18nData.java b/app/src/main/java/io/xpipe/app/core/AppI18nData.java index 8d7549448..a2cb53192 100644 --- a/app/src/main/java/io/xpipe/app/core/AppI18nData.java +++ b/app/src/main/java/io/xpipe/app/core/AppI18nData.java @@ -3,7 +3,6 @@ package io.xpipe.app.core; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.prefs.SupportedLocale; -import io.xpipe.core.XPipeInstallation; import lombok.Value; import org.apache.commons.io.FilenameUtils; @@ -59,7 +58,7 @@ public class AppI18nData { var translations = new HashMap(); { - var basePath = XPipeInstallation.getLangPath().resolve("strings"); + var basePath = AppInstallation.ofCurrent().getLangPath().resolve("strings"); AtomicInteger fileCounter = new AtomicInteger(); AtomicInteger lineCounter = new AtomicInteger(); Files.walkFileTree(basePath, new SimpleFileVisitor<>() { @@ -94,7 +93,7 @@ public class AppI18nData { var markdownDocumentations = new HashMap(); { - var basePath = XPipeInstallation.getLangPath().resolve("texts"); + var basePath = AppInstallation.ofCurrent().getLangPath().resolve("texts"); Files.walkFileTree(basePath, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { diff --git a/app/src/main/java/io/xpipe/app/core/AppInstallation.java b/app/src/main/java/io/xpipe/app/core/AppInstallation.java new file mode 100644 index 000000000..3c9c3d601 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/core/AppInstallation.java @@ -0,0 +1,312 @@ +package io.xpipe.app.core; + +import io.xpipe.core.OsType; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public abstract class AppInstallation { + + private static final Windows WINDOWS = new Windows(determineCurrentInstallationBasePath(AppProperties.get().isStaging())); + private static final Linux LINUX = new Linux(determineCurrentInstallationBasePath(AppProperties.get().isStaging())); + private static final MacOs MACOS = new MacOs(determineCurrentInstallationBasePath(AppProperties.get().isStaging())); + + private AppInstallation(Path base) {this.base = base;} + + public static AppInstallation ofCurrent() { + return switch (OsType.getLocal()) { + case OsType.Windows ignored -> WINDOWS; + case OsType.Linux ignored -> LINUX; + case OsType.MacOs ignored -> MACOS; + default -> throw new IllegalStateException(); + }; + } + + public static AppInstallation ofDefault() { + return ofDefault(AppProperties.get().isStaging()); + } + + public static AppInstallation ofDefault(boolean stage) { + var def = determineDefaultInstallationBasePath(stage); + return switch (OsType.getLocal()) { + case OsType.Windows ignored -> new Windows(def); + case OsType.Linux ignored -> new Linux(def); + case OsType.MacOs ignored -> new MacOs(def); + default -> throw new IllegalStateException(); + }; + } + + private static Path determineDefaultInstallationBasePath(boolean stage) { + return switch (OsType.getLocal()) { + case OsType.Linux linux -> { + yield Path.of(stage ? "/opt/xpipe-ptb" : "/opt/xpipe"); + } + case OsType.MacOs macOs -> { + yield Path.of(stage ? "/Applications/XPipe PTB.app" : "/Applications/XPipe.app"); + } + case OsType.Windows windows -> { + var pg = AppLocations.getWindows().getProgramFiles(); + var systemPath = pg.resolve(stage ? "XPipe PTB" : "XPipe"); + if (Files.exists(systemPath)) { + yield systemPath; + } + + var ad = AppLocations.getWindows().getLocalAppData(); + yield ad.resolve(stage ? "XPipe PTB" : "XPipe"); + } + }; + } + + private static Path determineCurrentInstallationBasePath(boolean stage) { + var command = ProcessHandle.current().info().command(); + // We should always have a command associated with the current process, otherwise something went seriously wrong + if (command.isEmpty()) { + var javaHome = System.getProperty("java.home"); + var javaExec = toRealPathIfPossible(Path.of(javaHome, "bin", "java")); + var path = getInstallationBasePathForJavaExecutable(javaExec); + return path; + } + + // Resolve any possible links to a real path + Path path = toRealPathIfPossible(Path.of(command.get())); + // Check if the process was started using a relative path, and adapt it if necessary + if (!path.isAbsolute()) { + path = toRealPathIfPossible(Path.of(System.getProperty("user.dir")).resolve(path)); + } + + var name = path.getFileName().toString(); + // Check if we launched the JVM via a start script instead of the native executable + if (name.endsWith("java") || name.endsWith("java.exe")) { + // If we are not an image, we are probably running in a development environment where we want to use the + // working directory + var isImage = AppProperties.get().isImage(); + if (!isImage) { + return Path.of(System.getProperty("user.dir")); + } + return getInstallationBasePathForJavaExecutable(path); + } else { + return getInstallationBasePathForDaemonExecutable(path); + } + } + + private static Path getInstallationBasePathForDaemonExecutable(Path executable) { + // Resolve root path of installation relative to executable in a JPackage installation + return switch (OsType.getLocal()) { + case OsType.Linux linux -> { + yield executable.getParent().getParent(); + } + case OsType.MacOs macOs -> { + yield executable.getParent().getParent().getParent(); + } + case OsType.Windows windows -> { + yield executable.getParent(); + } + }; + } + + private static Path getInstallationBasePathForJavaExecutable(Path executable) { + // Resolve root path of installation relative to executable in a JPackage installation + return switch (OsType.getLocal()) { + case OsType.Linux linux -> { + yield executable.getParent().getParent().getParent().getParent(); + } + case OsType.MacOs macOs -> { + yield executable + .getParent() + .getParent() + .getParent() + .getParent() + .getParent() + .getParent(); + } + case OsType.Windows windows -> { + yield executable.getParent().getParent(); + } + }; + } + + private final Path base; + + public Path getBaseInstallationPath() { + return base; + } + + private static Path toRealPathIfPossible(Path p) { + try { + // Under certain conditions, e.g. when running on a ramdisk, path resolution might fail. + // This is however not a big problem in that case, so we ignore it + return p.toRealPath(); + } catch (IOException e) { + return p; + } + } + + public abstract Path getDaemonDebugScriptPath(); + + public abstract Path getBundledFontsPath(); + + public abstract Path getLangPath(); + + public abstract Path getCliExecutablePath(); + + public abstract Path getDaemonExecutablePath(); + + public abstract Path getExtensionsPath(); + + public abstract Path getLogoPath(); + + public static class Windows extends AppInstallation { + + private Windows(Path base) { + super(base); + } + + @Override + public Path getDaemonDebugScriptPath() { + return getBaseInstallationPath().resolve("scripts", "xpiped_debug.bat"); + } + + @Override + public Path getBundledFontsPath() { + if (!AppProperties.get().isImage()) { + return getBaseInstallationPath().resolve("dist", "fonts"); + } + + return getBaseInstallationPath().resolve("fonts"); + } + + @Override + public Path getLangPath() { + return getBaseInstallationPath().resolve("lang"); + } + + @Override + public Path getCliExecutablePath() { + return getBaseInstallationPath().resolve("bin", "xpipe.exe"); + } + + @Override + public Path getDaemonExecutablePath() { + return getBaseInstallationPath().resolve("xpiped.exe"); + } + + @Override + public Path getExtensionsPath() { + return getBaseInstallationPath().resolve("extensions"); + } + + @Override + public Path getLogoPath() { + if (!AppProperties.get().isImage()) { + return getBaseInstallationPath().resolve("dist").resolve("logo").resolve("logo.ico"); + } + + return getBaseInstallationPath().resolve("logo.ico"); + } + } + + public static class Linux extends AppInstallation { + + private Linux(Path base) { + super(base); + } + + @Override + public Path getDaemonDebugScriptPath() { + return getBaseInstallationPath().resolve("scripts", "xpiped_debug.sh"); + } + + @Override + public Path getBundledFontsPath() { + if (!AppProperties.get().isImage()) { + return getBaseInstallationPath().resolve("dist", "fonts"); + } + + return getBaseInstallationPath().resolve("fonts"); + } + + @Override + public Path getLangPath() { + return getBaseInstallationPath().resolve("lang"); + } + + @Override + public Path getCliExecutablePath() { + return getBaseInstallationPath().resolve("bin", "xpipe"); + } + + @Override + public Path getDaemonExecutablePath() { + return getBaseInstallationPath().resolve("bin", "xpiped"); + } + + @Override + public Path getExtensionsPath() { + return getBaseInstallationPath().resolve("extensions"); + } + + @Override + public Path getLogoPath() { + if (!AppProperties.get().isImage()) { + return getBaseInstallationPath().resolve("dist").resolve("logo").resolve("logo.png"); + } + + return getBaseInstallationPath().resolve("logo.png"); + } + } + + public static class MacOs extends AppInstallation { + + private MacOs(Path base) { + super(base); + } + + @Override + public Path getDaemonDebugScriptPath() { + return getBaseInstallationPath().resolve("Contents", "Resources", "scripts", "xpiped_debug.sh"); + } + + @Override + public Path getBundledFontsPath() { + if (!AppProperties.get().isImage()) { + return getBaseInstallationPath().resolve("dist", "fonts"); + } + + return getBaseInstallationPath().resolve("Contents", "Resources", "fonts"); + } + + @Override + public Path getLangPath() { + if (!AppProperties.get().isImage()) { + return getBaseInstallationPath().resolve("lang"); + } + + return getBaseInstallationPath().resolve("Contents", "Resources", "lang"); + } + + @Override + public Path getCliExecutablePath() { + return getBaseInstallationPath().resolve("Contents", "MacOS", "xpipe"); + } + + @Override + public Path getDaemonExecutablePath() { + return getBaseInstallationPath().resolve("Contents", "MacOS", "xpiped"); + } + + @Override + public Path getExtensionsPath() { + return getBaseInstallationPath().resolve("Contents", "Resources", "extensions"); + } + + @Override + public Path getLogoPath() { + if (!AppProperties.get().isImage()) { + return getBaseInstallationPath().resolve("dist").resolve("logo").resolve("logo.icns"); + } + + return getBaseInstallationPath().resolve("Contents").resolve("Resources").resolve("xpipe.icns"); + } + } +} diff --git a/app/src/main/java/io/xpipe/app/core/AppInstance.java b/app/src/main/java/io/xpipe/app/core/AppInstance.java index ea340007b..559a90150 100644 --- a/app/src/main/java/io/xpipe/app/core/AppInstance.java +++ b/app/src/main/java/io/xpipe/app/core/AppInstance.java @@ -11,7 +11,6 @@ import io.xpipe.beacon.BeaconServer; import io.xpipe.beacon.api.DaemonFocusExchange; import io.xpipe.beacon.api.DaemonOpenExchange; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import java.awt.*; import java.util.List; @@ -82,7 +81,7 @@ public class AppInstance { return; } - var cli = XPipeInstallation.getLocalDefaultCliExecutable(); + var cli = AppInstallation.ofCurrent().getCliExecutablePath(); ErrorEventFactory.fromThrowable( "Unable to connect to existing running daemon instance as it did not respond." + " Either try to kill the process xpiped manually or use the command \"" @@ -94,7 +93,7 @@ public class AppInstance { .handle(); } - if (OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.MACOS) { Desktop.getDesktop().setOpenURIHandler(e -> { try { client.get() diff --git a/app/src/main/java/io/xpipe/app/core/AppLocations.java b/app/src/main/java/io/xpipe/app/core/AppLocations.java new file mode 100644 index 000000000..32f7a1c64 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/core/AppLocations.java @@ -0,0 +1,97 @@ +package io.xpipe.app.core; + +import java.nio.file.InvalidPathException; +import java.nio.file.Path; + +public interface AppLocations { + + Windows WINDOWS = new Windows(); + Linux LINUX = new Linux(); + MacOs MACOS = new MacOs(); + + static Windows getWindows() { + return WINDOWS; + } + + static Linux getLinux() { + return LINUX; + } + + static MacOs getMacOs() { + return MACOS; + } + + private static Path parsePath(String path) { + if (path == null) { + return null; + } + + try { + return Path.of(path); + } catch (InvalidPathException ignored) { + return null; + } + } + + final class Windows implements AppLocations { + + private Path userHome; + + public Path getProgramFiles() { + var env = AppLocations.parsePath(System.getenv("ProgramFiles")); + if (env != null) { + return env; + } + + var def = Path.of("C:\\ProgramFiles"); + return def; + } + + public Path getLocalAppData() { + var env = AppLocations.parsePath(System.getenv("LOCALAPPDATA")); + if (env != null) { + return env; + } + + var def = getUserHome().resolve("AppData").resolve("Local"); + return def; + } + + public Path getUserHome() { + if (userHome != null) { + return userHome; + } + + var dir = AppLocations.parsePath(System.getenv("USERPROFILE")); + if (dir == null) { + dir = AppLocations.parsePath(System.getProperty("user.home")); + } + if (dir == null) { + var username = System.getenv("USERNAME"); + if (username == null) { + username = System.getProperty("user.name"); + } + if (username == null) { + username = "User"; + } + dir = Path.of("C:\\Users\\" + username); + } + + try { + // Replace 8.3 filename + userHome = dir.toRealPath(); + } catch (Exception ignored) { + userHome = dir; + } + return dir; + } + } + + class Linux implements AppLocations { + + } + + final class MacOs implements AppLocations { + + } +} diff --git a/app/src/main/java/io/xpipe/app/core/AppRestart.java b/app/src/main/java/io/xpipe/app/core/AppRestart.java index cc7704f17..50f38ccec 100644 --- a/app/src/main/java/io/xpipe/app/core/AppRestart.java +++ b/app/src/main/java/io/xpipe/app/core/AppRestart.java @@ -6,7 +6,6 @@ import io.xpipe.app.process.ShellDialect; import io.xpipe.app.process.ShellDialects; import io.xpipe.app.util.LocalShell; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import java.util.List; @@ -14,17 +13,17 @@ public class AppRestart { private static String createTerminalLaunchCommand(List arguments, ShellDialect dialect) { var loc = AppProperties.get().isDevelopmentEnvironment() - ? XPipeInstallation.getLocalDefaultInstallationBasePath() - : XPipeInstallation.getCurrentInstallationBasePath(); + ? AppInstallation.ofDefault() + : AppInstallation.ofCurrent(); var suffix = (arguments.size() > 0 ? " " + String.join(" ", arguments) : ""); - if (OsType.getLocal().equals(OsType.LINUX)) { - var exec = loc.resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.getLocal())); + if (OsType.getLocal() == OsType.LINUX) { + var exec = loc.getCliExecutablePath(); return "\"" + exec + "\" open" + suffix; - } else if (OsType.getLocal().equals(OsType.MACOS)) { - var exec = loc.resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.getLocal())); + } else if (OsType.getLocal() == OsType.MACOS) { + var exec = loc.getCliExecutablePath(); return "\"" + exec + "\" open" + suffix; } else { - var exe = loc.resolve(XPipeInstallation.getDaemonExecutablePath(OsType.getLocal())); + var exe = loc.getDaemonDebugScriptPath(); if (ShellDialects.isPowershell(dialect)) { var escapedList = arguments.stream().map(s -> s.replaceAll("\"", "`\"")).toList(); @@ -39,15 +38,15 @@ public class AppRestart { private static String createBackgroundLaunchCommand(List arguments, ShellDialect dialect) { var loc = AppProperties.get().isDevelopmentEnvironment() - ? XPipeInstallation.getLocalDefaultInstallationBasePath() - : XPipeInstallation.getCurrentInstallationBasePath(); + ? AppInstallation.ofDefault() + : AppInstallation.ofCurrent(); var suffix = (arguments.size() > 0 ? " " + String.join(" ", arguments) : ""); - if (OsType.getLocal().equals(OsType.LINUX)) { - return "nohup \"" + loc + "/bin/xpiped\"" + suffix + " /dev/null 2>&1 & disown"; - } else if (OsType.getLocal().equals(OsType.MACOS)) { - return "(sleep 1;open \"" + loc + "\" --args" + suffix + " /dev/null) & disown"; + if (OsType.getLocal() == OsType.LINUX) { + return "nohup \"" + loc.getDaemonExecutablePath() + "\"" + suffix + " /dev/null 2>&1 & disown"; + } else if (OsType.getLocal() == OsType.MACOS) { + return "(sleep 1;open \"" + loc.getBaseInstallationPath() + "\" --args" + suffix + " /dev/null) & disown"; } else { - var exe = loc.resolve(XPipeInstallation.getDaemonExecutablePath(OsType.getLocal())); + var exe = loc.getDaemonExecutablePath(); if (ShellDialects.isPowershell(dialect)) { var escapedList = arguments.stream().map(s -> s.replaceAll("\"", "`\"")).toList(); diff --git a/app/src/main/java/io/xpipe/app/core/AppSid.java b/app/src/main/java/io/xpipe/app/core/AppSid.java index 928044fed..0363f9d1b 100644 --- a/app/src/main/java/io/xpipe/app/core/AppSid.java +++ b/app/src/main/java/io/xpipe/app/core/AppSid.java @@ -15,7 +15,7 @@ public class AppSid { private static boolean hasSetsid; public static void init() { - if (OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() == OsType.WINDOWS) { return; } diff --git a/app/src/main/java/io/xpipe/app/core/AppTrayIcon.java b/app/src/main/java/io/xpipe/app/core/AppTrayIcon.java index 2e18094ce..0f72a4fd2 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTrayIcon.java +++ b/app/src/main/java/io/xpipe/app/core/AppTrayIcon.java @@ -103,7 +103,7 @@ public class AppTrayIcon { } public void showErrorMessage(String title, String message) { - if (OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.MACOS) { showMacAlert(title, message, "Error"); } else { EventQueue.invokeLater(() -> this.trayIcon.displayMessage(title, message, TrayIcon.MessageType.ERROR)); diff --git a/app/src/main/java/io/xpipe/app/core/check/AppCertutilCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppCertutilCheck.java index 93d192354..1faedc625 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppCertutilCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppCertutilCheck.java @@ -26,7 +26,7 @@ public class AppCertutilCheck { return; } - if (!OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() != OsType.WINDOWS) { return; } diff --git a/app/src/main/java/io/xpipe/app/core/check/AppHomebrewCoreutilsCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppHomebrewCoreutilsCheck.java index 153ddda69..e6a3f7ea3 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppHomebrewCoreutilsCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppHomebrewCoreutilsCheck.java @@ -24,7 +24,7 @@ public class AppHomebrewCoreutilsCheck { } public static void check() { - if (!OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() != OsType.MACOS) { return; } diff --git a/app/src/main/java/io/xpipe/app/core/check/AppSystemFontCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppSystemFontCheck.java index b23f21b29..cda317fbf 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppSystemFontCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppSystemFontCheck.java @@ -1,7 +1,7 @@ package io.xpipe.app.core.check; +import io.xpipe.app.core.AppInstallation; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import java.util.concurrent.TimeUnit; @@ -17,7 +17,7 @@ public class AppSystemFontCheck { } System.setProperty( - "prism.fontdir", XPipeInstallation.getBundledFontsPath().toString()); + "prism.fontdir", AppInstallation.ofCurrent().getBundledFontsPath().toString()); System.setProperty("prism.embeddedfonts", "true"); } diff --git a/app/src/main/java/io/xpipe/app/core/check/AppTempCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppTempCheck.java index e34379ec7..24f21cbfa 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppTempCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppTempCheck.java @@ -29,7 +29,7 @@ public class AppTempCheck { } public static void check() { - if (!OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() != OsType.WINDOWS) { return; } diff --git a/app/src/main/java/io/xpipe/app/core/check/AppTestCommandCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppTestCommandCheck.java index 99002242d..3a60685d0 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppTestCommandCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppTestCommandCheck.java @@ -1,14 +1,14 @@ package io.xpipe.app.core.check; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.process.ProcessOutputException; import io.xpipe.app.util.LocalShell; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; public class AppTestCommandCheck { public static void check() throws Exception { - if (OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() == OsType.WINDOWS) { return; } @@ -17,7 +17,7 @@ public class AppTestCommandCheck { sc.getShellDialect() .directoryExists( sc, - XPipeInstallation.getCurrentInstallationBasePath() + AppInstallation.ofCurrent().getBaseInstallationPath() .toString()) .execute(); } catch (ProcessOutputException ex) { diff --git a/app/src/main/java/io/xpipe/app/core/mode/TrayMode.java b/app/src/main/java/io/xpipe/app/core/mode/TrayMode.java index 35ed1856e..739be047c 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/TrayMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/TrayMode.java @@ -11,7 +11,7 @@ public class TrayMode extends PlatformMode { @Override public boolean isSupported() { - return OsType.getLocal().equals(OsType.WINDOWS) + return OsType.getLocal()== OsType.WINDOWS && super.isSupported() && Desktop.isDesktopSupported() && SystemTray.isSupported(); diff --git a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java index 0747b86f9..edd8ab362 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java @@ -298,7 +298,7 @@ public class AppMainWindow { } }); - if (OsType.getLocal().equals(OsType.LINUX) || OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.LINUX || OsType.getLocal() == OsType.MACOS) { stage.getScene().addEventHandler(KeyEvent.KEY_PRESSED, event -> { if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(event)) { OperationMode.onWindowClose(); diff --git a/app/src/main/java/io/xpipe/app/ext/ExtensionException.java b/app/src/main/java/io/xpipe/app/ext/ExtensionException.java index 0244b78b4..178dc7df9 100644 --- a/app/src/main/java/io/xpipe/app/ext/ExtensionException.java +++ b/app/src/main/java/io/xpipe/app/ext/ExtensionException.java @@ -1,6 +1,6 @@ package io.xpipe.app.ext; -import io.xpipe.core.XPipeInstallation; +import io.xpipe.app.core.AppInstallation; public class ExtensionException extends RuntimeException { @@ -24,7 +24,7 @@ public class ExtensionException extends RuntimeException { public static ExtensionException corrupt(String message, Throwable cause) { try { - var loc = XPipeInstallation.getCurrentInstallationBasePath(); + var loc = AppInstallation.ofCurrent().getBaseInstallationPath(); var full = message + ".\n\n" + "Please check whether the XPipe installation data at " + loc + " is corrupted."; return new ExtensionException(full, cause); diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java index bfa178362..9d6ab8563 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java @@ -80,7 +80,7 @@ public interface ExternalApplicationType extends PrefsValue { @Override default boolean isSelectable() { - return OsType.getLocal().equals(OsType.MACOS); + return OsType.getLocal() == OsType.MACOS; } } @@ -180,7 +180,7 @@ public interface ExternalApplicationType extends PrefsValue { @Override default boolean isSelectable() { - return OsType.getLocal().equals(OsType.WINDOWS); + return OsType.getLocal() == OsType.WINDOWS; } } } diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java index 0f6c145f9..a35683917 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java @@ -515,13 +515,13 @@ public interface ExternalEditorType extends PrefsChoiceValue { @SuppressWarnings("TrivialFunctionalExpressionUsage") List ALL = ((Supplier>) () -> { var all = new ArrayList(); - if (OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() == OsType.WINDOWS) { all.addAll(WINDOWS_EDITORS); } - if (OsType.getLocal().equals(OsType.LINUX)) { + if (OsType.getLocal() == OsType.LINUX) { all.addAll(LINUX_EDITORS); } - if (OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.MACOS) { all.addAll(MACOS_EDITORS); } all.addAll(CROSS_PLATFORM_EDITORS); @@ -536,21 +536,21 @@ public interface ExternalEditorType extends PrefsChoiceValue { return existing; } - if (OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() == OsType.WINDOWS) { return WINDOWS_EDITORS.stream() .filter(PrefsChoiceValue::isAvailable) .findFirst() .orElse(NOTEPAD); } - if (OsType.getLocal().equals(OsType.LINUX)) { + if (OsType.getLocal() == OsType.LINUX) { return LINUX_EDITORS.stream() .filter(ExternalApplicationType.PathApplication::isAvailable) .findFirst() .orElse(null); } - if (OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.MACOS) { return MACOS_EDITORS.stream() .filter(PrefsChoiceValue::isAvailable) .findFirst() @@ -655,7 +655,7 @@ public interface ExternalEditorType extends PrefsChoiceValue { @Override public boolean isSelectable() { - return OsType.getLocal().equals(OsType.LINUX); + return OsType.getLocal() == OsType.LINUX; } } diff --git a/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java b/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java index 618ea7c85..3ff583341 100644 --- a/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java @@ -4,6 +4,7 @@ import io.xpipe.app.comp.Comp; import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.comp.base.TileButtonComp; import io.xpipe.app.core.AppCache; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppLogs; import io.xpipe.app.core.AppProperties; import io.xpipe.app.core.mode.OperationMode; @@ -18,7 +19,6 @@ import io.xpipe.app.update.AppDistributionType; import io.xpipe.app.util.*; import io.xpipe.core.FilePath; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import com.sun.management.HotSpotDiagnosticMXBean; import lombok.SneakyThrows; @@ -61,10 +61,7 @@ public class TroubleshootCategory extends AppPrefsCategory { .addComp( new TileButtonComp("launchDebugMode", "launchDebugModeDescription", "mdmz-refresh", e -> { OperationMode.executeAfterShutdown(() -> { - var script = FilePath.of( - XPipeInstallation.getCurrentInstallationBasePath() - .toString(), - XPipeInstallation.getDaemonDebugScriptPath(OsType.getLocal())); + var script = AppInstallation.ofCurrent().getDaemonDebugScriptPath(); TerminalLaunch.builder() .title("XPipe Debug") .localScript(sc -> new ShellScript( @@ -98,8 +95,7 @@ public class TroubleshootCategory extends AppPrefsCategory { "openInstallationDirectoryDescription", "mdomz-snippet_folder", e -> { - DesktopHelper.browsePathLocal( - XPipeInstallation.getCurrentInstallationBasePath()); + DesktopHelper.browsePathLocal(AppInstallation.ofCurrent().getBaseInstallationPath()); e.consume(); }) .grow(true, false), @@ -165,7 +161,7 @@ public class TroubleshootCategory extends AppPrefsCategory { "uninstallApplicationDescription", "mdi2d-dump-truck", e -> { - var file = XPipeInstallation.getCurrentInstallationBasePath() + var file = AppInstallation.ofCurrent().getBaseInstallationPath() .resolve("Contents") .resolve("Resources") .resolve("scripts") diff --git a/app/src/main/java/io/xpipe/app/prefs/WorkspaceCreationDialog.java b/app/src/main/java/io/xpipe/app/prefs/WorkspaceCreationDialog.java index 962bd6a08..514e73bbb 100644 --- a/app/src/main/java/io/xpipe/app/prefs/WorkspaceCreationDialog.java +++ b/app/src/main/java/io/xpipe/app/prefs/WorkspaceCreationDialog.java @@ -3,12 +3,13 @@ package io.xpipe.app.prefs; import io.xpipe.app.comp.base.ModalButton; import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppFontSizes; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppProperties; import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.util.*; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; + import javafx.beans.property.SimpleObjectProperty; @@ -44,9 +45,7 @@ public class WorkspaceCreationDialog { var file = switch (OsType.getLocal()) { case OsType.Windows w -> { - var exec = XPipeInstallation.getCurrentInstallationBasePath() - .resolve(XPipeInstallation.getDaemonExecutablePath(w)) - .toString(); + var exec = AppInstallation.ofCurrent().getDaemonExecutablePath().toString(); yield DesktopShortcuts.create( exec, "-Dio.xpipe.app.dataDir=\"" @@ -54,8 +53,7 @@ public class WorkspaceCreationDialog { shortcutName); } default -> { - var exec = XPipeInstallation.getCurrentInstallationBasePath() - .resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.getLocal())) + var exec = AppInstallation.ofCurrent().getCliExecutablePath() .toString(); yield DesktopShortcuts.create( exec, diff --git a/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java b/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java index 96cbef3ff..a499e6e3d 100644 --- a/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java +++ b/app/src/main/java/io/xpipe/app/rdp/ExternalRdpClient.java @@ -41,13 +41,13 @@ public interface ExternalRdpClient extends PrefsChoiceValue { @SuppressWarnings("TrivialFunctionalExpressionUsage") List ALL = ((Supplier>) () -> { var all = new ArrayList(); - if (OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() == OsType.WINDOWS) { all.addAll(WINDOWS_CLIENTS); } - if (OsType.getLocal().equals(OsType.LINUX)) { + if (OsType.getLocal() == OsType.LINUX) { all.addAll(LINUX_CLIENTS); } - if (OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.MACOS) { all.addAll(MACOS_CLIENTS); } all.add(CUSTOM); diff --git a/app/src/main/java/io/xpipe/app/terminal/CmdTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/CmdTerminalType.java index aa23010b9..205a58621 100644 --- a/app/src/main/java/io/xpipe/app/terminal/CmdTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/CmdTerminalType.java @@ -35,7 +35,7 @@ public class CmdTerminalType } private CommandBuilder toCommand(TerminalLaunchConfiguration configuration) { - if (configuration.getScriptDialect().equals(ShellDialects.CMD)) { + if (configuration.getScriptDialect() == ShellDialects.CMD) { return CommandBuilder.of().add("/c").addFile(configuration.getScriptFile()); } diff --git a/app/src/main/java/io/xpipe/app/terminal/CustomTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/CustomTerminalType.java index c659e81f9..8a46d17f2 100644 --- a/app/src/main/java/io/xpipe/app/terminal/CustomTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/CustomTerminalType.java @@ -38,7 +38,7 @@ public class CustomTerminalType implements ExternalApplicationType, ExternalTerm var toExecute = ExternalApplicationHelper.replaceVariableArgument( format, "CMD", configuration.getScriptFile().toString()); // We can't be sure whether the command is blocking or not, so always make it not blocking - if (pc.getOsType().equals(OsType.WINDOWS)) { + if (pc.getOsType() == OsType.WINDOWS) { toExecute = "start \"" + configuration.getCleanTitle() + "\" " + toExecute; } else { toExecute = "nohup " + toExecute + " /dev/null & disown"; diff --git a/app/src/main/java/io/xpipe/app/terminal/ExternalTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/ExternalTerminalType.java index c099fd621..2f83fffcc 100644 --- a/app/src/main/java/io/xpipe/app/terminal/ExternalTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/ExternalTerminalType.java @@ -606,13 +606,13 @@ public interface ExternalTerminalType extends PrefsChoiceValue { static List getTypes(OsType osType, boolean custom) { var all = new ArrayList(); - if (osType == null || osType.equals(OsType.WINDOWS)) { + if (osType == null || osType == OsType.WINDOWS) { all.addAll(WINDOWS_TERMINALS); } - if (osType == null || osType.equals(OsType.LINUX)) { + if (osType == null || osType == OsType.LINUX) { all.addAll(LINUX_TERMINALS); } - if (osType == null || osType.equals(OsType.MACOS)) { + if (osType == null || osType == OsType.MACOS) { all.addAll(MACOS_TERMINALS); } // Prefer recommended @@ -626,7 +626,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue { static ExternalTerminalType determineDefault(ExternalTerminalType existing) { // Check for incompatibility with fallback shell if (ExternalTerminalType.CMD.equals(existing) - && !ProcessControlProvider.get().getEffectiveLocalDialect().equals(ShellDialects.CMD)) { + && ProcessControlProvider.get().getEffectiveLocalDialect() != ShellDialects.CMD) { return ExternalTerminalType.POWERSHELL; } diff --git a/app/src/main/java/io/xpipe/app/terminal/KittyTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/KittyTerminalType.java index 56bdb8f3f..8023b1d66 100644 --- a/app/src/main/java/io/xpipe/app/terminal/KittyTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/KittyTerminalType.java @@ -1,5 +1,6 @@ package io.xpipe.app.terminal; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.ext.ProcessControlProvider; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.prefs.ExternalApplicationType; @@ -11,7 +12,7 @@ import io.xpipe.app.util.LocalShell; import io.xpipe.app.util.ShellTemp; import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.FilePath; -import io.xpipe.core.XPipeInstallation; + import com.fasterxml.jackson.databind.node.JsonNodeFactory; @@ -38,7 +39,7 @@ public interface KittyTerminalType extends ExternalTerminalType, TrackableTermin payload.put("type", "tab"); payload.put("logo_alpha", 0.01); payload.put( - "logo", XPipeInstallation.getLocalDefaultInstallationIcon().toString()); + "logo", AppInstallation.ofCurrent().getLogoPath().toString()); var json = JsonNodeFactory.instance.objectNode(); json.put("cmd", "launch"); diff --git a/app/src/main/java/io/xpipe/app/terminal/PowerShellTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/PowerShellTerminalType.java index 760a08707..c92073c83 100644 --- a/app/src/main/java/io/xpipe/app/terminal/PowerShellTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/PowerShellTerminalType.java @@ -42,7 +42,7 @@ public class PowerShellTerminalType implements ExternalApplicationType.PathAppli } protected CommandBuilder toCommand(TerminalLaunchConfiguration configuration) { - if (configuration.getScriptDialect().equals(ShellDialects.POWERSHELL)) { + if (configuration.getScriptDialect() == ShellDialects.POWERSHELL) { return CommandBuilder.of() .add("-ExecutionPolicy", "Bypass") .add("-File") diff --git a/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java index dfe19e337..db0cd4483 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java @@ -66,7 +66,7 @@ public interface TabbyTerminalType extends ExternalTerminalType, TrackableTermin @Override public void launch(TerminalLaunchConfiguration configuration) throws Exception { // Tabby has a very weird handling of output, even detaching with start does not prevent it from printing - if (configuration.getScriptDialect().equals(ShellDialects.CMD)) { + if (configuration.getScriptDialect() == ShellDialects.CMD) { // It also freezes with any other input than .bat files, why? launch(CommandBuilder.of() .add("run") diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalLaunchConfiguration.java b/app/src/main/java/io/xpipe/app/terminal/TerminalLaunchConfiguration.java index 822a43754..fea49a69b 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalLaunchConfiguration.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalLaunchConfiguration.java @@ -1,5 +1,6 @@ package io.xpipe.app.terminal; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppProperties; import io.xpipe.app.ext.ProcessControlProvider; import io.xpipe.app.issue.ErrorEventFactory; @@ -17,7 +18,6 @@ import io.xpipe.app.util.LocalShell; import io.xpipe.app.util.ScriptHelper; import io.xpipe.core.FilePath; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import lombok.*; import lombok.experimental.NonFinal; @@ -127,7 +127,7 @@ public class TerminalLaunchConfiguration { var cliExecutable = TerminalProxyManager.getProxy() .orElse(LocalShell.getShell()) .getLocalSystemAccess() - .translateFromLocalSystemPath(FilePath.of(XPipeInstallation.getLocalDefaultCliExecutable())); + .translateFromLocalSystemPath(FilePath.of(AppInstallation.ofCurrent().getCliExecutablePath())); var scriptCommand = sc.getOsType() == OsType.MACOS || sc.getOsType() == OsType.BSD ? "script -e -q '%s' \"%s\"".formatted(logFile, command) : "script --quiet --command '%s' \"%s\"".formatted(command, logFile); diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java b/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java index 1ec67def3..2c65b1cb4 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java @@ -1,6 +1,7 @@ package io.xpipe.app.terminal; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.ext.ProcessControlProvider; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.prefs.AppPrefs; @@ -11,7 +12,7 @@ import io.xpipe.app.util.LocalShell; import io.xpipe.app.util.ScriptHelper; import io.xpipe.core.FailableFunction; import io.xpipe.core.FilePath; -import io.xpipe.core.XPipeInstallation; + import java.io.IOException; import java.util.List; @@ -192,7 +193,7 @@ public class TerminalLauncher { } private static String getTerminalRegisterCommand(UUID request) throws Exception { - var exec = XPipeInstallation.getLocalDefaultCliExecutable(); + var exec = AppInstallation.ofCurrent().getCliExecutablePath(); return CommandBuilder.of() .addFile(exec) .add("terminal-register", "--request", request.toString()) diff --git a/app/src/main/java/io/xpipe/app/terminal/WaveTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/WaveTerminalType.java index f82c676d9..45fba6353 100644 --- a/app/src/main/java/io/xpipe/app/terminal/WaveTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/WaveTerminalType.java @@ -1,10 +1,11 @@ package io.xpipe.app.terminal; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.process.CommandBuilder; import io.xpipe.app.util.CommandSupport; import io.xpipe.app.util.LocalShell; -import io.xpipe.core.XPipeInstallation; + public interface WaveTerminalType extends ExternalTerminalType, TrackableTerminalType { @@ -65,7 +66,7 @@ public interface WaveTerminalType extends ExternalTerminalType, TrackableTermina .formatted( inPath ? "xpipe open" - : XPipeInstallation.getLocalDefaultCliExecutable() + " open"); + : "\"" + AppInstallation.ofCurrent().getCliExecutablePath() + "\" open"); throw ErrorEventFactory.expected(new IllegalStateException(msg)); } diff --git a/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java index 726fe5b4a..9f3da9110 100644 --- a/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/WindowsTerminalType.java @@ -1,13 +1,14 @@ package io.xpipe.app.terminal; import io.xpipe.app.core.AppCache; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppProperties; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.process.CommandBuilder; import io.xpipe.app.util.LocalShell; import io.xpipe.core.FilePath; import io.xpipe.core.JacksonMapper; -import io.xpipe.core.XPipeInstallation; + import com.fasterxml.jackson.databind.node.JsonNodeFactory; @@ -91,8 +92,8 @@ public interface WindowsTerminalType extends ExternalTerminalType, TrackableTerm newProfile.put("suppressApplicationTitle", true); newProfile.put("elevate", false); if (!AppProperties.get().isDevelopmentEnvironment()) { - var dir = XPipeInstallation.getLocalDefaultInstallationIcon(); - newProfile.put("icon", dir.toString()); + var logoFile = AppInstallation.ofCurrent().getLogoPath(); + newProfile.put("icon", logoFile.toString()); } profiles.add(newProfile); JacksonMapper.getDefault().writeValue(getConfigFile().toFile(), config); diff --git a/app/src/main/java/io/xpipe/app/test/LocalExtensionTest.java b/app/src/main/java/io/xpipe/app/test/LocalExtensionTest.java index a8c25ffde..ba7b265dd 100644 --- a/app/src/main/java/io/xpipe/app/test/LocalExtensionTest.java +++ b/app/src/main/java/io/xpipe/app/test/LocalExtensionTest.java @@ -15,7 +15,7 @@ public class LocalExtensionTest extends ExtensionTest { return; } - var mode = OsType.getLocal().equals(OsType.WINDOWS) ? "tray" : "background"; + var mode = OsType.getLocal() == OsType.WINDOWS ? "tray" : "background"; OperationMode.init(new String[] {"-Dio.xpipe.app.mode=" + mode}); } } diff --git a/app/src/main/java/io/xpipe/app/update/AppDistributionType.java b/app/src/main/java/io/xpipe/app/update/AppDistributionType.java index 5fd6b64ef..fea904e33 100644 --- a/app/src/main/java/io/xpipe/app/update/AppDistributionType.java +++ b/app/src/main/java/io/xpipe/app/update/AppDistributionType.java @@ -7,7 +7,7 @@ import io.xpipe.app.process.ShellScript; import io.xpipe.app.util.LocalExec; import io.xpipe.app.util.Translatable; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; + import javafx.beans.value.ObservableValue; @@ -110,9 +110,7 @@ public enum AppDistributionType implements Translatable { private static boolean isDifferentDaemonExecutable() { var cached = AppCache.getNonNull("daemonExecutable", String.class, () -> null); - var current = XPipeInstallation.getCurrentInstallationBasePath() - .resolve(XPipeInstallation.getDaemonExecutablePath(OsType.getLocal())) - .toString(); + var current = AppInstallation.ofCurrent().getDaemonExecutablePath().toString(); if (current.equals(cached)) { return false; } @@ -130,9 +128,9 @@ public enum AppDistributionType implements Translatable { } public static AppDistributionType determine() { - var base = XPipeInstallation.getCurrentInstallationBasePath(); - if (OsType.getLocal().equals(OsType.MACOS)) { - if (!base.equals(XPipeInstallation.getLocalDefaultInstallationBasePath())) { + var base = AppInstallation.ofCurrent().getBaseInstallationPath(); + if (OsType.getLocal() == OsType.MACOS) { + if (!base.equals(AppInstallation.ofDefault().getBaseInstallationPath())) { return PORTABLE; } @@ -163,7 +161,7 @@ public enum AppDistributionType implements Translatable { return WEBTOP; } - if (OsType.getLocal().equals(OsType.WINDOWS) && !AppProperties.get().isStaging()) { + if (OsType.getLocal() == OsType.WINDOWS && !AppProperties.get().isStaging()) { var chocoOut = LocalExec.readStdoutIfPossible("choco", "list", "xpipe"); if (chocoOut.isPresent()) { if (chocoOut.get().contains("xpipe") @@ -182,7 +180,7 @@ public enum AppDistributionType implements Translatable { // } } - if (OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.MACOS) { var out = LocalExec.readStdoutIfPossible("/opt/homebrew/bin/brew", "list", "--casks", "--versions"); if (out.isPresent()) { if (out.get().lines().anyMatch(s -> { diff --git a/app/src/main/java/io/xpipe/app/update/AppInstaller.java b/app/src/main/java/io/xpipe/app/update/AppInstaller.java index 67a29071a..fb4ccd42b 100644 --- a/app/src/main/java/io/xpipe/app/update/AppInstaller.java +++ b/app/src/main/java/io/xpipe/app/update/AppInstaller.java @@ -1,5 +1,6 @@ package io.xpipe.app.update; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppLogs; import io.xpipe.app.core.AppRestart; import io.xpipe.app.core.mode.OperationMode; @@ -14,7 +15,7 @@ import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.FailableRunnable; import io.xpipe.core.FilePath; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; + import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -27,17 +28,17 @@ import java.nio.file.Path; public class AppInstaller { public static InstallerAssetType getSuitablePlatformAsset() { - if (OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() == OsType.WINDOWS) { return new InstallerAssetType.Msi(); } - if (OsType.getLocal().equals(OsType.LINUX)) { + if (OsType.getLocal() == OsType.LINUX) { return Files.exists(Path.of("/etc/debian_version")) ? new InstallerAssetType.Debian() : new InstallerAssetType.Rpm(); } - if (OsType.getLocal().equals(OsType.MACOS)) { + if (OsType.getLocal() == OsType.MACOS) { return new InstallerAssetType.Pkg(); } @@ -69,7 +70,7 @@ public class AppInstaller { FilePath.of(logsDir, "installer_" + file.getFileName().toString() + ".log"); var systemWide = isSystemWide(); var cmdScript = - ProcessControlProvider.get().getEffectiveLocalDialect().equals(ShellDialects.CMD) + ProcessControlProvider.get().getEffectiveLocalDialect() == ShellDialects.CMD && !systemWide; var command = cmdScript ? getCmdCommand(file.toString(), logFile.toString()) @@ -101,8 +102,7 @@ public class AppInstaller { } private boolean isSystemWide() { - return Files.exists( - XPipeInstallation.getCurrentInstallationBasePath().resolve("system")); + return Files.exists(AppInstallation.ofCurrent().getBaseInstallationPath().resolve("system")); } private String getCmdCommand(String file, String logFile) { diff --git a/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java b/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java index d66e73833..e54efeee4 100644 --- a/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java +++ b/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java @@ -2,6 +2,7 @@ package io.xpipe.app.update; import io.xpipe.app.comp.base.ModalButton; import io.xpipe.app.core.AppCache; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppProperties; import io.xpipe.app.core.AppRestart; import io.xpipe.app.core.mode.OperationMode; @@ -13,7 +14,7 @@ import io.xpipe.app.terminal.TerminalLaunch; import io.xpipe.app.terminal.TerminalLauncher; import io.xpipe.app.util.Hyperlinks; import io.xpipe.app.util.LocalShell; -import io.xpipe.core.XPipeInstallation; + import java.nio.file.Files; import java.time.Instant; @@ -120,8 +121,7 @@ public class ChocoUpdater extends UpdateHandler { var performedUpdate = new PerformedUpdate(p.getVersion(), p.getBody(), p.getVersion()); AppCache.update("performedUpdate", performedUpdate); OperationMode.executeAfterShutdown(() -> { - var systemWide = Files.exists( - XPipeInstallation.getCurrentInstallationBasePath().resolve("system")); + var systemWide = Files.exists(AppInstallation.ofCurrent().getBaseInstallationPath().resolve("system")); var propertiesArguments = systemWide ? ", --install-arguments=\"'ALLUSERS=1'\"" : ""; TerminalLaunch.builder().title("XPipe Updater").localScript(sc -> { var pkg = "xpipe"; diff --git a/app/src/main/java/io/xpipe/app/update/WingetUpdater.java b/app/src/main/java/io/xpipe/app/update/WingetUpdater.java index 16270e54e..e775b7c0d 100644 --- a/app/src/main/java/io/xpipe/app/update/WingetUpdater.java +++ b/app/src/main/java/io/xpipe/app/update/WingetUpdater.java @@ -2,6 +2,7 @@ package io.xpipe.app.update; import io.xpipe.app.comp.base.ModalButton; import io.xpipe.app.core.AppCache; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppProperties; import io.xpipe.app.core.AppRestart; import io.xpipe.app.core.mode.OperationMode; @@ -12,7 +13,7 @@ import io.xpipe.app.terminal.TerminalLaunch; import io.xpipe.app.terminal.TerminalLauncher; import io.xpipe.app.util.Hyperlinks; import io.xpipe.app.util.LocalShell; -import io.xpipe.core.XPipeInstallation; + import java.nio.file.Files; import java.time.Instant; @@ -121,8 +122,7 @@ public class WingetUpdater extends UpdateHandler { AppCache.update("performedUpdate", performedUpdate); OperationMode.executeAfterShutdown(() -> { TerminalLaunch.builder().title("XPipe Updater").localScript(sc -> { - var systemWide = Files.exists( - XPipeInstallation.getCurrentInstallationBasePath().resolve("system")); + var systemWide = Files.exists(AppInstallation.ofCurrent().getBaseInstallationPath().resolve("system")); var pkgId = "xpipe-io.xpipe"; if (systemWide) { return ShellScript.lines( diff --git a/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java b/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java index ecd369cdc..40254c9fa 100644 --- a/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java +++ b/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java @@ -1,10 +1,11 @@ package io.xpipe.app.util; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.process.CommandBuilder; import io.xpipe.app.process.OsFileSystem; import io.xpipe.core.FilePath; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; + import java.nio.file.Files; import java.nio.file.Path; @@ -20,7 +21,7 @@ public class DesktopShortcuts { return shortcutPath; } - var icon = XPipeInstallation.getLocalDefaultInstallationIcon(); + var icon = AppInstallation.ofCurrent().getLogoPath(); var content = String.format( """ $TARGET="%s" @@ -42,7 +43,7 @@ public class DesktopShortcuts { private static Path createLinuxShortcut(String executable, String args, String name) throws Exception { // Linux .desktop names are very restrictive var fixedName = name.replaceAll("[^\\w _]", ""); - var icon = XPipeInstallation.getLocalDefaultInstallationIcon(); + var icon = AppInstallation.ofCurrent().getLogoPath(); var content = String.format( """ [Desktop Entry] @@ -72,7 +73,7 @@ public class DesktopShortcuts { } private static Path createMacOSShortcut(String executable, String args, String name) throws Exception { - var icon = XPipeInstallation.getLocalDefaultInstallationIcon(); + var icon = AppInstallation.ofCurrent().getLogoPath(); var assets = icon.getParent().resolve("Assets.car"); var base = DesktopHelper.getDesktopDirectory().resolve(name + ".app"); var content = String.format( @@ -116,15 +117,15 @@ public class DesktopShortcuts { } public static Path createCliOpen(String action, String name) throws Exception { - var exec = XPipeInstallation.getLocalDefaultCliExecutable(); + var exec = AppInstallation.ofCurrent().getCliExecutablePath().toString(); return create(exec, "open " + action, name); } public static Path create(String executable, String args, String name) throws Exception { var compat = OsFileSystem.ofLocal().makeFileSystemCompatible(name); - if (OsType.getLocal().equals(OsType.WINDOWS)) { + if (OsType.getLocal() == OsType.WINDOWS) { return createWindowsShortcut(executable, args, compat); - } else if (OsType.getLocal().equals(OsType.LINUX)) { + } else if (OsType.getLocal() == OsType.LINUX) { return createLinuxShortcut(executable, args, compat); } else { return createMacOSShortcut(executable, args, compat); diff --git a/app/src/main/java/io/xpipe/app/util/FileOpener.java b/app/src/main/java/io/xpipe/app/util/FileOpener.java index da395e354..68851091c 100644 --- a/app/src/main/java/io/xpipe/app/util/FileOpener.java +++ b/app/src/main/java/io/xpipe/app/util/FileOpener.java @@ -74,14 +74,14 @@ public class FileOpener { public static void openInDefaultApplication(String localFile) { try (var pc = LocalShell.getShell().start()) { - if (pc.getOsType().equals(OsType.WINDOWS)) { + if (pc.getOsType() == OsType.WINDOWS) { if (pc.getShellDialect() == ShellDialects.POWERSHELL) { pc.command(CommandBuilder.of().add("Invoke-Item").addFile(localFile)) .execute(); } else { pc.executeSimpleCommand("start \"\" \"" + localFile + "\""); } - } else if (pc.getOsType().equals(OsType.LINUX)) { + } else if (pc.getOsType() == OsType.LINUX) { pc.executeSimpleCommand("xdg-open \"" + localFile + "\""); } else { pc.executeSimpleCommand("open \"" + localFile + "\""); diff --git a/app/src/main/java/io/xpipe/app/util/NativeBridge.java b/app/src/main/java/io/xpipe/app/util/NativeBridge.java index b53fd4a69..80a73b571 100644 --- a/app/src/main/java/io/xpipe/app/util/NativeBridge.java +++ b/app/src/main/java/io/xpipe/app/util/NativeBridge.java @@ -1,9 +1,9 @@ package io.xpipe.app.util; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppProperties; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.core.OsType; -import io.xpipe.core.XPipeInstallation; import com.sun.jna.Library; import com.sun.jna.Native; @@ -35,7 +35,7 @@ public class NativeBridge { try { System.setProperty( "jna.library.path", - XPipeInstallation.getCurrentInstallationBasePath() + AppInstallation.ofCurrent().getBaseInstallationPath() .resolve("Contents") .resolve("runtime") .resolve("Contents") diff --git a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java index 6301096b4..0f1a90248 100644 --- a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java +++ b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java @@ -65,7 +65,7 @@ public class ScriptHelper { // Fix for powershell as there are permission issues when executing a powershell askpass script if (forceExecutable && ShellDialects.isPowershell(parent)) { - scriptType = parent.getOsType().equals(OsType.WINDOWS) ? ShellDialects.CMD : ShellDialects.SH; + scriptType = parent.getOsType() == OsType.WINDOWS ? ShellDialects.CMD : ShellDialects.SH; } return createTerminalPreparedAskpassScript(pass, parent, scriptType); diff --git a/app/src/main/java/io/xpipe/app/util/ShellTemp.java b/app/src/main/java/io/xpipe/app/util/ShellTemp.java index 54a897fbe..1738f542c 100644 --- a/app/src/main/java/io/xpipe/app/util/ShellTemp.java +++ b/app/src/main/java/io/xpipe/app/util/ShellTemp.java @@ -19,7 +19,7 @@ public class ShellTemp { var temp = FileUtils.getTempDirectory().toPath().resolve("xpipe"); // On Windows and macOS, we already have user specific temp directories // Even on macOS as root we will have a unique directory (in contrast to shell controls) - if (OsType.getLocal().equals(OsType.LINUX)) { + if (OsType.getLocal() == OsType.LINUX) { var user = System.getenv("USER"); temp = temp.resolve(user != null ? user : "user"); @@ -39,7 +39,7 @@ public class ShellTemp { FilePath base; // On Windows and macOS, we already have user specific temp directories // Even on macOS as root it is technically unique as only root will use /tmp - if (!proc.getOsType().equals(OsType.WINDOWS) && !proc.getOsType().equals(OsType.MACOS)) { + if (proc.getOsType() != OsType.WINDOWS && proc.getOsType() != OsType.MACOS) { var temp = proc.getSystemTemporaryDirectory(); base = temp.join("xpipe"); proc.command(proc.getShellDialect().getMkdirsCommand(base.toString())) @@ -84,7 +84,7 @@ public class ShellTemp { } private static boolean checkDirectoryPermissions(ShellControl proc, String dir) throws Exception { - if (proc.getOsType().equals(OsType.WINDOWS)) { + if (proc.getOsType() == OsType.WINDOWS) { return true; } diff --git a/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java b/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java index b485bd1c3..7eaa2af21 100644 --- a/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java +++ b/app/src/main/java/io/xpipe/app/util/SshLocalBridge.java @@ -1,6 +1,7 @@ package io.xpipe.app.util; import io.xpipe.app.beacon.AppBeaconServer; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.core.AppProperties; import io.xpipe.app.ext.ProcessControlProvider; import io.xpipe.app.issue.ErrorEventFactory; @@ -8,7 +9,7 @@ import io.xpipe.app.process.CommandBuilder; import io.xpipe.app.process.ShellControl; import io.xpipe.app.process.ShellDialects; import io.xpipe.core.FilePath; -import io.xpipe.core.XPipeInstallation; + import lombok.Getter; import lombok.Setter; @@ -173,7 +174,7 @@ public class SshLocalBridge { } private String getRemoteCommand(ShellControl sc) { - var command = "\"" + XPipeInstallation.getLocalDefaultCliExecutable() + "\" ssh-launch " + var command = "\"" + AppInstallation.ofCurrent().getCliExecutablePath() + "\" ssh-launch " + sc.getShellDialect().environmentVariable("SSH_ORIGINAL_COMMAND"); var p = Pattern.compile("\".+?\\\\Users\\\\([^\\\\]+)\\\\(.+)\""); var matcher = p.matcher(command); diff --git a/app/src/main/java/io/xpipe/app/util/StoreStateFormat.java b/app/src/main/java/io/xpipe/app/util/StoreStateFormat.java index ce86d1944..717e878c4 100644 --- a/app/src/main/java/io/xpipe/app/util/StoreStateFormat.java +++ b/app/src/main/java/io/xpipe/app/util/StoreStateFormat.java @@ -50,7 +50,7 @@ public class StoreStateFormat { .format(); } - if (s.getShellDialect().equals(ShellDialects.NO_INTERACTION)) { + if (s.getShellDialect() == ShellDialects.NO_INTERACTION) { return new StoreStateFormat(null, null, info).format(); } diff --git a/beacon/README.md b/beacon/README.md index f6e316a92..56604a18a 100644 --- a/beacon/README.md +++ b/beacon/README.md @@ -29,26 +29,8 @@ For a full documentation, see the [OpenAPI spec](https://docs.xpipe.io/api) The default port can be changed by passing the property `io.xpipe.beacon.port=` to the daemon. Note that if both sides do not have the same port setting, they won't be able to reach each other. -#### Custom launch command - -The beacon API also supports launching the daemon automatically in case it is not started yet. -By default, it launches the daemon of the local XPipe installation. -It is possible to pass a custom launch command with the property `io.xpipe.beacon.customDaemonCommand=` -and pass arguments to it using the property `io.xpipe.beacon.daemonArgs=`. -This allows for a custom launch behaviour in a testing/development environment. -Note that the `` value has to be a single property string, which can be prone to formatting errors - #### Verbose output By passing the property `io.xpipe.beacon.printMessages=true`, it is possible to print debug information about the underlying communications. -In case the `io.xpipe.beacon.printDaemonOutput` property is set, the output of the daemon can also be -printed by passing the property `io.xpipe.beacon.debugExecOutput=true`. -#### Daemon debug mode - -In case the daemon is started by the beacon, it is possible to customize in which mode the daemon will start up. -By passing the property `io.xpipe.beacon.launchDebugDaemon=true`, the daemon is started in debug mode, -i.e. will log more information and enable a few other options. -By passing the property `io.xpipe.beacon.attachDebuggerToDaemon=true`, it is possible to launch a daemon -in a mode where it is waiting to attach to a debugger first prior to starting up. diff --git a/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java b/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java index c84876f09..99c587f9b 100644 --- a/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java +++ b/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java @@ -2,7 +2,6 @@ package io.xpipe.beacon; import io.xpipe.beacon.api.HandshakeExchange; import io.xpipe.core.JacksonMapper; -import io.xpipe.core.XPipeInstallation; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.SneakyThrows; @@ -26,7 +25,7 @@ public class BeaconClient { public static BeaconClient establishConnection(int port, BeaconClientInformation information) throws Exception { var client = new BeaconClient(port); - var auth = Files.readString(XPipeInstallation.getLocalBeaconAuthFile()); + var auth = Files.readString(BeaconConfig.getLocalBeaconAuthFile()); HandshakeExchange.Response response = client.performRequest(HandshakeExchange.Request.builder() .client(information) .auth(BeaconAuthMethod.Local.builder().authFileContent(auth).build()) diff --git a/beacon/src/main/java/io/xpipe/beacon/BeaconConfig.java b/beacon/src/main/java/io/xpipe/beacon/BeaconConfig.java index e56d03b59..8c3681f85 100644 --- a/beacon/src/main/java/io/xpipe/beacon/BeaconConfig.java +++ b/beacon/src/main/java/io/xpipe/beacon/BeaconConfig.java @@ -1,19 +1,16 @@ package io.xpipe.beacon; -import io.xpipe.core.XPipeInstallation; - import lombok.experimental.UtilityClass; +import java.nio.file.Path; +import java.util.Optional; + @UtilityClass public class BeaconConfig { public static final String BEACON_PORT_PROP = "io.xpipe.beacon.port"; public static final String DAEMON_ARGUMENTS_PROP = "io.xpipe.beacon.daemonArgs"; private static final String PRINT_MESSAGES_PROPERTY = "io.xpipe.beacon.printMessages"; - private static final String LAUNCH_DAEMON_IN_DEBUG_PROP = "io.xpipe.beacon.launchDebugDaemon"; - private static final String ATTACH_DEBUGGER_PROP = "io.xpipe.beacon.attachDebuggerToDaemon"; - private static final String EXEC_DEBUG_PROP = "io.xpipe.beacon.printDaemonOutput"; - private static final String EXEC_PROCESS_PROP = "io.xpipe.beacon.customDaemonCommand"; public static boolean printMessages() { if (System.getProperty(PRINT_MESSAGES_PROPERTY) != null) { @@ -22,27 +19,6 @@ public class BeaconConfig { return false; } - public static boolean launchDaemonInDebugMode() { - if (System.getProperty(LAUNCH_DAEMON_IN_DEBUG_PROP) != null) { - return Boolean.parseBoolean(System.getProperty(LAUNCH_DAEMON_IN_DEBUG_PROP)); - } - return false; - } - - public static boolean attachDebuggerToDaemon() { - if (System.getProperty(ATTACH_DEBUGGER_PROP) != null) { - return Boolean.parseBoolean(System.getProperty(ATTACH_DEBUGGER_PROP)); - } - return false; - } - - public static boolean printDaemonOutput() { - if (System.getProperty(EXEC_DEBUG_PROP) != null) { - return Boolean.parseBoolean(System.getProperty(EXEC_DEBUG_PROP)); - } - return false; - } - public static int getUsedPort() { var beaconPort = System.getenv("BEACON_PORT"); if (beaconPort != null && !beaconPort.isBlank()) { @@ -53,22 +29,21 @@ public class BeaconConfig { return Integer.parseInt(System.getProperty(BEACON_PORT_PROP)); } - return XPipeInstallation.getDefaultBeaconPort(); + return getDefaultBeaconPort(); } - public static String getCustomDaemonCommand() { - if (System.getProperty(EXEC_PROCESS_PROP) != null) { - return System.getProperty(EXEC_PROCESS_PROP); - } - - return null; + public static int getDefaultBeaconPort() { + var staging = Optional.ofNullable(System.getProperty("io.xpipe.app.staging")) + .map(Boolean::parseBoolean) + .orElse(false); + var offset = staging ? 1 : 0; + return 21721 + offset; } - public static String getDaemonArguments() { - if (System.getProperty(DAEMON_ARGUMENTS_PROP) != null) { - return System.getProperty(DAEMON_ARGUMENTS_PROP); - } - - return null; + public static Path getLocalBeaconAuthFile() { + var staging = Optional.ofNullable(System.getProperty("io.xpipe.app.staging")) + .map(Boolean::parseBoolean) + .orElse(false); + return Path.of(System.getProperty("java.io.tmpdir"), staging ? "xpipe_ptb_auth" : "xpipe_auth"); } } diff --git a/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java b/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java index 6948a3d60..0df9a050d 100644 --- a/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java +++ b/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java @@ -1,10 +1,6 @@ package io.xpipe.beacon; import io.xpipe.beacon.api.DaemonStopExchange; -import io.xpipe.core.FilePath; -import io.xpipe.core.OsType; -import io.xpipe.core.XPipeDaemonMode; -import io.xpipe.core.XPipeInstallation; import lombok.SneakyThrows; @@ -14,11 +10,10 @@ import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.nio.file.Path; import java.util.List; +import java.util.Optional; -/** - * Contains basic functionality to start, communicate, and stop a remote beacon server. - */ public class BeaconServer { @SneakyThrows @@ -42,109 +37,9 @@ public class BeaconServer { } } - private static List toProcessCommand(String toExec) { - // Having the trailing space is very important to force cmd to not interpret surrounding spaces and removing - // them - return OsType.getLocal().equals(OsType.WINDOWS) - ? List.of("cmd", "/c", toExec + " ") - : List.of("sh", "-c", toExec); - } - - public static Process tryStartCustom() throws Exception { - var custom = BeaconConfig.getCustomDaemonCommand(); - if (custom != null) { - var toExec = - custom + (BeaconConfig.getDaemonArguments() != null ? " " + BeaconConfig.getDaemonArguments() : ""); - var command = toProcessCommand(toExec); - Process process = Runtime.getRuntime().exec(command.toArray(String[]::new)); - printDaemonOutput(process, command); - return process; - } - return null; - } - - public static Process start(String installationBase, XPipeDaemonMode mode) throws Exception { - String command; - if (!BeaconConfig.launchDaemonInDebugMode()) { - command = XPipeInstallation.createExternalAsyncLaunchCommand( - installationBase, mode, BeaconConfig.getDaemonArguments(), false); - } else { - command = XPipeInstallation.createExternalLaunchCommand( - getDaemonDebugExecutable(installationBase), BeaconConfig.getDaemonArguments(), mode); - } - - var fullCommand = toProcessCommand(command); - Process process = new ProcessBuilder(fullCommand).start(); - printDaemonOutput(process, fullCommand); - return process; - } - - private static void printDaemonOutput(Process proc, List command) { - boolean print = BeaconConfig.printDaemonOutput(); - if (print) { - System.out.println("Starting daemon: " + command); - } - - var out = new Thread( - null, - () -> { - try { - InputStreamReader isr = new InputStreamReader(proc.getInputStream()); - BufferedReader br = new BufferedReader(isr); - String line; - while ((line = br.readLine()) != null) { - if (print) { - System.out.println("[xpiped] " + line); - } - } - } catch (Exception ioe) { - ioe.printStackTrace(); - } - }, - "daemon sysout"); - out.setDaemon(true); - out.start(); - - var err = new Thread( - null, - () -> { - try { - InputStreamReader isr = new InputStreamReader(proc.getErrorStream()); - BufferedReader br = new BufferedReader(isr); - String line; - while ((line = br.readLine()) != null) { - if (print) { - System.err.println("[xpiped] " + line); - } - } - } catch (Exception ioe) { - ioe.printStackTrace(); - } - }, - "daemon syserr"); - err.setDaemon(true); - err.start(); - } - public static boolean tryStop(BeaconClient client) throws Exception { DaemonStopExchange.Response res = client.performRequest(DaemonStopExchange.Request.builder().build()); return res.isSuccess(); } - - public static String getDaemonDebugExecutable(String installationBase) { - var osType = OsType.getLocal(); - var debug = BeaconConfig.launchDaemonInDebugMode(); - if (!debug) { - throw new IllegalStateException(); - } else { - if (BeaconConfig.attachDebuggerToDaemon()) { - return FilePath.of(installationBase, XPipeInstallation.getDaemonDebugAttachScriptPath(osType)) - .toString(); - } else { - return FilePath.of(installationBase, XPipeInstallation.getDaemonDebugScriptPath(osType)) - .toString(); - } - } - } } diff --git a/beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonController.java b/beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonController.java deleted file mode 100644 index 68d67c4d0..000000000 --- a/beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonController.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.xpipe.beacon.test; - -import io.xpipe.beacon.BeaconClient; -import io.xpipe.beacon.BeaconClientInformation; -import io.xpipe.beacon.BeaconConfig; -import io.xpipe.beacon.BeaconServer; -import io.xpipe.core.XPipeDaemonMode; -import io.xpipe.core.XPipeInstallation; - -import java.io.IOException; - -public class BeaconDaemonController { - - private static boolean alreadyStarted; - - public static void start(XPipeDaemonMode mode) throws Exception { - if (BeaconServer.isReachable(BeaconConfig.getUsedPort())) { - alreadyStarted = true; - return; - } - - var custom = false; - Process process; - if ((process = BeaconServer.tryStartCustom()) != null) { - custom = true; - } else { - var defaultBase = XPipeInstallation.getLocalDefaultInstallationBasePath(); - process = BeaconServer.start(defaultBase.toString(), mode); - } - - waitForStartup(process, custom); - if (!BeaconServer.isReachable(BeaconConfig.getUsedPort())) { - throw new AssertionError(); - } - } - - public static void stop() throws Exception { - if (alreadyStarted) { - return; - } - - if (!BeaconServer.isReachable(BeaconConfig.getUsedPort())) { - return; - } - - var client = BeaconClient.establishConnection( - BeaconConfig.getUsedPort(), - BeaconClientInformation.Api.builder() - .name("Beacon daemon controller") - .build()); - if (!BeaconServer.tryStop(client)) { - throw new AssertionError(); - } - waitForShutdown(); - } - - private static void waitForStartup(Process process, boolean custom) throws IOException { - for (int i = 0; i < 160; i++) { - // Breaks when using nohup & disown - // if (process != null && !custom && !process.isAlive()) { - // throw new IOException("Daemon start failed"); - // } - - if (process != null && custom && !process.isAlive() && process.exitValue() != 0) { - throw new IOException("Custom launch command failed"); - } - - try { - Thread.sleep(500); - } catch (InterruptedException ignored) { - } - - var s = BeaconClient.tryEstablishConnection( - BeaconConfig.getUsedPort(), - BeaconClientInformation.Api.builder() - .name("Beacon daemon controller") - .build()); - if (s.isPresent()) { - return; - } - } - - throw new IOException("Wait for daemon start up timed out"); - } - - private static void waitForShutdown() { - for (int i = 0; i < 40; i++) { - try { - Thread.sleep(500); - } catch (InterruptedException ignored) { - } - - var r = BeaconServer.isReachable(BeaconConfig.getUsedPort()); - if (!r) { - return; - } - } - } -} diff --git a/beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonExtensionTest.java b/beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonExtensionTest.java deleted file mode 100644 index fabe6d033..000000000 --- a/beacon/src/main/java/io/xpipe/beacon/test/BeaconDaemonExtensionTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.xpipe.beacon.test; - -import io.xpipe.core.ModuleLayerLoader; -import io.xpipe.core.OsType; -import io.xpipe.core.XPipeDaemonMode; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; - -public class BeaconDaemonExtensionTest { - - @BeforeAll - public static void setup() throws Exception { - ModuleLayerLoader.loadAll(ModuleLayer.boot(), throwable -> throwable.printStackTrace()); - BeaconDaemonController.start( - OsType.getLocal().equals(OsType.WINDOWS) ? XPipeDaemonMode.TRAY : XPipeDaemonMode.BACKGROUND); - } - - @AfterAll - public static void teardown() throws Exception { - BeaconDaemonController.stop(); - } -} diff --git a/beacon/src/main/java/module-info.java b/beacon/src/main/java/module-info.java index 6e44a0dd4..19a349f6e 100644 --- a/beacon/src/main/java/module-info.java +++ b/beacon/src/main/java/module-info.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.Module; open module io.xpipe.beacon { exports io.xpipe.beacon; - exports io.xpipe.beacon.test; exports io.xpipe.beacon.api; requires com.fasterxml.jackson.core; diff --git a/core/src/main/java/io/xpipe/core/Deobfuscator.java b/core/src/main/java/io/xpipe/core/Deobfuscator.java index f12388ebe..97eaba51e 100644 --- a/core/src/main/java/io/xpipe/core/Deobfuscator.java +++ b/core/src/main/java/io/xpipe/core/Deobfuscator.java @@ -63,7 +63,7 @@ public class Deobfuscator { var file = Files.createTempFile("xpipe_stracktrace", null); Files.writeString(file, stackTrace); var proc = new ProcessBuilder( - "retrace." + (OsType.getLocal().equals(OsType.WINDOWS) ? "bat" : "sh"), + "retrace." + (OsType.getLocal() == OsType.WINDOWS ? "bat" : "sh"), System.getenv("XPIPE_MAPPING"), file.toString()) .redirectErrorStream(true); diff --git a/core/src/main/java/io/xpipe/core/XPipeInstallation.java b/core/src/main/java/io/xpipe/core/XPipeInstallation.java deleted file mode 100644 index 11a9342e6..000000000 --- a/core/src/main/java/io/xpipe/core/XPipeInstallation.java +++ /dev/null @@ -1,298 +0,0 @@ -package io.xpipe.core; - -import lombok.Getter; -import lombok.SneakyThrows; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - -public class XPipeInstallation { - - private static final String STAGING_PROP = "io.xpipe.app.staging"; - - @Getter - private static final boolean staging = Optional.ofNullable(System.getProperty(STAGING_PROP)) - .map(Boolean::parseBoolean) - .orElse(false); - - public static int getDefaultBeaconPort() { - var offset = isStaging() ? 1 : 0; - return 21721 + offset; - } - - public static Path getLocalBeaconAuthFile() { - return Path.of(System.getProperty("java.io.tmpdir"), isStaging() ? "xpipe_ptb_auth" : "xpipe_auth"); - } - - public static String createExternalAsyncLaunchCommand( - String installationBase, XPipeDaemonMode mode, String arguments, boolean restart) { - var suffix = (arguments != null ? " " + arguments : ""); - var modeOption = mode != null ? " -Dio.xpipe.app.mode=" + mode.getDisplayName() : ""; - if (OsType.getLocal().equals(OsType.LINUX)) { - return "nohup \"" + installationBase + "/bin/xpiped\"" + modeOption + suffix - + "/dev/null 2>&1 & disown"; - } else if (OsType.getLocal().equals(OsType.MACOS)) { - if (restart) { - return "(sleep 1;open \"" + installationBase + "\" --args" + modeOption + suffix - + "/dev/null) & disown"; - } else { - return "open \"" + installationBase + "\" --args" + modeOption + suffix; - } - } - - return "\"" + FilePath.of(installationBase, XPipeInstallation.getDaemonExecutablePath(OsType.getLocal())) + "\"" - + modeOption + suffix; - } - - public static String createExternalLaunchCommand(String command, String arguments, XPipeDaemonMode mode) { - var suffix = (arguments != null ? " " + arguments : ""); - var modeOption = mode != null ? " -Dio.xpipe.app.mode=" + mode.getDisplayName() : ""; - return "\"" + command + "\"" + modeOption + suffix; - } - - private static boolean isImage() { - return XPipeInstallation.class - .getProtectionDomain() - .getCodeSource() - .getLocation() - .getProtocol() - .equals("jrt"); - } - - @SneakyThrows - public static Path getCurrentInstallationBasePath() { - var command = ProcessHandle.current().info().command(); - // We should always have a command associated with the current process, otherwise something went seriously wrong - if (command.isEmpty()) { - var javaHome = System.getProperty("java.home"); - var javaExec = toRealPathIfPossible(Path.of(javaHome, "bin", "java")); - var path = getLocalInstallationBasePathForJavaExecutable(javaExec); - return path; - } - - // Resolve any possible links to a real path - Path path = toRealPathIfPossible(Path.of(command.get())); - // Check if the process was started using a relative path, and adapt it if necessary - if (!path.isAbsolute()) { - path = toRealPathIfPossible(Path.of(System.getProperty("user.dir")).resolve(path)); - } - - var name = path.getFileName().toString(); - // Check if we launched the JVM via a start script instead of the native executable - if (name.endsWith("java") || name.endsWith("java.exe")) { - // If we are not an image, we are probably running in a development environment where we want to use the - // working directory - var isImage = isImage(); - if (!isImage) { - return Path.of(System.getProperty("user.dir")); - } - return getLocalInstallationBasePathForJavaExecutable(path); - } else { - return getLocalInstallationBasePathForDaemonExecutable(path); - } - } - - private static Path toRealPathIfPossible(Path p) { - try { - // Under certain conditions, e.g. when running on a ramdisk, path resolution might fail. - // This is however not a big problem in that case, so we ignore it - return p.toRealPath(); - } catch (IOException e) { - return p; - } - } - - public static Path getLocalExtensionsDirectory(Path path) { - return OsType.getLocal().equals(OsType.MACOS) - ? path.resolve("Contents").resolve("Resources").resolve("extensions") - : path.resolve("extensions"); - } - - private static Path getLocalInstallationBasePathForJavaExecutable(Path executable) { - // Resolve root path of installation relative to the java executable in a JPackage installation - if (OsType.getLocal().equals(OsType.MACOS)) { - return executable - .getParent() - .getParent() - .getParent() - .getParent() - .getParent() - .getParent(); - } else if (OsType.getLocal().equals(OsType.LINUX)) { - return executable.getParent().getParent().getParent().getParent(); - } else { - return executable.getParent().getParent().getParent(); - } - } - - private static Path getLocalInstallationBasePathForDaemonExecutable(Path executable) { - // Resolve root path of installation relative to executable in a JPackage installation - if (OsType.getLocal().equals(OsType.MACOS)) { - return executable.getParent().getParent().getParent(); - } else if (OsType.getLocal().equals(OsType.LINUX)) { - return executable.getParent().getParent(); - } else { - return executable.getParent(); - } - } - - public static Path getLocalInstallationBasePathForCLI(String cliExecutable) { - var defaultInstallation = getLocalDefaultInstallationBasePath(); - - // Can be empty in development mode - if (cliExecutable == null) { - return defaultInstallation; - } - - if (OsType.getLocal().equals(OsType.LINUX) && cliExecutable.equals("/usr/bin/xpipe")) { - return defaultInstallation; - } - - var path = Path.of(cliExecutable); - if (OsType.getLocal().equals(OsType.MACOS)) { - return path.getParent().getParent().getParent(); - } else if (OsType.getLocal().equals(OsType.LINUX)) { - return path.getParent().getParent(); - } else { - return path.getParent().getParent(); - } - } - - public static String queryLocalInstallationVersion(String exec) throws Exception { - var process = new ProcessBuilder(exec, "version") - .redirectError(ProcessBuilder.Redirect.DISCARD) - .start(); - var v = new String(process.getInputStream().readAllBytes(), StandardCharsets.US_ASCII); - process.waitFor(); - return v; - } - - public static String getLocalDefaultCliExecutable() { - Path path = isImage() ? getCurrentInstallationBasePath() : getLocalDefaultInstallationBasePath(); - return path.resolve(getRelativeCliExecutablePath(OsType.getLocal())).toString(); - } - - public static Path getLocalDefaultInstallationIcon() { - Path path = getCurrentInstallationBasePath(); - - // Check for development environment - if (!isImage()) { - if (OsType.getLocal().equals(OsType.WINDOWS)) { - return path.resolve("dist").resolve("logo").resolve("logo.ico"); - } else if (OsType.getLocal().equals(OsType.LINUX)) { - return path.resolve("dist").resolve("logo").resolve("logo.png"); - } else { - return path.resolve("dist").resolve("logo").resolve("logo.icns"); - } - } - - if (OsType.getLocal().equals(OsType.WINDOWS)) { - return path.resolve("logo.ico"); - } else if (OsType.getLocal().equals(OsType.LINUX)) { - return path.resolve("logo.png"); - } else { - return path.resolve("Contents").resolve("Resources").resolve("xpipe.icns"); - } - } - - public static Path getLocalDefaultInstallationBasePath() { - return getLocalDefaultInstallationBasePath(staging); - } - - public static Path getLocalDefaultInstallationBasePath(boolean stage) { - Path path; - if (OsType.getLocal().equals(OsType.WINDOWS)) { - var pg = System.getenv("ProgramFiles"); - var systemPath = Path.of(pg, stage ? "XPipe PTB" : "XPipe"); - if (Files.exists(systemPath)) { - return systemPath; - } - - var base = Path.of(System.getenv("LOCALAPPDATA")); - path = base.resolve(stage ? "XPipe PTB" : "XPipe"); - } else if (OsType.getLocal().equals(OsType.LINUX)) { - path = Path.of(stage ? "/opt/xpipe-ptb" : "/opt/xpipe"); - } else { - path = Path.of(stage ? "/Applications/XPipe PTB.app" : "/Applications/XPipe.app"); - } - - return path; - } - - public static Path getLangPath() { - if (!isImage()) { - return getCurrentInstallationBasePath().resolve("lang"); - } - - var install = getCurrentInstallationBasePath(); - var type = OsType.getLocal(); - if (type.equals(OsType.WINDOWS)) { - return install.resolve("lang"); - } else if (type.equals(OsType.LINUX)) { - return install.resolve("lang"); - } else { - return install.resolve("Contents").resolve("Resources").resolve("lang"); - } - } - - public static Path getBundledFontsPath() { - if (!isImage()) { - return Path.of("dist", "fonts"); - } - - var install = getCurrentInstallationBasePath(); - var type = OsType.getLocal(); - if (type.equals(OsType.WINDOWS)) { - return install.resolve("fonts"); - } else if (type.equals(OsType.LINUX)) { - return install.resolve("fonts"); - } else { - return install.resolve("Contents").resolve("Resources").resolve("fonts"); - } - } - - public static String getDaemonDebugScriptPath(OsType.Local type) { - if (type.equals(OsType.WINDOWS)) { - return FilePath.of("scripts", "xpiped_debug.bat").toString(); - } else if (type.equals(OsType.LINUX)) { - return FilePath.of("scripts", "xpiped_debug.sh").toString(); - } else { - return FilePath.of("Contents", "Resources", "scripts", "xpiped_debug.sh") - .toString(); - } - } - - public static String getDaemonDebugAttachScriptPath(OsType.Local type) { - if (type.equals(OsType.WINDOWS)) { - return FilePath.of("scripts", "xpiped_debug_attach.bat").toString(); - } else if (type.equals(OsType.LINUX)) { - return FilePath.of("scripts", "xpiped_debug_attach.sh").toString(); - } else { - return FilePath.of("Contents", "Resources", "scripts", "xpiped_debug_attach.sh") - .toString(); - } - } - - public static String getDaemonExecutablePath(OsType.Local type) { - if (type.equals(OsType.WINDOWS)) { - return FilePath.of("xpiped.exe").toString(); - } else if (type.equals(OsType.LINUX)) { - return FilePath.of("bin", "xpiped").toString(); - } else { - return FilePath.of("Contents", "MacOS", "xpiped").toString(); - } - } - - public static String getRelativeCliExecutablePath(OsType.Local type) { - if (type.equals(OsType.WINDOWS)) { - return FilePath.of("bin", "xpipe.exe").toString(); - } else if (type.equals(OsType.LINUX)) { - return FilePath.of("bin", "xpipe").toString(); - } else { - return FilePath.of("Contents", "MacOS", "xpipe").toString(); - } - } -} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java index 33d2f6fbc..9c2b62258 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java @@ -67,7 +67,7 @@ public class SshIdentityStateManager { event.customAction(shutdown).handle(); if (r.get()) { - if (sc.getShellDialect().equals(ShellDialects.CMD)) { + if (sc.getShellDialect() == ShellDialects.CMD) { sc.command( "powershell -Command \"Start-Process cmd -Wait -ArgumentList /c, sc, stop, ssh-agent -Verb runAs\"") .executeAndCheck(); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java index c1dd5ea39..ba82e735f 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStrategy.java @@ -136,7 +136,7 @@ public interface SshIdentityStrategy { @Override public void prepareParent(ShellControl parent) throws Exception { - if (!parent.getOsType().equals(OsType.WINDOWS)) { + if (parent.getOsType() != OsType.WINDOWS) { var out = parent.executeSimpleStringCommand("pageant -l"); if (out.isBlank()) { throw ErrorEventFactory.expected( @@ -157,7 +157,7 @@ public interface SshIdentityStrategy { @Override public void buildCommand(CommandBuilder builder) { builder.environment("SSH_AUTH_SOCK", parent -> { - if (parent.getOsType().equals(OsType.WINDOWS)) { + if (parent.getOsType() == OsType.WINDOWS) { return getPageantWindowsPipe(parent); } @@ -332,7 +332,7 @@ public interface SshIdentityStrategy { + " is marked to be a public key file, SSH authentication requires the private key")); } - if ((parent.getOsType().equals(OsType.LINUX) || parent.getOsType().equals(OsType.MACOS))) { + if ((parent.getOsType() == OsType.LINUX || parent.getOsType() == OsType.MACOS)) { // Try to preserve the same permission set parent.command(CommandBuilder.of() .add("test", "-w") diff --git a/gradle/gradle_scripts/local_junit_suite.gradle b/gradle/gradle_scripts/local_junit_suite.gradle index eebc8c1f9..4f9b137c6 100644 --- a/gradle/gradle_scripts/local_junit_suite.gradle +++ b/gradle/gradle_scripts/local_junit_suite.gradle @@ -26,10 +26,8 @@ testing { dependsOn(project.allExtensions.stream().map(p -> p.getTasksByName('jar', true)[0]).toList()) systemProperty 'io.xpipe.app.fullVersion', "true" - systemProperty 'io.xpipe.beacon.printDaemonOutput', "false" systemProperty 'io.xpipe.app.useVirtualThreads', "false" systemProperty "io.xpipe.beacon.port", "21723" - systemProperty "io.xpipe.beacon.launchDebugDaemon", "true" systemProperty "io.xpipe.app.dataDir", "$projectDir/local/" systemProperty "io.xpipe.app.logLevel", "trace" systemProperty "io.xpipe.app.writeSysOut", "true" diff --git a/gradle/gradle_scripts/remote_junit_suite.gradle b/gradle/gradle_scripts/remote_junit_suite.gradle index 7d8ef127c..6cd8e6781 100644 --- a/gradle/gradle_scripts/remote_junit_suite.gradle +++ b/gradle/gradle_scripts/remote_junit_suite.gradle @@ -35,9 +35,7 @@ testing { " -Dio.xpipe.beacon.printMessages=true" + " -Dio.xpipe.app.logLevel=trace" - systemProperty 'io.xpipe.beacon.printDaemonOutput', "true" systemProperty "io.xpipe.beacon.port", "21723" - systemProperty "io.xpipe.beacon.launchDebugDaemon", "true" } } }