This commit is contained in:
crschnick
2026-06-12 15:45:11 +00:00
parent ef0548c0df
commit f17eed8fbb
8 changed files with 99 additions and 1 deletions
@@ -12,6 +12,7 @@ import io.xpipe.app.hub.comp.StoreChoiceComp;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.platform.*;
import io.xpipe.app.process.LocalShell;
import io.xpipe.app.process.ShellScript;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
@@ -22,6 +23,7 @@ import io.xpipe.app.util.OsType;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -333,9 +335,11 @@ public class TerminalCategory extends AppPrefsCategory {
var choice = choiceBuilder.build().buildComp();
choice.maxWidth(600);
var testCounter = new SimpleIntegerProperty();
var testDisabled = new SimpleBooleanProperty();
var test = new ButtonComp(AppI18n.observable("test"), new FontIcon("mdi2p-play"), () -> {
testDisabled.set(true);
testCounter.set(testCounter.getValue() + 1);
ThreadHelper.runFailableAsync(() -> {
try {
var term = AppPrefs.get().terminalType().getValue();
@@ -382,6 +386,9 @@ public class TerminalCategory extends AppPrefsCategory {
.documentationLink(DocumentationLink.TERMINAL_MULTIPLEXER)
.addComp(choice);
if (OsType.ofLocal() == OsType.WINDOWS) {
options.disable(Bindings.createBooleanBinding(() -> {
return prefs.terminalProxy() == null && !TerminalMultiplexerManager.isAvailableOnWindows();
}, prefs.terminalProxy(), testCounter));
options.disable(BindingsHelper.map(prefs.terminalProxy(), uuid -> uuid == null));
}
options.addComp(test).hide(prefs.terminalMultiplexer.isNull());
@@ -1,19 +1,38 @@
package io.xpipe.app.terminal;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.prefs.ExternalApplicationHelper;
import io.xpipe.app.process.*;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.util.OsType;
import io.xpipe.app.webtop.WebtopApp;
import lombok.Builder;
import lombok.extern.jackson.Jacksonized;
import java.util.ArrayList;
import java.util.Optional;
@Builder
@Jacksonized
@JsonTypeName("screen")
public class ScreenTerminalMultiplexer implements TerminalMultiplexer {
@Override
public boolean requiresUnixEnvironment() {
return true;
}
@Override
public boolean isSupported() throws Exception {
if (OsType.ofLocal() == OsType.WINDOWS) {
var p = TerminalProxyManager.getProxy();
return p.isPresent() && p.get().view().findProgram("screen").isPresent();
} else {
return LocalShell.getShell().view().findProgram("screen").isPresent();
}
}
@Override
public WebtopApp getRequiredWebtopApp() {
return null;
@@ -239,6 +239,10 @@ public class TerminalLauncher {
return false;
}
if (OsType.ofLocal() == OsType.WINDOWS && multiplexer.get().requiresUnixEnvironment() && TerminalProxyManager.getProxy().isEmpty()) {
throw ErrorEventFactory.expected(new IllegalStateException("Terminal multiplexer is only supported with a WSL terminal environment on Windows"));
}
var control = TerminalProxyManager.getProxy().orElse(LocalShell.getShell());
// Throw if not supported
@@ -49,6 +49,10 @@ public interface TerminalMultiplexer {
return null;
}
boolean requiresUnixEnvironment();
boolean isSupported() throws Exception;
WebtopApp getRequiredWebtopApp();
boolean supportsSplitView();
@@ -1,6 +1,9 @@
package io.xpipe.app.terminal;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.process.LocalShell;
import io.xpipe.app.update.AppDistributionType;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.OsType;
@@ -11,6 +14,7 @@ public class TerminalMultiplexerManager {
private static final Map<UUID, TerminalMultiplexer> connectionHubRequests = new HashMap<>();
private static UUID pendingMultiplexerLaunch;
private static UUID runningMultiplexerContainer;
private static Boolean availableOnWindows;
public static void registerMultiplexerContainerLaunch(UUID uuid) {
pendingMultiplexerLaunch = uuid;
@@ -42,6 +46,19 @@ public class TerminalMultiplexerManager {
TerminalView.get().addListener(listener);
}
public static boolean isAvailableOnWindows() {
if (availableOnWindows != null) {
return availableOnWindows;
}
try {
return (availableOnWindows = LocalShell.getShell().view().findProgram("zellij").isPresent());
} catch (Exception e) {
ErrorEventFactory.fromThrowable(e).handle();
return (availableOnWindows = false);
}
}
public static Optional<TerminalMultiplexer> getEffectiveMultiplexer() {
var multiplexer = AppPrefs.get().terminalMultiplexer().getValue();
if (multiplexer == null) {
@@ -53,9 +70,20 @@ public class TerminalMultiplexerManager {
return Optional.empty();
}
// Don't apply multiplexer on webtop if it is not installed yet
if (AppDistributionType.get() == AppDistributionType.WEBTOP) {
try {
if (!multiplexer.isSupported()) {
return Optional.empty();
}
} catch (Exception e) {
ErrorEventFactory.fromThrowable(e).handle();
}
}
if (OsType.ofLocal() == OsType.WINDOWS) {
var hasProxy = AppPrefs.get().terminalProxy().getValue() != null;
if (!hasProxy) {
if (!hasProxy && multiplexer.requiresUnixEnvironment()) {
return Optional.empty();
}
}
@@ -7,6 +7,7 @@ import io.xpipe.app.process.ShellControl;
import io.xpipe.app.process.ShellScript;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.util.OsType;
import io.xpipe.app.webtop.WebtopApp;
import lombok.Builder;
import lombok.extern.jackson.Jacksonized;
@@ -19,6 +20,20 @@ import java.util.List;
@JsonTypeName("tmux")
public class TmuxTerminalMultiplexer implements TerminalMultiplexer {
@Override
public boolean requiresUnixEnvironment() {
return true;
}
@Override
public boolean isSupported() throws Exception {
if (OsType.ofLocal() == OsType.WINDOWS) {
var p = TerminalProxyManager.getProxy();
return p.isPresent() && p.get().view().findProgram("tmux").isPresent();
} else {
return LocalShell.getShell().view().findProgram("tmux").isPresent();
}
}
@Override
public WebtopApp getRequiredWebtopApp() {
return WebtopApp.TMUX;
@@ -20,6 +20,22 @@ import java.util.List;
@JsonTypeName("zellij")
public class ZellijTerminalMultiplexer implements TerminalMultiplexer {
@Override
public boolean requiresUnixEnvironment() {
return true;
}
@Override
public boolean isSupported() throws Exception {
if (OsType.ofLocal() == OsType.WINDOWS) {
var p = TerminalProxyManager.getProxy();
if (p.isPresent() && p.get().view().findProgram("zellij").isPresent()) {
return true;
}
}
return LocalShell.getShell().view().findProgram("zellij").isPresent();
}
@Override
public WebtopApp getRequiredWebtopApp() {
return WebtopApp.ZELLIJ;
+5
View File
@@ -2236,3 +2236,8 @@ configureWebtop=Configure webtop
#context: verb
transfer=Transfer
rebuild=Rebuild
webtop.displayName=XPipe Webtop
webtop.displayDescription=Access and manage a webtop environment remotely
webtopHost=Host
webtopHostDescription=The container the webtop environment is running on