From d734741289ed7cad4e4228ec07d25e0167f16ba8 Mon Sep 17 00:00:00 2001 From: crschnick Date: Sat, 13 Jun 2026 22:46:24 +0000 Subject: [PATCH] Multiplexer fixes --- .../java/io/xpipe/app/issue/TrackEvent.java | 2 +- .../io/xpipe/app/prefs/TerminalCategory.java | 6 +- .../xpipe/app/terminal/TerminalLauncher.java | 2 +- .../terminal/TerminalMultiplexerManager.java | 12 ++-- .../terminal/ZellijTerminalMultiplexer.java | 56 ++++++++++++------- 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/issue/TrackEvent.java b/app/src/main/java/io/xpipe/app/issue/TrackEvent.java index 4ecb5b98c..a92428474 100644 --- a/app/src/main/java/io/xpipe/app/issue/TrackEvent.java +++ b/app/src/main/java/io/xpipe/app/issue/TrackEvent.java @@ -82,7 +82,7 @@ public class TrackEvent { ? "\n" + (valueString .lines() - .map(line -> " | " + line) + .map(line -> " " + line) .collect(Collectors.joining("\n"))) : valueString; s.append(" ").append(e.getKey()).append("=").append(value).append("\n"); diff --git a/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java b/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java index aa112c895..f54206210 100644 --- a/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java @@ -387,8 +387,12 @@ public class TerminalCategory extends AppPrefsCategory { .addComp(choice); if (OsType.ofLocal() == OsType.WINDOWS) { options.disable(Bindings.createBooleanBinding(() -> { + if (prefs.terminalMultiplexer.getValue() != null) { + return false; + } + return TerminalProxyManager.getProxy().isEmpty() && !TerminalMultiplexerManager.isAvailableOnWindows(); - }, prefs.terminalProxy(), testCounter)); + }, prefs.terminalProxy(), prefs.terminalMultiplexer(), testCounter)); } options.addComp(test).hide(prefs.terminalMultiplexer.isNull()); return options; 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 bed7e87c0..753130777 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java @@ -240,7 +240,7 @@ public class TerminalLauncher { } 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")); + throw ErrorEventFactory.expected(new IllegalStateException("The currently active terminal multiplexer is only supported with a WSL terminal environment on Windows")); } var control = TerminalProxyManager.getProxy().orElse(LocalShell.getShell()); diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexerManager.java b/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexerManager.java index 917e6e5d3..df697e7d5 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexerManager.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexerManager.java @@ -47,6 +47,12 @@ public class TerminalMultiplexerManager { } public static boolean isAvailableOnWindows() { + // TODO: It seems like zellij is still broken on Windows + // Check back later + if (true) { + return false; + } + if (availableOnWindows != null) { return availableOnWindows; } @@ -81,12 +87,6 @@ public class TerminalMultiplexerManager { } } - if (OsType.ofLocal() == OsType.WINDOWS) { - var hasProxy = AppPrefs.get().terminalProxy().getValue() != null; - if (!hasProxy && multiplexer.requiresUnixEnvironment()) { - return Optional.empty(); - } - } return Optional.of(multiplexer); } diff --git a/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java b/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java index 3e803aea6..88c7b041c 100644 --- a/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java +++ b/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java @@ -22,6 +22,8 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { @Override public boolean requiresUnixEnvironment() { + // TODO: It seems like zellij is still broken on Windows + // Check back later return true; } @@ -68,8 +70,8 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { config.getPanes().getFirst().getDialectLaunchCommand().buildSimple(); l.addAll(List.of( "zellij attach --create-background xpipe", - "zellij -s xpipe action new-tab --name \"" + escape(config.getColoredTitle(), false, true) + "\"", - "zellij -s xpipe action write-chars -- " + escape(" " + firstCommand, true, true) + "\\;exit", + "zellij -s xpipe action new-tab --name \"" + escape(control, config.getColoredTitle(), false, true) + "\"", + "zellij -s xpipe action write-chars -- " + escape(control, " " + firstCommand, true, true) + getCommandExitLiteral(control), "zellij -s xpipe action clear", "zellij -s xpipe action write 10")); @@ -88,9 +90,9 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { l.addAll(List.of( "zellij -s xpipe action new-pane " + directionString + " --name \"" - + escape(config.getPanes().get(i).getTitle(), false, true) + + escape(control, config.getPanes().get(i).getTitle(), false, true) + "\"", - "zellij -s xpipe action write-chars -- " + escape(" " + iCommand, true, true) + "\\;exit", + "zellij -s xpipe action write-chars -- " + escape(control, " " + iCommand, true, true) + getCommandExitLiteral(control), "zellij -s xpipe action clear", "zellij -s xpipe action write 10", "zellij -s xpipe action focus-next-pane")); @@ -121,27 +123,24 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { control.view().writeTextFile(configFile, configText + "\n" + "show_startup_tips false"); } - var l = new ArrayList(); var firstConfig = config.getPanes().getFirst(); var firstCommand = firstConfig.getDialectLaunchCommand().buildSimple(); - l.add("zellij attach xpipe"); - var sc = TerminalProxyManager.getProxy().orElse(LocalShell.getShell()); // Set proper env variables for a terminal - try (var sub = sc.identicalDialectSubShell().start()) { + try (var sub = control.identicalDialectSubShell().start()) { sub.writeLine(sub.getShellDialect().prepareTerminalEnvironmentCommands(true)); - sub.command("zellij delete-session -f xpipe > /dev/null 2>&1").executeAndCheck(); + sub.command("zellij delete-session -f xpipe").executeAndCheck(); sub.command("zellij attach --create-background xpipe").executeAndCheck(); } var asyncLines = new ArrayList(); asyncLines.addAll(List.of( "sleep 0.5", - "zellij -s xpipe action new-tab --name \"" + escape(config.getColoredTitle(), false, true) + "\"", + "zellij -s xpipe action new-tab --name \"" + escape(control, config.getColoredTitle(), false, true) + "\"", "sleep 0.5", "zellij -s xpipe action go-to-tab 2", "sleep 0.5", - "zellij -s xpipe action write-chars -- " + escape(" " + firstCommand, true, true) + "\\;exit", + "zellij -s xpipe action write-chars -- " + escape(control, " " + firstCommand, true, true) + getCommandExitLiteral(control), "zellij -s xpipe action clear", "zellij -s xpipe action write 10", "zellij -s xpipe action go-to-tab 1", @@ -160,8 +159,8 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { : "--direction down"; asyncLines.addAll(List.of( "zellij -s xpipe action new-pane " + directionString + " --name \"" - + escape(config.getPanes().get(i).getTitle(), false, true) + "\"", - "zellij -s xpipe action write-chars -- " + escape(" " + iCommand, true, true) + "\\;exit", + + escape(control, config.getPanes().get(i).getTitle(), false, true) + "\"", + "zellij -s xpipe action write-chars -- " + escape(control, " " + iCommand, true, true) + getCommandExitLiteral(control), "zellij -s xpipe action clear", "zellij -s xpipe action write 10", "zellij -s xpipe action focus-next-pane")); @@ -174,9 +173,16 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { @SneakyThrows public void onSessionOpened(TerminalView.ShellSession session) { TerminalView.get().removeListener(this); + + // We might have changed the prefs meanwhile + var isActive = TerminalMultiplexerManager.getEffectiveMultiplexer() + .map(terminalMultiplexer -> terminalMultiplexer == ZellijTerminalMultiplexer.this).orElse(false); + if (!isActive) { + return; + } + ThreadHelper.runFailableAsync(() -> { - var sc = TerminalProxyManager.getProxy().orElse(LocalShell.getShell()); - var command = sc.command(String.join("\n", asyncLines)); + var command = control.command(String.join("\n", asyncLines)); // Zellij sometimes freezes command.killOnTimeout(CountDown.of().start(10_000)); command.executeAndCheck(); @@ -185,14 +191,25 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { }; TerminalView.get().addListener(listener); + var l = new ArrayList(); + l.add("zellij attach xpipe"); + l.add("Start-Sleep -s 50"); return ShellScript.lines(l); } + private String getCommandExitLiteral(ShellControl sc) { + return sc.getOsType() == OsType.WINDOWS ? "`;exit" : "\\;exit"; + } + private FilePath getConfigFile(ShellControl sc) throws Exception { if (sc.getOsType() == OsType.MACOS) { return sc.view() .userHome() .join("Library", "Application Support", "org.Zellij-Contributors.Zellij", "config.kdl"); + } else if (sc.getOsType() == OsType.WINDOWS) { + return sc.view() + .userHome() + .join(".config", "zellij", "config.kdl"); } else { return sc.view() .getEnvironmentVariable("XDG_HOME") @@ -202,13 +219,14 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { } } - private String escape(String s, boolean spaces, boolean quotes) { - var r = s.replaceAll("\\\\", "\\\\\\\\"); + private String escape(ShellControl sc, String s, boolean spaces, boolean quotes) { + var escapeString = sc.getOsType() == OsType.WINDOWS ? "`" : "\\\\"; + var r = s.replaceAll(escapeString, escapeString + escapeString); if (quotes) { - r = r.replaceAll("\"", "\\\\\""); + r = r.replaceAll("\"", escapeString + "'"); } if (spaces) { - r = r.replaceAll(" ", "\\\\ "); + r = r.replaceAll(" ", escapeString + " "); } return r; }