diff --git a/.github/workflows/win-debug.yml b/.github/workflows/win-debug.yml
index d26851105..88c29a8c4 100644
--- a/.github/workflows/win-debug.yml
+++ b/.github/workflows/win-debug.yml
@@ -9,8 +9,10 @@ on:
env:
JAVA_VERSION: 19
- JAVA_DIST: 'zulu'
+ JAVA_DIST: 'temurin'
JAVA_CACHE: 'maven'
+ JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_windows-x64_bin-jmods.zip'
+ JFX_JMODS_HASH: 'B7CF2CAD2468842B3B78D99F6C0555771499A36FA1F1EE3DD1B9A4597F1FAB86'
defaults:
run:
@@ -35,17 +37,31 @@ jobs:
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
- java-package: 'jdk+fx'
+ java-package: 'jdk'
cache: ${{ env.JAVA_CACHE }}
- - name: Ensure major jfx version in pom equals in jdk
- shell: pwsh
+ - name: Download and extract JavaFX jmods from Gluon
+ #In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
run: |
- $jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
- $jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
- if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
- Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
- exit 1
+ curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}"
+ if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) {
+ exit 1;
}
+ Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
+ Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
+ shell: pwsh
+ - name: Ensure major jfx version in pom and in jmods is the same
+ run: |
+ JMOD_VERSION_AMD64=$(jmod describe jfxjmods/javafx.base.jmod | head -1)
+ JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
+ JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
+ POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
+ POM_JFX_VERSION=${POM_JFX_VERSION#*@}
+ POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
+
+ if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
+ >&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION_AMD64})"
+ exit 1
+ fi
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
@@ -59,7 +75,7 @@ jobs:
${JAVA_HOME}/bin/jlink
--verbose
--output runtime
- --module-path "${JAVA_HOME}/jmods"
+ --module-path "jfxjmods;${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--strip-native-commands
--no-header-files
diff --git a/README.md b/README.md
index 03931f6b0..b78fb7d88 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ Cryptomator is provided free of charge as an open-source project despite the hig
 |
+  |
 |
