From e7280e2ef5cd78d9f3cb101b4c558f7e71aed66a Mon Sep 17 00:00:00 2001 From: crschnick Date: Sat, 31 Jan 2026 23:06:04 +0000 Subject: [PATCH] Rework [stage] --- .../xpipe/app/spice/ExternalSpiceClient.java | 12 +- ...ient.java => RemoteViewerSpiceClient.java} | 14 +- .../app/terminal/TerminalDockHubManager.java | 1 + .../xpipe/app/terminal/TerminalDockView.java | 9 ++ .../io/xpipe/app/vnc/ExternalVncClient.java | 5 +- .../xpipe/app/vnc/RemoteViewerVncClient.java | 153 ++++++++++++++++++ dist/changelog/21.0.md | 3 + lang/strings/fixed_en.properties | 1 + version | 2 +- 9 files changed, 185 insertions(+), 15 deletions(-) rename app/src/main/java/io/xpipe/app/spice/{VirtViewerSpiceClient.java => RemoteViewerSpiceClient.java} (84%) create mode 100644 app/src/main/java/io/xpipe/app/vnc/RemoteViewerVncClient.java diff --git a/app/src/main/java/io/xpipe/app/spice/ExternalSpiceClient.java b/app/src/main/java/io/xpipe/app/spice/ExternalSpiceClient.java index c15f6b755..2a1db62cb 100644 --- a/app/src/main/java/io/xpipe/app/spice/ExternalSpiceClient.java +++ b/app/src/main/java/io/xpipe/app/spice/ExternalSpiceClient.java @@ -20,13 +20,13 @@ public interface ExternalSpiceClient extends PrefsValue { return switch (OsType.ofLocal()) { case OsType.Linux ignored -> { - yield new VirtViewerSpiceClient.Linux(); + yield new RemoteViewerSpiceClient.Linux(); } case OsType.MacOs ignored -> { - yield new VirtViewerSpiceClient.MacOs(); + yield new RemoteViewerSpiceClient.MacOs(); } case OsType.Windows ignored -> { - yield new VirtViewerSpiceClient.Windows(); + yield new RemoteViewerSpiceClient.Windows(); } }; } @@ -44,13 +44,13 @@ public interface ExternalSpiceClient extends PrefsValue { var l = new ArrayList>(); switch (OsType.ofLocal()) { case OsType.Linux ignored -> { - l.add(VirtViewerSpiceClient.Linux.class); + l.add(RemoteViewerSpiceClient.Linux.class); } case OsType.MacOs ignored -> { - l.add(VirtViewerSpiceClient.MacOs.class); + l.add(RemoteViewerSpiceClient.MacOs.class); } case OsType.Windows ignored -> { - l.add(VirtViewerSpiceClient.Windows.class); + l.add(RemoteViewerSpiceClient.Windows.class); } } l.add(CustomSpiceClient.class); diff --git a/app/src/main/java/io/xpipe/app/spice/VirtViewerSpiceClient.java b/app/src/main/java/io/xpipe/app/spice/RemoteViewerSpiceClient.java similarity index 84% rename from app/src/main/java/io/xpipe/app/spice/VirtViewerSpiceClient.java rename to app/src/main/java/io/xpipe/app/spice/RemoteViewerSpiceClient.java index 4837cd442..adfc5f2ce 100644 --- a/app/src/main/java/io/xpipe/app/spice/VirtViewerSpiceClient.java +++ b/app/src/main/java/io/xpipe/app/spice/RemoteViewerSpiceClient.java @@ -14,7 +14,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; -public abstract class VirtViewerSpiceClient implements ExternalSpiceClient { +public abstract class RemoteViewerSpiceClient implements ExternalSpiceClient { protected CommandBuilder createBuilder(SpiceLaunchConfig configuration) { var builder = CommandBuilder.of().addFile(configuration.getFile()); @@ -28,8 +28,8 @@ public abstract class VirtViewerSpiceClient implements ExternalSpiceClient { @Builder @Jacksonized - @JsonTypeName("virtViewer") - public static class Windows extends VirtViewerSpiceClient implements ExternalApplicationType.WindowsType { + @JsonTypeName("remoteViewer") + public static class Windows extends RemoteViewerSpiceClient implements ExternalApplicationType.WindowsType { @Override public boolean detach() { @@ -68,8 +68,8 @@ public abstract class VirtViewerSpiceClient implements ExternalSpiceClient { @Builder @Jacksonized - @JsonTypeName("virtViewer") - public static class Linux extends VirtViewerSpiceClient implements ExternalApplicationType.LinuxApplication { + @JsonTypeName("remoteViewer") + public static class Linux extends RemoteViewerSpiceClient implements ExternalApplicationType.LinuxApplication { @Override public void launch(SpiceLaunchConfig configuration) throws Exception { @@ -95,8 +95,8 @@ public abstract class VirtViewerSpiceClient implements ExternalSpiceClient { @Builder @Jacksonized - @JsonTypeName("virtViewer") - public static class MacOs extends VirtViewerSpiceClient implements ExternalApplicationType.PathApplication { + @JsonTypeName("remoteViewer") + public static class MacOs extends RemoteViewerSpiceClient implements ExternalApplicationType.PathApplication { @Override public void launch(SpiceLaunchConfig configuration) throws Exception { diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java index 0295746f4..0d2bd4ef6 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java @@ -208,6 +208,7 @@ public class TerminalDockHubManager { public void refreshDockStatus() { dockModel.clearDeadTerminals(); + dockModel.updateCustomBounds(); var running = dockModel.isRunning(); if (!running) { diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java b/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java index d849d186c..f4b5e586f 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java @@ -44,6 +44,10 @@ public class TerminalDockView { return terminalInstances.stream().noneMatch(terminal -> terminal.isActive()); } + public synchronized void updateCustomBounds() { + terminalInstances.forEach(terminal -> terminal.updateBoundsState()); + } + public synchronized void trackTerminal(ControllableTerminalSession terminal, boolean dock) { if (!terminalInstances.add(terminal)) { return; @@ -64,6 +68,11 @@ public class TerminalDockView { terminal.updatePosition(windowBoundsFunction.apply(viewBounds)); }, Duration.ofMillis(100)); + GlobalTimer.delay( + () -> { + terminal.updatePosition(windowBoundsFunction.apply(viewBounds)); + }, + Duration.ofMillis(1000)); } } } diff --git a/app/src/main/java/io/xpipe/app/vnc/ExternalVncClient.java b/app/src/main/java/io/xpipe/app/vnc/ExternalVncClient.java index 27dd0ae90..9c7c73c9d 100644 --- a/app/src/main/java/io/xpipe/app/vnc/ExternalVncClient.java +++ b/app/src/main/java/io/xpipe/app/vnc/ExternalVncClient.java @@ -35,17 +35,20 @@ public interface ExternalVncClient { case OsType.Linux ignored -> { l.add(RemminaVncClient.class); l.add(TigerVncClient.Linux.class); + l.add(RemoteViewerVncClient.Linux.class); l.add(RealVncClient.Linux.class); } case OsType.MacOs ignored -> { l.add(ScreenSharingVncClient.class); l.add(TigerVncClient.MacOs.class); + l.add(RemoteViewerVncClient.MacOs.class); l.add(RealVncClient.MacOs.class); } case OsType.Windows ignored -> { l.add(TigerVncClient.Windows.class); - l.add(RealVncClient.Windows.class); l.add(TightVncClient.class); + l.add(RemoteViewerVncClient.Windows.class); + l.add(RealVncClient.Windows.class); } } l.add(CustomVncClient.class); diff --git a/app/src/main/java/io/xpipe/app/vnc/RemoteViewerVncClient.java b/app/src/main/java/io/xpipe/app/vnc/RemoteViewerVncClient.java new file mode 100644 index 000000000..18ccd12e9 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/vnc/RemoteViewerVncClient.java @@ -0,0 +1,153 @@ +package io.xpipe.app.vnc; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.xpipe.app.core.AppLocalTemp; +import io.xpipe.app.core.AppSystemInfo; +import io.xpipe.app.issue.ErrorEventFactory; +import io.xpipe.app.prefs.ExternalApplicationType; +import io.xpipe.app.process.CommandBuilder; +import io.xpipe.app.process.OsFileSystem; +import io.xpipe.app.spice.ExternalSpiceClient; +import io.xpipe.app.spice.SpiceLaunchConfig; +import io.xpipe.app.util.RdpConfig; +import lombok.Builder; +import lombok.extern.jackson.Jacksonized; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +public abstract class RemoteViewerVncClient implements ExternalVncClient { + + protected CommandBuilder createBuilder(VncLaunchConfig configuration) throws Exception { + var vv = """ + [virt-viewer] + type=vnc + host=%s + port=%s + title=%s + """.formatted(configuration.getHost(), configuration.getPort(), configuration.getTitle()); + + var user = configuration.retrieveUsername(); + if (user.isPresent()) { + vv += "username=" + user.get() + "\n"; + } + + var pass = configuration.retrievePassword(); + if (pass.isPresent()) { + vv += "password=" + pass.get().getSecretValue() + "\n"; + } + + var file = writeVncConfigFile(configuration.getTitle(), vv); + var builder = CommandBuilder.of().addFile(file); + return builder; + } + + private Path writeVncConfigFile(String title, String content) throws Exception { + var name = OsFileSystem.ofLocal().makeFileSystemCompatible(title); + var file = AppLocalTemp.getLocalTempDataDirectory("vnc").resolve(name + ".vv"); + Files.createDirectories(file.getParent()); + Files.writeString(file, content); + return file; + } + + @Override + public String getWebsite() { + return "https://virt-manager.org"; + } + + @Override + public boolean supportsPasswords() { + return true; + } + + @Builder + @Jacksonized + @JsonTypeName("remoteViewer") + public static class Windows extends RemoteViewerVncClient implements ExternalApplicationType.WindowsType { + + @Override + public boolean detach() { + return false; + } + + @Override + public String getExecutable() { + return "remote-viewer.exe"; + } + + @Override + public Optional determineInstallation() { + try (var stream = Files.list(AppSystemInfo.ofWindows().getProgramFiles())) { + var l = stream.toList(); + var found = l.stream() + .filter(path -> path.toString().contains("VirtViewer")) + .findFirst(); + if (found.isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(found.get().resolve("bin", "remote-viewer.exe")); + } catch (IOException e) { + ErrorEventFactory.fromThrowable(e).handle(); + return Optional.empty(); + } + } + + @Override + public void launch(VncLaunchConfig configuration) throws Exception { + var builder = createBuilder(configuration); + launch(builder); + } + } + + @Builder + @Jacksonized + @JsonTypeName("remoteViewer") + public static class Linux extends RemoteViewerVncClient implements ExternalApplicationType.LinuxApplication { + + @Override + public void launch(VncLaunchConfig configuration) throws Exception { + var builder = createBuilder(configuration); + launch(builder); + } + + @Override + public String getExecutable() { + return "remote-viewer"; + } + + @Override + public boolean detach() { + return true; + } + + @Override + public String getFlatpakId() { + return "org.virt_manager.virt-viewer"; + } + } + + @Builder + @Jacksonized + @JsonTypeName("remoteViewer") + public static class MacOs extends RemoteViewerVncClient implements ExternalApplicationType.PathApplication { + + @Override + public void launch(VncLaunchConfig configuration) throws Exception { + var builder = createBuilder(configuration); + launch(builder); + } + + @Override + public String getExecutable() { + return "remote-viewer"; + } + + @Override + public boolean detach() { + return true; + } + } +} diff --git a/dist/changelog/21.0.md b/dist/changelog/21.0.md index 75a528261..f1fdd6b7c 100644 --- a/dist/changelog/21.0.md +++ b/dist/changelog/21.0.md @@ -49,10 +49,13 @@ The scripting system has been completely reworked with the goal of becoming simp - Add support to automatically wait in terminal until serial port is connected - Creating a new category will now automatically focus the text field to rename it - Add support for Yakuake terminal +- Add support for virt-viewer as a VNC client ## Fixes - Fix various performance issues +- Fix docker refresh action taking very long when compose projects were available +- Fix automatic use gateway setting detection for VMs - Fix various entries like SSH connections or SSH config entries sometimes disappearing on restart - Fix SSH config write, e.g. for vscode SSH, not properly passing all configured options - Fix SSH config identity detection not working for patterns in host entries diff --git a/lang/strings/fixed_en.properties b/lang/strings/fixed_en.properties index 65ed01be1..32bebf60a 100644 --- a/lang/strings/fixed_en.properties +++ b/lang/strings/fixed_en.properties @@ -157,3 +157,4 @@ ed25519Sk=ED25519 (FIDO2) westonEditor=Weston Editor passbolt=Passbolt yakuake=Yakuake +remoteViewer=Virt viewer diff --git a/version b/version index d352ddf67..e546b2e4b 100644 --- a/version +++ b/version @@ -1 +1 @@ -21.0-4 +21.0-5