diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff4af5ea5..a31a3b241 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,8 +32,8 @@ If you are on Linux or macOS, you can easily accomplish that by running ```bash curl -s "https://get.sdkman.io" | bash . "$HOME/.sdkman/bin/sdkman-init.sh" -sdk install java 22.0.2-graalce -sdk default java 22.0.2-graalce +sdk install java 24-graalce +sdk default java 24-graalce ``` . On Windows, you have to manually install a JDK, e.g. from [Adoptium](https://adoptium.net/temurin/releases/?version=21). @@ -65,7 +65,7 @@ many IDEs still have problems building this project properly. For example, you can't build this project in eclipse or vscode as it will complain about missing modules. The tested and recommended IDE is IntelliJ. -When setting up the project in IntelliJ, make sure that the correct JDK (Java 22) +When setting up the project in IntelliJ, make sure that the correct JDK (Java 24) is selected both for the project and for gradle itself. ## Contributing guide 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 bccca2db9..3751ae9b5 100644 --- a/app/src/main/java/io/xpipe/app/core/AppDesktopIntegration.java +++ b/app/src/main/java/io/xpipe/app/core/AppDesktopIntegration.java @@ -60,11 +60,7 @@ public class AppDesktopIntegration { AppOpenArguments.handle(List.of(e.getURI().toString())); }); - // Do it this way to prevent IDE inspections from complaining - var c = Class.forName( - ModuleLayer.boot().findModule("java.desktop").orElseThrow(), "com.apple.eawt.Application"); - var m = c.getDeclaredMethod("addAppEventListener", SystemEventListener.class); - m.invoke(c.getMethod("getApplication").invoke(null), new AppReopenedListener() { + Desktop.getDesktop().addAppEventListener(new AppReopenedListener() { @Override public void appReopened(AppReopenedEvent e) { OperationMode.switchToAsync(OperationMode.GUI); diff --git a/app/src/main/java/io/xpipe/app/core/AppDistributionType.java b/app/src/main/java/io/xpipe/app/core/AppDistributionType.java index 022691ec4..ee357cb73 100644 --- a/app/src/main/java/io/xpipe/app/core/AppDistributionType.java +++ b/app/src/main/java/io/xpipe/app/core/AppDistributionType.java @@ -9,7 +9,6 @@ import io.xpipe.app.update.WebtopUpdater; import io.xpipe.app.util.LocalExec; import io.xpipe.app.util.Translatable; import io.xpipe.core.process.OsType; -import io.xpipe.core.util.ModuleHelper; import io.xpipe.core.util.XPipeInstallation; import javafx.beans.value.ObservableValue; @@ -54,7 +53,7 @@ public enum AppDistributionType implements Translatable { return; } - if (!ModuleHelper.isImage()) { + if (!AppProperties.get().isImage()) { type = DEVELOPMENT; 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 aeec23ea2..a848c4d8e 100644 --- a/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java +++ b/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java @@ -6,7 +6,6 @@ import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.resources.AppResources; import io.xpipe.core.process.OsType; -import io.xpipe.core.util.ModuleHelper; import io.xpipe.core.util.ModuleLayerLoader; import io.xpipe.core.util.XPipeInstallation; @@ -209,10 +208,6 @@ public class AppExtensionManager { return Optional.empty(); } - ext.get().getModule().getPackages().forEach(pkg -> { - ModuleHelper.exportAndOpen(pkg, ext.get().getModule()); - }); - TrackEvent.withInfo("Loaded extension module") .tag("name", ext.get().getName()) .tag("dir", dir.toString()) diff --git a/app/src/main/java/io/xpipe/app/core/AppProperties.java b/app/src/main/java/io/xpipe/app/core/AppProperties.java index 08fdedca2..6eb5c67f0 100644 --- a/app/src/main/java/io/xpipe/app/core/AppProperties.java +++ b/app/src/main/java/io/xpipe/app/core/AppProperties.java @@ -4,7 +4,6 @@ import io.xpipe.app.core.check.AppUserDirectoryCheck; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.prefs.AppPrefs; -import io.xpipe.core.util.ModuleHelper; import io.xpipe.core.util.XPipeDaemonMode; import lombok.Getter; @@ -84,7 +83,12 @@ public class AppProperties { } var referenceDir = Files.exists(appDir) ? appDir : Path.of(System.getProperty("user.dir")); - image = ModuleHelper.isImage(); + image = AppProperties.class + .getProtectionDomain() + .getCodeSource() + .getLocation() + .getProtocol() + .equals("jrt"); arguments = AppArguments.init(args); fullVersion = Optional.ofNullable(System.getProperty("io.xpipe.app.fullVersion")) .map(Boolean::parseBoolean) 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 cfd8c881b..1c2291fb9 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTrayIcon.java +++ b/app/src/main/java/io/xpipe/app/core/AppTrayIcon.java @@ -90,7 +90,6 @@ public class AppTrayIcon { EventQueue.invokeLater(() -> { try { tray.add(this.trayIcon); - fixBackground(); } catch (Exception e) { // This can sometimes fail on Linux ErrorEvent.fromThrowable("Unable to add TrayIcon", e).expected().handle(); @@ -98,67 +97,12 @@ public class AppTrayIcon { }); } - private void fixBackground() { - // Ugly fix to show a transparent background on Linux - if (OsType.getLocal().equals(OsType.LINUX)) { - EventQueue.invokeLater(() -> { - try { - Field peerField; - peerField = TrayIcon.class.getDeclaredField("peer"); - peerField.setAccessible(true); - var peer = peerField.get(this.trayIcon); - - // If tray initialization fails, this can be null - if (peer == null) { - return; - } - - var canvasField = peer.getClass().getDeclaredField("canvas"); - canvasField.setAccessible(true); - Component canvas = (Component) canvasField.get(peer); - canvas.setBackground(new Color(0, 0, 0, 0)); - - var frameField = peer.getClass().getDeclaredField("eframe"); - frameField.setAccessible(true); - Frame frame = (Frame) frameField.get(peer); - frame.setTitle("XPipe"); - } catch (Exception e) { - ErrorEvent.fromThrowable(e).omit().handle(); - } - }); - } - } - public void hide() { EventQueue.invokeLater(() -> { tray.remove(trayIcon); }); } - public void showInfoMessage(String title, String message) { - if (OsType.getLocal().equals(OsType.MACOS)) { - showMacAlert(title, message, "Information"); - } else { - EventQueue.invokeLater(() -> this.trayIcon.displayMessage(title, message, TrayIcon.MessageType.INFO)); - } - } - - public void showInfoMessage(String message) { - this.showInfoMessage(null, message); - } - - public void showWarningMessage(String title, String message) { - if (OsType.getLocal().equals(OsType.MACOS)) { - showMacAlert(title, message, "Warning"); - } else { - EventQueue.invokeLater(() -> this.trayIcon.displayMessage(title, message, TrayIcon.MessageType.WARNING)); - } - } - - public void showWarningMessage(String message) { - this.showWarningMessage(null, message); - } - public void showErrorMessage(String title, String message) { if (OsType.getLocal().equals(OsType.MACOS)) { showMacAlert(title, message, "Error"); @@ -167,22 +111,6 @@ public class AppTrayIcon { } } - public void showErrorMessage(String message) { - this.showErrorMessage(null, message); - } - - public void showMessage(String title, String message) { - if (OsType.getLocal().equals(OsType.MACOS)) { - showMacAlert(title, message, "Message"); - } else { - EventQueue.invokeLater(() -> this.trayIcon.displayMessage(title, message, TrayIcon.MessageType.NONE)); - } - } - - public void showMessage(String message) { - this.showMessage(null, message); - } - private void showMacAlert(String subTitle, String message, String title) { String execute = String.format( "display notification \"%s\"" + " with title \"%s\"" + " subtitle \"%s\"", diff --git a/app/src/main/java/io/xpipe/app/core/check/AppDebugModeCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppDebugModeCheck.java index bd2ab35a6..788b49566 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppDebugModeCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppDebugModeCheck.java @@ -1,13 +1,13 @@ package io.xpipe.app.core.check; import io.xpipe.app.core.AppLogs; +import io.xpipe.app.core.AppProperties; import io.xpipe.app.util.ThreadHelper; -import io.xpipe.core.util.ModuleHelper; public class AppDebugModeCheck { public static void printIfNeeded() { - if (!ModuleHelper.isImage() || !AppLogs.get().getLogLevel().equals("trace")) { + if (!AppProperties.get().isImage() || !AppLogs.get().getLogLevel().equals("trace")) { return; } diff --git a/app/src/main/java/io/xpipe/app/core/window/NativeMacOsWindowControl.java b/app/src/main/java/io/xpipe/app/core/window/NativeMacOsWindowControl.java index 315a22680..57ac4e059 100644 --- a/app/src/main/java/io/xpipe/app/core/window/NativeMacOsWindowControl.java +++ b/app/src/main/java/io/xpipe/app/core/window/NativeMacOsWindowControl.java @@ -3,7 +3,6 @@ package io.xpipe.app.core.window; import io.xpipe.app.core.AppProperties; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.util.NativeBridge; -import io.xpipe.core.util.ModuleHelper; import javafx.stage.Window; @@ -33,7 +32,7 @@ public class NativeMacOsWindowControl { } public boolean setAppearance(boolean seamlessFrame, boolean darkMode) { - if (!ModuleHelper.isImage() || !AppProperties.get().isFullVersion()) { + if (!AppProperties.get().isImage() || !AppProperties.get().isFullVersion()) { return false; } diff --git a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java index e76b96899..f020d31b8 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java +++ b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java @@ -16,7 +16,6 @@ import io.xpipe.app.terminal.TerminalPromptManager; import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.PlatformThread; import io.xpipe.core.process.ShellScript; -import io.xpipe.core.util.ModuleHelper; import javafx.beans.property.*; import javafx.beans.value.ObservableBooleanValue; @@ -345,7 +344,7 @@ public class AppPrefs { } public boolean isDevelopmentEnvironment() { - return developerMode().getValue() && !ModuleHelper.isImage(); + return developerMode().getValue() && !AppProperties.get().isImage(); } public ObservableValue passwordManager() { 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 b062bc384..5ba9091cf 100644 --- a/app/src/main/java/io/xpipe/app/util/NativeBridge.java +++ b/app/src/main/java/io/xpipe/app/util/NativeBridge.java @@ -3,7 +3,6 @@ package io.xpipe.app.util; import io.xpipe.app.core.AppProperties; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.core.process.OsType; -import io.xpipe.core.util.ModuleHelper; import io.xpipe.core.util.XPipeInstallation; import com.sun.jna.Library; @@ -26,7 +25,7 @@ public class NativeBridge { } public static Optional getMacOsLibrary() { - if (!ModuleHelper.isImage() || !AppProperties.get().isFullVersion()) { + if (!AppProperties.get().isImage() || !AppProperties.get().isFullVersion()) { return Optional.empty(); } diff --git a/build.gradle b/build.gradle index 9377f4d8a..cdc62ffb0 100644 --- a/build.gradle +++ b/build.gradle @@ -127,9 +127,12 @@ project.ext { devJavafxVersion = '25-ea+10' platformName = getPlatformName() jvmRunArgs = [ - "--add-opens", "java.base/java.lang=io.xpipe.app", - "--add-opens", "java.base/java.lang=io.xpipe.core", - "--add-opens", "java.desktop/java.awt=io.xpipe.app", + "--enable-native-access=com.sun.jna", + "--enable-native-access=javafx.graphics", + "--enable-native-access=javafx.web", + "--sun-misc-unsafe-memory-access=allow", + "--add-opens", "java.base/java.io=io.xpipe.ext.proc", + "--add-opens", "org.apache.commons.io/org.apache.commons.io.input=io.xpipe.ext.proc", "--add-opens", "net.synedra.validatorfx/net.synedra.validatorfx=io.xpipe.app", "--add-opens", "java.base/java.nio.file=io.xpipe.app", "--add-exports", "javafx.graphics/com.sun.javafx.tk=io.xpipe.app", @@ -145,6 +148,9 @@ project.ext { "-Djdk.virtualThreadScheduler.parallelism=8" ] + // Use project liliput + jvmRunArgs += ['-XX:+UnlockExperimentalVMOptions', '-XX:+UseCompactObjectHeaders'] + // GC config jvmRunArgs += ['-XX:+UseG1GC', '-Xms300m', '-Xmx4G', '-XX:GCTimeRatio=9', '-XX:+UseStringDeduplication']; diff --git a/core/src/main/java/io/xpipe/core/util/ModuleHelper.java b/core/src/main/java/io/xpipe/core/util/ModuleHelper.java deleted file mode 100644 index 54c5e04b5..000000000 --- a/core/src/main/java/io/xpipe/core/util/ModuleHelper.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.xpipe.core.util; - -import lombok.SneakyThrows; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -public class ModuleHelper { - - public static boolean isImage() { - return ModuleHelper.class - .getProtectionDomain() - .getCodeSource() - .getLocation() - .getProtocol() - .equals("jrt"); - } - - @SneakyThrows - public static Module getEveryoneModule() { - Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); - getDeclaredFields0.setAccessible(true); - Field[] fields = (Field[]) getDeclaredFields0.invoke(Module.class, false); - Field modifiers = null; - for (Field each : fields) { - if ("EVERYONE_MODULE".equals(each.getName())) { - modifiers = each; - break; - } - } - modifiers.setAccessible(true); - return (Module) modifiers.get(null); - } - - @SneakyThrows - public static void exportAndOpen(String pkg, Module mod) { - if (mod.isExported(pkg) && mod.isOpen(pkg)) { - return; - } - - Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredMethods0", boolean.class); - getDeclaredFields0.setAccessible(true); - Method[] fields = (Method[]) getDeclaredFields0.invoke(Module.class, false); - Method modifiers = null; - for (Method each : fields) { - if ("implAddExportsOrOpens".equals(each.getName())) { - modifiers = each; - break; - } - } - modifiers.setAccessible(true); - - var e = getEveryoneModule(); - modifiers.invoke(mod, pkg, e, false, true); - modifiers.invoke(mod, pkg, e, true, true); - } -} diff --git a/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java b/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java index c8c6e4d1f..33e0d5302 100644 --- a/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java +++ b/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java @@ -55,6 +55,15 @@ public class XPipeInstallation { 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(); @@ -78,7 +87,7 @@ public class XPipeInstallation { 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 = ModuleHelper.isImage(); + var isImage = isImage(); if (!isImage) { return Path.of(System.getProperty("user.dir")); } @@ -164,7 +173,7 @@ public class XPipeInstallation { } public static String getLocalDefaultCliExecutable() { - Path path = ModuleHelper.isImage() + Path path = isImage() ? getCurrentInstallationBasePath() : Path.of(getLocalDefaultInstallationBasePath()); return path.resolve(getRelativeCliExecutablePath(OsType.getLocal())).toString(); @@ -174,7 +183,7 @@ public class XPipeInstallation { Path path = getCurrentInstallationBasePath(); // Check for development environment - if (!ModuleHelper.isImage()) { + if (!isImage()) { if (OsType.getLocal().equals(OsType.WINDOWS)) { return path.resolve("dist").resolve("logo").resolve("logo.ico"); } else if (OsType.getLocal().equals(OsType.LINUX)) { @@ -218,7 +227,7 @@ public class XPipeInstallation { } public static Path getLangPath() { - if (!ModuleHelper.isImage()) { + if (!isImage()) { return getCurrentInstallationBasePath().resolve("lang"); } @@ -234,7 +243,7 @@ public class XPipeInstallation { } public static Path getBundledFontsPath() { - if (!ModuleHelper.isImage()) { + if (!isImage()) { return Path.of("dist", "fonts"); } diff --git a/dist/jpackage.gradle b/dist/jpackage.gradle index 7b4906453..2d8afa22f 100644 --- a/dist/jpackage.gradle +++ b/dist/jpackage.gradle @@ -13,7 +13,8 @@ def releaseArguments = distJvmArgs + [ "-Dio.xpipe.app.staging=$rootProject.isStage", '-Dio.xpipe.app.sentryUrl=https://fd5f67ff10764b7e8a704bec9558c8fe@o1084459.ingest.sentry.io/6094279', '-Djna.nosys=false', - '-Djna.nounpack=true' + '-Djna.nounpack=true', + '-Xlog:cds' ] if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) { diff --git a/dist/licenses/graalvm.properties b/dist/licenses/graalvm.properties index 2e077e2f5..2580c6c2d 100644 --- a/dist/licenses/graalvm.properties +++ b/dist/licenses/graalvm.properties @@ -1,4 +1,4 @@ name=GraalVM Community -version=22.0.2 +version=24 license=GPL2 with the Classpath Exception link=https://www.graalvm.org/ \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197..9bbc975c7 100755 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ed4c299ad..a34806c2b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip +distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-8.14-20250406024815+0000-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6..faf93008b 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -206,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line.