diff --git a/pom.xml b/pom.xml
index bc4b83d15..6c98a255a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,7 +38,7 @@
1.2.0
1.2.0
1.2.0
- 2.0.4
+ 2.0.5
2.0.0
2.0.2
@@ -317,41 +317,6 @@
-
- org.codehaus.mojo
- exec-maven-plugin
- 3.1.0
-
-
- compile-light-theme
- compile
-
- java
-
-
- javafx.graphics/com.sun.javafx.css.parser.Css2Bin
-
- ${project.basedir}/src/main/resources/css/light_theme.css
- ${project.build.outputDirectory}/css/light_theme.bss
-
-
-
-
- compile-dark-theme
- compile
-
- java
-
-
- javafx.graphics/com.sun.javafx.css.parser.Css2Bin
-
- ${project.basedir}/src/main/resources/css/dark_theme.css
- ${project.build.outputDirectory}/css/dark_theme.bss
-
-
-
-
-
org.apache.maven.plugins
maven-jar-plugin
diff --git a/src/main/java/org/cryptomator/common/ObservableUtil.java b/src/main/java/org/cryptomator/common/ObservableUtil.java
index 289f6e929..7927b6e54 100644
--- a/src/main/java/org/cryptomator/common/ObservableUtil.java
+++ b/src/main/java/org/cryptomator/common/ObservableUtil.java
@@ -2,7 +2,9 @@ package org.cryptomator.common;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
+import java.util.Objects;
import java.util.function.Function;
+import java.util.function.Supplier;
public class ObservableUtil {
@@ -15,4 +17,14 @@ public class ObservableUtil {
}
}, observable);
}
+
+ public static ObservableValue mapWithDefault(ObservableValue observable, Function super T, ? extends U> mapper, Supplier defaultValue) {
+ return Bindings.createObjectBinding(() -> {
+ if (observable.getValue() == null) {
+ return defaultValue.get();
+ } else {
+ return mapper.apply(observable.getValue());
+ }
+ }, observable);
+ }
}
diff --git a/src/main/java/org/cryptomator/common/mount/MountModule.java b/src/main/java/org/cryptomator/common/mount/MountModule.java
index d78cc3216..0978c44db 100644
--- a/src/main/java/org/cryptomator/common/mount/MountModule.java
+++ b/src/main/java/org/cryptomator/common/mount/MountModule.java
@@ -2,50 +2,71 @@ package org.cryptomator.common.mount;
import dagger.Module;
import dagger.Provides;
+import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.settings.Settings;
+import org.cryptomator.integrations.mount.Mount;
import org.cryptomator.integrations.mount.MountService;
+import javax.inject.Named;
import javax.inject.Singleton;
-import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
@Module
public class MountModule {
+ private static final AtomicReference formerSelectedMountService = new AtomicReference<>(null);
+ private static final List problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
+
@Provides
@Singleton
static List provideSupportedMountServices() {
return MountService.get().toList();
}
- //currently not used, because macFUSE and FUSE-T cannot be used in the same JVM
- /*
@Provides
@Singleton
- static ObservableValue provideMountService(Settings settings, List serviceImpls) {
+ @Named("FUPFMS")
+ static AtomicReference provideFirstUsedProblematicFuseMountService() {
+ return new AtomicReference<>(null);
+ }
+
+ @Provides
+ @Singleton
+ static ObservableValue provideMountService(Settings settings, List serviceImpls, @Named("FUPFMS") AtomicReference fupfms) {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
- return ObservableUtil.mapWithDefault(settings.mountService(), //
+
+ var observableMountService = ObservableUtil.mapWithDefault(settings.mountService(), //
desiredServiceImpl -> { //
- var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
- return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); //
+ var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
+ var targetedService = serviceFromSettings.orElse(fallbackProvider);
+ return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms);
}, //
- new ActualMountService(fallbackProvider, true));
- }
- */
-
- @Provides
- @Singleton
- static ActualMountService provideActualMountService(Settings settings, List serviceImpls) {
- var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
- var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(settings.mountService().getValue())).findFirst(); //
- return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); //
+ () -> { //
+ return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms);
+ });
+ return observableMountService;
}
- @Provides
- @Singleton
- static ObservableValue provideMountService(ActualMountService service) {
- return new SimpleObjectProperty<>(service);
+ //see https://github.com/cryptomator/cryptomator/issues/2786
+ private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference firstUsedProblematicFuseMountService) {
+ //set the first used problematic fuse service if applicable
+ var targetIsProblematicFuse = isProblematicFuseService(targetedService);
+ if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
+ firstUsedProblematicFuseMountService.set(targetedService);
+ }
+
+ //do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used.
+ if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) {
+ return new ActualMountService(formerSelectedMountService.get(), false);
+ } else {
+ formerSelectedMountService.set(targetedService);
+ return new ActualMountService(targetedService, isDesired);
+ }
}
+ public static boolean isProblematicFuseService(MountService service) {
+ return problematicFuseMountServices.contains(service.getClass().getName());
+ }
}
diff --git a/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java
index 61a54ba22..6c60e74b7 100644
--- a/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java
+++ b/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java
@@ -2,12 +2,14 @@ package org.cryptomator.ui.preferences;
import dagger.Lazy;
import org.cryptomator.common.ObservableUtil;
+import org.cryptomator.common.mount.MountModule;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.mount.MountCapability;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
+import javax.inject.Named;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanExpression;
@@ -19,6 +21,7 @@ import javafx.util.StringConverter;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
+import java.util.concurrent.atomic.AtomicReference;
@PreferencesScoped
public class VolumePreferencesController implements FxController {
@@ -33,6 +36,7 @@ public class VolumePreferencesController implements FxController {
private final ObservableValue mountToDriveLetterSupported;
private final ObservableValue mountFlagsSupported;
private final ObservableValue readonlySupported;
+ private final ObservableValue fuseRestartRequired;
private final Lazy application;
private final List mountProviders;
public ChoiceBox volumeTypeChoiceBox;
@@ -40,7 +44,7 @@ public class VolumePreferencesController implements FxController {
public Button loopbackPortApplyButton;
@Inject
- VolumePreferencesController(Settings settings, Lazy application, List mountProviders, ResourceBundle resourceBundle) {
+ VolumePreferencesController(Settings settings, Lazy application, List mountProviders, @Named("FUPFMS") AtomicReference firstUsedProblematicFuseMountService, ResourceBundle resourceBundle) {
this.settings = settings;
this.application = application;
this.mountProviders = mountProviders;
@@ -53,6 +57,12 @@ public class VolumePreferencesController implements FxController {
this.mountToDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
this.readonlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
+ this.fuseRestartRequired = selectedMountService.map(s -> {//
+ return firstUsedProblematicFuseMountService.get() != null //
+ && MountModule.isProblematicFuseService(s) //
+ && !firstUsedProblematicFuseMountService.get().equals(s);
+ });
+
}
public void initialize() {
@@ -129,6 +139,14 @@ public class VolumePreferencesController implements FxController {
return mountFlagsSupported.getValue();
}
+ public ObservableValue fuseRestartRequiredProperty() {
+ return fuseRestartRequired;
+ }
+
+ public boolean getFuseRestartRequired() {
+ return fuseRestartRequired.getValue();
+ }
+
/* Helpers */
private class MountServiceConverter extends StringConverter {
diff --git a/src/main/resources/css/dark_theme.css b/src/main/resources/css/dark_theme.css
index 5f0877842..45cadba93 100644
--- a/src/main/resources/css/dark_theme.css
+++ b/src/main/resources/css/dark_theme.css
@@ -116,6 +116,10 @@
-fx-font-size: 0.64em;
}
+.label-red {
+ -fx-text-fill: RED_5;
+}
+
.text-flow > * {
-fx-fill: TEXT_FILL;
}
diff --git a/src/main/resources/css/light_theme.css b/src/main/resources/css/light_theme.css
index decf64b64..c3c0faaa9 100644
--- a/src/main/resources/css/light_theme.css
+++ b/src/main/resources/css/light_theme.css
@@ -116,6 +116,10 @@
-fx-font-size: 0.64em;
}
+.label-red {
+ -fx-text-fill: RED_5;
+}
+
.text-flow > * {
-fx-fill: TEXT_FILL;
}
diff --git a/src/main/resources/fxml/preferences_volume.fxml b/src/main/resources/fxml/preferences_volume.fxml
index 16ccc2b52..f48b1c1c8 100644
--- a/src/main/resources/fxml/preferences_volume.fxml
+++ b/src/main/resources/fxml/preferences_volume.fxml
@@ -8,9 +8,9 @@
+
-
+
+
diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties
index 17169e819..06baefce6 100644
--- a/src/main/resources/i18n/strings.properties
+++ b/src/main/resources/i18n/strings.properties
@@ -275,9 +275,10 @@ preferences.interface.showMinimizeButton=Show minimize button
preferences.interface.showTrayIcon=Show tray icon (requires restart)
## Volume
preferences.volume=Virtual Drive
-preferences.volume.type=Volume Type (requires restart)
+preferences.volume.type=Volume Type
preferences.volume.type.automatic=Automatic
preferences.volume.docsTooltip=Open the documentation to learn more about the different volume types.
+preferences.volume.fuseRestartRequired=To apply the changes, Cryptomator needs to be restarted.
preferences.volume.tcp.port=TCP Port
preferences.volume.supportedFeatures=The chosen volume type supports the following features:
preferences.volume.feature.mountAuto=Automatic mount point selection