mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Rework terminal launching, path caching, and state management
This commit is contained in:
parent
4fda66e7db
commit
2af59af190
27 changed files with 180 additions and 131 deletions
|
@ -1,35 +1,20 @@
|
||||||
package io.xpipe.app.browser;
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
import io.xpipe.app.util.ShellControlCache;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.process.ShellDialect;
|
import io.xpipe.core.process.ShellDialect;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class OpenFileSystemCache {
|
public class OpenFileSystemCache extends ShellControlCache {
|
||||||
|
|
||||||
private final OpenFileSystemModel model;
|
private final OpenFileSystemModel model;
|
||||||
private final Map<String, Boolean> installedApplications = new HashMap<>();
|
private final String username;
|
||||||
private final Map<String, Object> multiPurposeCache = new HashMap<>();
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
public OpenFileSystemCache(OpenFileSystemModel model) {
|
public OpenFileSystemCache(OpenFileSystemModel model) throws Exception {
|
||||||
|
super(model.getFileSystem().getShell().orElseThrow());
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T get(String key) {
|
|
||||||
return (T) multiPurposeCache.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(String key, Object value) {
|
|
||||||
multiPurposeCache.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init() throws Exception {
|
|
||||||
ShellControl sc = model.getFileSystem().getShell().get();
|
ShellControl sc = model.getFileSystem().getShell().get();
|
||||||
ShellDialect d = sc.getShellDialect();
|
ShellDialect d = sc.getShellDialect();
|
||||||
username = d.printUsernameCommand(sc).readStdoutOrThrow();
|
username = d.printUsernameCommand(sc).readStdoutOrThrow();
|
||||||
|
@ -38,18 +23,4 @@ public class OpenFileSystemCache {
|
||||||
public boolean isRoot() {
|
public boolean isRoot() {
|
||||||
return username.equals("root");
|
return username.equals("root");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isApplicationInPath(String app) {
|
|
||||||
if (!installedApplications.containsKey(app)) {
|
|
||||||
try {
|
|
||||||
var b = ApplicationHelper.isInPath(
|
|
||||||
model.getFileSystem().getShell().orElseThrow(), app);
|
|
||||||
installedApplications.put(app, b);
|
|
||||||
} catch (Exception e) {
|
|
||||||
installedApplications.put(app, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return installedApplications.get(app);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ public final class OpenFileSystemModel {
|
||||||
private final BooleanProperty busy = new SimpleBooleanProperty();
|
private final BooleanProperty busy = new SimpleBooleanProperty();
|
||||||
private final BrowserModel browserModel;
|
private final BrowserModel browserModel;
|
||||||
private OpenFileSystemSavedState savedState;
|
private OpenFileSystemSavedState savedState;
|
||||||
private final OpenFileSystemCache cache = new OpenFileSystemCache(this);
|
private OpenFileSystemCache cache;
|
||||||
private final Property<ModalOverlayComp.OverlayContent> overlay = new SimpleObjectProperty<>();
|
private final Property<ModalOverlayComp.OverlayContent> overlay = new SimpleObjectProperty<>();
|
||||||
private final BooleanProperty inOverview = new SimpleBooleanProperty();
|
private final BooleanProperty inOverview = new SimpleBooleanProperty();
|
||||||
private final String name;
|
private final String name;
|
||||||
|
@ -375,7 +375,7 @@ public final class OpenFileSystemModel {
|
||||||
.map(shellControl -> shellControl.hasLocalSystemAccess())
|
.map(shellControl -> shellControl.hasLocalSystemAccess())
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
|
|
||||||
this.cache.init();
|
this.cache = new OpenFileSystemCache(this);
|
||||||
for (BrowserAction b : BrowserAction.ALL) {
|
for (BrowserAction b : BrowserAction.ALL) {
|
||||||
b.init(this);
|
b.init(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -345,7 +345,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
||||||
.getValue()
|
.getValue()
|
||||||
.getValidationResult()
|
.getValidationResult()
|
||||||
.getMessages()
|
.getMessages()
|
||||||
.get(0)
|
.getFirst()
|
||||||
.getText();
|
.getText();
|
||||||
TrackEvent.info(msg);
|
TrackEvent.info(msg);
|
||||||
var newMessage = msg;
|
var newMessage = msg;
|
||||||
|
@ -360,6 +360,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
||||||
|
|
||||||
ThreadHelper.runAsync(() -> {
|
ThreadHelper.runAsync(() -> {
|
||||||
try (var b = new BooleanScope(busy).start()) {
|
try (var b = new BooleanScope(busy).start()) {
|
||||||
|
DataStorage.get().addStoreEntryInProgress(entry.getValue());
|
||||||
entry.getValue().validateOrThrow();
|
entry.getValue().validateOrThrow();
|
||||||
finished.setValue(true);
|
finished.setValue(true);
|
||||||
PlatformThread.runLaterIfNeeded(parent::next);
|
PlatformThread.runLaterIfNeeded(parent::next);
|
||||||
|
@ -375,6 +376,8 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
||||||
ErrorEvent.unreportable(ex);
|
ErrorEvent.unreportable(ex);
|
||||||
}
|
}
|
||||||
ErrorEvent.fromThrowable(ex).omit().handle();
|
ErrorEvent.fromThrowable(ex).omit().handle();
|
||||||
|
} finally {
|
||||||
|
DataStorage.get().removeStoreEntryInProgress(entry.getValue());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -3,14 +3,15 @@ package io.xpipe.app.core.mode;
|
||||||
import io.xpipe.app.browser.BrowserModel;
|
import io.xpipe.app.browser.BrowserModel;
|
||||||
import io.xpipe.app.comp.store.StoreViewState;
|
import io.xpipe.app.comp.store.StoreViewState;
|
||||||
import io.xpipe.app.core.*;
|
import io.xpipe.app.core.*;
|
||||||
import io.xpipe.app.issue.*;
|
import io.xpipe.app.ext.ActionProvider;
|
||||||
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
import io.xpipe.app.prefs.AppPrefs;
|
import io.xpipe.app.prefs.AppPrefs;
|
||||||
import io.xpipe.app.storage.DataStorage;
|
import io.xpipe.app.storage.DataStorage;
|
||||||
import io.xpipe.app.update.XPipeDistributionType;
|
import io.xpipe.app.update.XPipeDistributionType;
|
||||||
import io.xpipe.app.util.LicenseProvider;
|
|
||||||
import io.xpipe.app.util.FileBridge;
|
import io.xpipe.app.util.FileBridge;
|
||||||
|
import io.xpipe.app.util.LicenseProvider;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.app.util.LockedSecretValue;
|
import io.xpipe.app.util.LockedSecretValue;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.util.JacksonMapper;
|
import io.xpipe.core.util.JacksonMapper;
|
||||||
|
|
||||||
public class BaseMode extends OperationMode {
|
public class BaseMode extends OperationMode {
|
||||||
|
@ -47,7 +48,7 @@ public class BaseMode extends OperationMode {
|
||||||
AppI18n.init();
|
AppI18n.init();
|
||||||
LicenseProvider.get().init();
|
LicenseProvider.get().init();
|
||||||
AppAntivirusAlert.showIfNeeded();
|
AppAntivirusAlert.showIfNeeded();
|
||||||
LocalStore.init();
|
LocalShell.init();
|
||||||
XPipeDistributionType.init();
|
XPipeDistributionType.init();
|
||||||
AppPrefs.init();
|
AppPrefs.init();
|
||||||
AppCharsets.init();
|
AppCharsets.init();
|
||||||
|
@ -56,6 +57,7 @@ public class BaseMode extends OperationMode {
|
||||||
DataStorage.init();
|
DataStorage.init();
|
||||||
AppFileWatcher.init();
|
AppFileWatcher.init();
|
||||||
FileBridge.init();
|
FileBridge.init();
|
||||||
|
ActionProvider.initProviders();
|
||||||
TrackEvent.info("mode", "Finished base components initialization");
|
TrackEvent.info("mode", "Finished base components initialization");
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ import io.xpipe.app.core.*;
|
||||||
import io.xpipe.app.ext.DataStoreProviders;
|
import io.xpipe.app.ext.DataStoreProviders;
|
||||||
import io.xpipe.app.issue.*;
|
import io.xpipe.app.issue.*;
|
||||||
import io.xpipe.app.launcher.LauncherCommand;
|
import io.xpipe.app.launcher.LauncherCommand;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.app.util.PlatformState;
|
import io.xpipe.app.util.PlatformState;
|
||||||
import io.xpipe.app.util.ThreadHelper;
|
import io.xpipe.app.util.ThreadHelper;
|
||||||
import io.xpipe.app.util.XPipeSession;
|
import io.xpipe.app.util.XPipeSession;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.util.FailableRunnable;
|
import io.xpipe.core.util.FailableRunnable;
|
||||||
import io.xpipe.core.util.XPipeDaemonMode;
|
import io.xpipe.core.util.XPipeDaemonMode;
|
||||||
import io.xpipe.core.util.XPipeInstallation;
|
import io.xpipe.core.util.XPipeInstallation;
|
||||||
|
@ -184,7 +184,7 @@ public abstract class OperationMode {
|
||||||
public static void restart() {
|
public static void restart() {
|
||||||
OperationMode.executeAfterShutdown(() -> {
|
OperationMode.executeAfterShutdown(() -> {
|
||||||
var exec = XPipeInstallation.createExternalAsyncLaunchCommand(XPipeInstallation.getLocalDefaultInstallationBasePath(), XPipeDaemonMode.GUI, "");
|
var exec = XPipeInstallation.createExternalAsyncLaunchCommand(XPipeInstallation.getLocalDefaultInstallationBasePath(), XPipeDaemonMode.GUI, "");
|
||||||
LocalStore.getShell().executeSimpleCommand(exec);
|
LocalShell.getShell().executeSimpleCommand(exec);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,16 @@ public interface ActionProvider {
|
||||||
|
|
||||||
List<ActionProvider> ALL = new ArrayList<>();
|
List<ActionProvider> ALL = new ArrayList<>();
|
||||||
|
|
||||||
|
static void initProviders() {
|
||||||
|
for (ActionProvider actionProvider : ALL) {
|
||||||
|
try {
|
||||||
|
actionProvider.init();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
ErrorEvent.fromThrowable(t).handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Loader implements ModuleLayerLoader {
|
class Loader implements ModuleLayerLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,6 +61,9 @@ public interface ActionProvider {
|
||||||
void execute() throws Exception;
|
void execute() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void init() {
|
||||||
|
}
|
||||||
|
|
||||||
default String getId() {
|
default String getId() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.app.prefs;
|
||||||
|
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.core.store.LocalStore;
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
|
@ -44,7 +44,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Optional<Path> getApplicationPath() {
|
protected Optional<Path> getApplicationPath() {
|
||||||
try (ShellControl pc = LocalStore.getShell().start()) {
|
try (ShellControl pc = LocalShell.getShell().start()) {
|
||||||
try (var c = pc.command(String.format(
|
try (var c = pc.command(String.format(
|
||||||
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister "
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister "
|
||||||
+ "-dump | grep -o \"/.*%s.app\" | grep -v -E \"Caches|TimeMachine|Temporary|.Trash|/Volumes/%s\" | uniq",
|
+ "-dump | grep -o \"/.*%s.app\" | grep -v -E \"Caches|TimeMachine|Temporary|.Trash|/Volumes/%s\" | uniq",
|
||||||
|
@ -100,7 +100,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
return pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand(executable));
|
return pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand(executable));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorEvent.fromThrowable(e).omit().handle();
|
ErrorEvent.fromThrowable(e).omit().handle();
|
||||||
|
@ -122,7 +122,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
|
|
||||||
protected Optional<Path> determineFromPath() {
|
protected Optional<Path> determineFromPath() {
|
||||||
// Try to locate if it is in the Path
|
// Try to locate if it is in the Path
|
||||||
try (var cc = LocalStore.getShell()
|
try (var cc = LocalShell.getShell()
|
||||||
.command(ShellDialects.getPlatformDefault().getWhichCommand(executable))
|
.command(ShellDialects.getPlatformDefault().getWhichCommand(executable))
|
||||||
.start()) {
|
.start()) {
|
||||||
var out = cc.readStdoutDiscardErr();
|
var out = cc.readStdoutDiscardErr();
|
||||||
|
|
|
@ -3,10 +3,10 @@ package io.xpipe.app.prefs;
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
import io.xpipe.app.util.ApplicationHelper;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.app.util.WindowsRegistry;
|
import io.xpipe.app.util.WindowsRegistry;
|
||||||
import io.xpipe.core.process.CommandBuilder;
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -152,7 +152,7 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(Path file) throws Exception {
|
public void launch(Path file) throws Exception {
|
||||||
LocalStore.getShell().executeSimpleCommand(CommandBuilder.of().add(executable).addFile(file.toString()));
|
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().add(executable).addFile(file.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,15 +3,11 @@ package io.xpipe.app.prefs;
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.storage.DataStoreColor;
|
import io.xpipe.app.storage.DataStoreColor;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
import io.xpipe.app.util.*;
|
||||||
import io.xpipe.app.util.MacOsPermissions;
|
|
||||||
import io.xpipe.app.util.ScriptHelper;
|
|
||||||
import io.xpipe.app.util.WindowsRegistry;
|
|
||||||
import io.xpipe.core.process.CommandBuilder;
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.store.FileNames;
|
import io.xpipe.core.store.FileNames;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
|
@ -28,7 +24,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
LocalStore.getShell()
|
LocalShell.getShell()
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
.executeSimpleCommand(CommandBuilder.of()
|
||||||
.add("start")
|
.add("start")
|
||||||
.addQuoted(configuration.getTitle())
|
.addQuoted(configuration.getTitle())
|
||||||
|
@ -99,7 +95,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
// backslash of a filepath to escape the closing quote in the title argument
|
// backslash of a filepath to escape the closing quote in the title argument
|
||||||
// So just remove that slash
|
// So just remove that slash
|
||||||
var fixedName = FileNames.removeTrailingSlash(configuration.getTitle());
|
var fixedName = FileNames.removeTrailingSlash(configuration.getTitle());
|
||||||
LocalStore.getShell()
|
LocalShell.getShell()
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
.executeSimpleCommand(CommandBuilder.of()
|
||||||
.add("wt", "-w", "1", "nt", "--title")
|
.add("wt", "-w", "1", "nt", "--title")
|
||||||
.addQuoted(fixedName)
|
.addQuoted(fixedName)
|
||||||
|
@ -127,7 +123,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addQuoted("colors.primary.background='%s'"
|
.addQuoted("colors.primary.background='%s'"
|
||||||
.formatted(configuration.getColor().toHexString()));
|
.formatted(configuration.getColor().toHexString()));
|
||||||
}
|
}
|
||||||
LocalStore.getShell()
|
LocalShell.getShell()
|
||||||
.executeSimpleCommand(b.add("-t")
|
.executeSimpleCommand(b.add("-t")
|
||||||
.addQuoted(configuration.getTitle())
|
.addQuoted(configuration.getTitle())
|
||||||
.add("-e")
|
.add("-e")
|
||||||
|
@ -242,7 +238,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
ApplicationHelper.checkIsInPath(pc, executable, toTranslatedString(), null);
|
ApplicationHelper.checkIsInPath(pc, executable, toTranslatedString(), null);
|
||||||
|
|
||||||
var toExecute = CommandBuilder.of()
|
var toExecute = CommandBuilder.of()
|
||||||
|
@ -478,7 +474,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
LocalStore.getShell()
|
LocalShell.getShell()
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
.executeSimpleCommand(CommandBuilder.of()
|
||||||
.add("open", "-a")
|
.add("open", "-a")
|
||||||
.addQuoted("Alacritty.app")
|
.addQuoted("Alacritty.app")
|
||||||
|
@ -502,7 +498,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
pc.osascriptCommand(String.format(
|
pc.osascriptCommand(String.format(
|
||||||
"""
|
"""
|
||||||
if application "Kitty" is running then
|
if application "Kitty" is running then
|
||||||
|
@ -594,7 +590,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
var suffix = "\"" + configuration.getScriptFile().replaceAll("\"", "\\\\\"") + "\"";
|
var suffix = "\"" + configuration.getScriptFile().replaceAll("\"", "\\\\\"") + "\"";
|
||||||
pc.osascriptCommand(String.format(
|
pc.osascriptCommand(String.format(
|
||||||
"""
|
"""
|
||||||
|
@ -627,7 +623,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
var format = custom.toLowerCase(Locale.ROOT).contains("$cmd") ? custom : custom + " $CMD";
|
var format = custom.toLowerCase(Locale.ROOT).contains("$cmd") ? custom : custom + " $CMD";
|
||||||
try (var pc = LocalStore.getShell()) {
|
try (var pc = LocalShell.getShell()) {
|
||||||
var toExecute = ApplicationHelper.replaceFileArgument(format, "CMD", configuration.getScriptFile());
|
var toExecute = ApplicationHelper.replaceFileArgument(format, "CMD", configuration.getScriptFile());
|
||||||
// We can't be sure whether the command is blocking or not, so always make it not blocking
|
// We can't be sure whether the command is blocking or not, so always make it not blocking
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
||||||
|
@ -663,7 +659,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
throw new IllegalStateException("iTerm installation not found");
|
throw new IllegalStateException("iTerm installation not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
var a = app.get().toString();
|
var a = app.get().toString();
|
||||||
pc.osascriptCommand(String.format(
|
pc.osascriptCommand(String.format(
|
||||||
"""
|
"""
|
||||||
|
@ -695,7 +691,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
LocalStore.getShell()
|
LocalShell.getShell()
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
.executeSimpleCommand(CommandBuilder.of()
|
||||||
.add("open", "-a")
|
.add("open", "-a")
|
||||||
.addQuoted("Tabby.app")
|
.addQuoted("Tabby.app")
|
||||||
|
@ -721,7 +717,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
pc.osascriptCommand(String.format(
|
pc.osascriptCommand(String.format(
|
||||||
"""
|
"""
|
||||||
tell application "Warp" to activate
|
tell application "Warp" to activate
|
||||||
|
@ -756,7 +752,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
return pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand(executable));
|
return pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand(executable));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorEvent.fromThrowable(e).omit().handle();
|
ErrorEvent.fromThrowable(e).omit().handle();
|
||||||
|
@ -779,7 +775,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
try (ShellControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
if (!ApplicationHelper.isInPath(pc, executable)) {
|
if (!ApplicationHelper.isInPath(pc, executable)) {
|
||||||
throw ErrorEvent.unreportable(
|
throw ErrorEvent.unreportable(
|
||||||
new IOException(
|
new IOException(
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class TroubleshootComp extends Comp<CompStructure<?>> {
|
||||||
sc.executeSimpleCommand(
|
sc.executeSimpleCommand(
|
||||||
ScriptHelper.createDetachCommand(sc, "\"" + script + "\""));
|
ScriptHelper.createDetachCommand(sc, "\"" + script + "\""));
|
||||||
} else {
|
} else {
|
||||||
TerminalHelper.open("XPipe Debug", LocalStore.getShell().command("\"" + script + "\""));
|
TerminalHelper.open("XPipe Debug", LocalShell.getShell().command("\"" + script + "\""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,8 @@ public class DataStateProviderImpl extends DataStateProvider {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entry = DataStorage.get().getStoreEntryIfPresent(store);
|
var entry = DataStorage.get().getStoreEntryIfPresent(store)
|
||||||
|
.or(() -> DataStorage.get().getStoreEntryInProgressIfPresent(store));
|
||||||
if (entry.isEmpty()) {
|
if (entry.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,8 @@ public abstract class DataStorage {
|
||||||
@Getter
|
@Getter
|
||||||
private final List<StorageListener> listeners = new CopyOnWriteArrayList<>();
|
private final List<StorageListener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
private final Map<DataStoreEntry, DataStoreEntry> storeEntriesInProgress = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
protected final ReentrantLock busyIo = new ReentrantLock();
|
protected final ReentrantLock busyIo = new ReentrantLock();
|
||||||
|
|
||||||
public DataStorage() {
|
public DataStorage() {
|
||||||
|
@ -356,6 +358,14 @@ public abstract class DataStorage {
|
||||||
this.listeners.forEach(l -> l.onCategoryAdd(cat));
|
this.listeners.forEach(l -> l.onCategoryAdd(cat));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addStoreEntryInProgress(@NonNull DataStoreEntry e) {
|
||||||
|
this.storeEntriesInProgress.put(e, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeStoreEntryInProgress(@NonNull DataStoreEntry e) {
|
||||||
|
this.storeEntriesInProgress.remove(e);
|
||||||
|
}
|
||||||
|
|
||||||
public DataStoreEntry addStoreEntryIfNotPresent(@NonNull DataStoreEntry e) {
|
public DataStoreEntry addStoreEntryIfNotPresent(@NonNull DataStoreEntry e) {
|
||||||
var found = storeEntries.get(e);
|
var found = storeEntries.get(e);
|
||||||
if (found != null) {
|
if (found != null) {
|
||||||
|
@ -638,6 +648,12 @@ public abstract class DataStorage {
|
||||||
return getStoreEntryIfPresent(store).orElseThrow(() -> new IllegalArgumentException("Store not found"));
|
return getStoreEntryIfPresent(store).orElseThrow(() -> new IllegalArgumentException("Store not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<DataStoreEntry> getStoreEntryInProgressIfPresent(@NonNull DataStore store) {
|
||||||
|
return storeEntriesInProgress.keySet().stream()
|
||||||
|
.filter(n -> n.getStore() == store)
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<DataStoreEntry> getStoreEntryIfPresent(@NonNull DataStore store) {
|
public Optional<DataStoreEntry> getStoreEntryIfPresent(@NonNull DataStore store) {
|
||||||
return storeEntriesSet.stream()
|
return storeEntriesSet.stream()
|
||||||
.filter(n -> n.getStore() == store
|
.filter(n -> n.getStore() == store
|
||||||
|
|
|
@ -4,8 +4,8 @@ import io.xpipe.app.core.AppCache;
|
||||||
import io.xpipe.app.core.AppProperties;
|
import io.xpipe.app.core.AppProperties;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.app.util.XPipeSession;
|
import io.xpipe.app.util.XPipeSession;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.util.ModuleHelper;
|
import io.xpipe.core.util.ModuleHelper;
|
||||||
import io.xpipe.core.util.XPipeInstallation;
|
import io.xpipe.core.util.XPipeInstallation;
|
||||||
|
@ -79,11 +79,11 @@ public enum XPipeDistributionType {
|
||||||
return PORTABLE;
|
return PORTABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LocalStore.isLocalShellInitialized()) {
|
if (!LocalShell.isLocalShellInitialized()) {
|
||||||
return UNKNOWN;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var sc = LocalStore.getShell()) {
|
try (var sc = LocalShell.getShell()) {
|
||||||
// In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the production behavior
|
// In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the production behavior
|
||||||
if (OsType.getLocal().equals(OsType.WINDOWS)) {
|
if (OsType.getLocal().equals(OsType.WINDOWS)) {
|
||||||
try (var chocoOut =
|
try (var chocoOut =
|
||||||
|
|
|
@ -4,7 +4,6 @@ import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
import io.xpipe.app.storage.DataStoreEntry;
|
import io.xpipe.app.storage.DataStoreEntry;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.util.FailableSupplier;
|
import io.xpipe.core.util.FailableSupplier;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -24,7 +23,7 @@ public class ApplicationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void executeLocalApplication(Function<ShellControl, String> s, boolean detach) throws Exception {
|
public static void executeLocalApplication(Function<ShellControl, String> s, boolean detach) throws Exception {
|
||||||
try (var sc = LocalStore.getShell().start()) {
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
var cmd = detach ? ScriptHelper.createDetachCommand(sc, s.apply(sc)) : s.apply(sc);
|
var cmd = detach ? ScriptHelper.createDetachCommand(sc, s.apply(sc)) : s.apply(sc);
|
||||||
TrackEvent.withDebug("proc", "Executing local application")
|
TrackEvent.withDebug("proc", "Executing local application")
|
||||||
.tag("command", cmd)
|
.tag("command", cmd)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package io.xpipe.app.util;
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
@ -12,9 +11,9 @@ public class DesktopHelper {
|
||||||
|
|
||||||
public static Path getDesktopDirectory() throws Exception {
|
public static Path getDesktopDirectory() throws Exception {
|
||||||
if (OsType.getLocal() == OsType.WINDOWS) {
|
if (OsType.getLocal() == OsType.WINDOWS) {
|
||||||
return Path.of(LocalStore.getLocalPowershell().executeSimpleStringCommand("[Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)"));
|
return Path.of(LocalShell.getLocalPowershell().executeSimpleStringCommand("[Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)"));
|
||||||
} else if (OsType.getLocal() == OsType.LINUX) {
|
} else if (OsType.getLocal() == OsType.LINUX) {
|
||||||
try (var cmd = LocalStore.getShell().command("xdg-user-dir DESKTOP").start()) {
|
try (var cmd = LocalShell.getShell().command("xdg-user-dir DESKTOP").start()) {
|
||||||
var read = cmd.readStdoutDiscardErr();
|
var read = cmd.readStdoutDiscardErr();
|
||||||
var exit = cmd.getExitCode();
|
var exit = cmd.getExitCode();
|
||||||
if (exit == 0) {
|
if (exit == 0) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package io.xpipe.app.util;
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.util.XPipeInstallation;
|
import io.xpipe.core.util.XPipeInstallation;
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ public class DesktopShortcuts {
|
||||||
%%PWS%% -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('%%SHORTCUT%%'); $S.IconLocation='%s'; $S.WindowStyle=7; $S.TargetPath = '%%TARGET%%'; $S.Arguments = 'open %s'; $S.Save()"
|
%%PWS%% -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('%%SHORTCUT%%'); $S.IconLocation='%s'; $S.WindowStyle=7; $S.TargetPath = '%%TARGET%%'; $S.Arguments = 'open %s'; $S.Save()"
|
||||||
""",
|
""",
|
||||||
shortcutTarget, shortcutPath, icon, target);
|
shortcutTarget, shortcutPath, icon, target);
|
||||||
LocalStore.getShell().executeSimpleCommand(content);
|
LocalShell.getShell().executeSimpleCommand(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createLinuxShortcut(String target, String name) throws Exception {
|
private static void createLinuxShortcut(String target, String name) throws Exception {
|
||||||
|
@ -55,7 +54,7 @@ public class DesktopShortcuts {
|
||||||
""",
|
""",
|
||||||
exec, target);
|
exec, target);
|
||||||
|
|
||||||
try (var pc = LocalStore.getShell()) {
|
try (var pc = LocalShell.getShell()) {
|
||||||
pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/MacOS"));
|
pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/MacOS"));
|
||||||
pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/Resources"));
|
pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/Resources"));
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.app.util;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.prefs.AppPrefs;
|
import io.xpipe.app.prefs.AppPrefs;
|
||||||
import io.xpipe.core.store.FileNames;
|
import io.xpipe.core.store.FileNames;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.process.CommandControl;
|
import io.xpipe.core.process.CommandControl;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.store.FileSystem;
|
import io.xpipe.core.store.FileSystem;
|
||||||
|
@ -71,7 +70,7 @@ public class FileOpener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void openInDefaultApplication(String file) {
|
public static void openInDefaultApplication(String file) {
|
||||||
try (var pc = LocalStore.getShell().start()) {
|
try (var pc = LocalShell.getShell().start()) {
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
||||||
pc.executeSimpleCommand("start \"\" \"" + file + "\"");
|
pc.executeSimpleCommand("start \"\" \"" + file + "\"");
|
||||||
} else if (pc.getOsType().equals(OsType.LINUX)) {
|
} else if (pc.getOsType().equals(OsType.LINUX)) {
|
||||||
|
|
40
app/src/main/java/io/xpipe/app/util/LocalShell.java
Normal file
40
app/src/main/java/io/xpipe/app/util/LocalShell.java
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
|
import io.xpipe.core.process.ProcessControlProvider;
|
||||||
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
import io.xpipe.core.process.ShellDialects;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class LocalShell {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private static ShellControlCache localCache;
|
||||||
|
private static ShellControl local;
|
||||||
|
private static ShellControl localPowershell;
|
||||||
|
|
||||||
|
public static void init() throws Exception {
|
||||||
|
local = ProcessControlProvider.get().createLocalProcessControl(false).start();
|
||||||
|
localCache = new ShellControlCache(local);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShellControl getLocalPowershell() throws Exception {
|
||||||
|
if (localPowershell == null) {
|
||||||
|
localPowershell = ProcessControlProvider.get().createLocalProcessControl(true)
|
||||||
|
.subShell(ShellDialects.POWERSHELL)
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
return localPowershell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLocalShellInitialized() {
|
||||||
|
return local != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShellControl getShell() {
|
||||||
|
if (local == null) {
|
||||||
|
throw new IllegalStateException("Local shell not initialized yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package io.xpipe.app.util;
|
||||||
|
|
||||||
import io.xpipe.app.core.AppI18n;
|
import io.xpipe.app.core.AppI18n;
|
||||||
import io.xpipe.app.core.AppWindowHelper;
|
import io.xpipe.app.core.AppWindowHelper;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
|
@ -15,7 +14,7 @@ public class MacOsPermissions {
|
||||||
public static boolean waitForAccessibilityPermissions() throws Exception {
|
public static boolean waitForAccessibilityPermissions() throws Exception {
|
||||||
AtomicReference<Alert> alert = new AtomicReference<>();
|
AtomicReference<Alert> alert = new AtomicReference<>();
|
||||||
var state = new SimpleBooleanProperty(true);
|
var state = new SimpleBooleanProperty(true);
|
||||||
try (var pc = LocalStore.getShell().start()) {
|
try (var pc = LocalShell.getShell().start()) {
|
||||||
while (state.get()) {
|
while (state.get()) {
|
||||||
// We can't wait in the platform thread, so just return instantly
|
// We can't wait in the platform thread, so just return instantly
|
||||||
if (Platform.isFxApplicationThread()) {
|
if (Platform.isFxApplicationThread()) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.app.util;
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
import io.xpipe.core.process.*;
|
import io.xpipe.core.process.*;
|
||||||
import io.xpipe.core.store.FileNames;
|
import io.xpipe.core.store.FileNames;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.util.SecretValue;
|
import io.xpipe.core.util.SecretValue;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ public class ScriptHelper {
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static String createLocalExecScript(String content) {
|
public static String createLocalExecScript(String content) {
|
||||||
try (var l = LocalStore.getShell().start()) {
|
try (var l = LocalShell.getShell().start()) {
|
||||||
return createExecScript(l, content);
|
return createExecScript(l, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
app/src/main/java/io/xpipe/app/util/ShellControlCache.java
Normal file
51
app/src/main/java/io/xpipe/app/util/ShellControlCache.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class ShellControlCache {
|
||||||
|
|
||||||
|
private final ShellControl shellControl;
|
||||||
|
private final Map<String, Boolean> installedApplications = new HashMap<>();
|
||||||
|
private final Map<String, Object> multiPurposeCache = new HashMap<>();
|
||||||
|
|
||||||
|
public ShellControlCache(ShellControl shellControl) {
|
||||||
|
this.shellControl = shellControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(String key) {
|
||||||
|
return (T) multiPurposeCache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getOrDefault(String key, T val) {
|
||||||
|
return (T) multiPurposeCache.getOrDefault(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String key, Object value) {
|
||||||
|
multiPurposeCache.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIfAbsent(String key, Supplier<Object> value) {
|
||||||
|
multiPurposeCache.computeIfAbsent(key, s -> value.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isApplicationInPath(String app) {
|
||||||
|
if (!installedApplications.containsKey(app)) {
|
||||||
|
try {
|
||||||
|
var b = ApplicationHelper.isInPath(shellControl, app);
|
||||||
|
installedApplications.put(app, b);
|
||||||
|
} catch (Exception e) {
|
||||||
|
installedApplications.put(app, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return installedApplications.get(app);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ public class TerminalHelper {
|
||||||
: "";
|
: "";
|
||||||
var cleanTitle = (title != null ? title : entry != null ? entry.getName() : "?");
|
var cleanTitle = (title != null ? title : entry != null ? entry.getName() : "?");
|
||||||
var adjustedTitle = prefix + cleanTitle;
|
var adjustedTitle = prefix + cleanTitle;
|
||||||
var file = ScriptHelper.createLocalExecScript(cc.prepareTerminalOpen(new TerminalInitScriptConfig(adjustedTitle, type.shouldClear())));
|
var file = ScriptHelper.createLocalExecScript(cc.prepareTerminalOpen(new TerminalInitScriptConfig(adjustedTitle, type.shouldClear(), color != null)));
|
||||||
var config = new ExternalTerminalType.LaunchConfiguration(entry != null ? color : null, adjustedTitle, cleanTitle, file);
|
var config = new ExternalTerminalType.LaunchConfiguration(entry != null ? color : null, adjustedTitle, cleanTitle, file);
|
||||||
try {
|
try {
|
||||||
type.launch(config);
|
type.launch(config);
|
||||||
|
|
|
@ -172,7 +172,7 @@ public interface ShellControl extends ProcessControl {
|
||||||
FailableSupplier<SecretValue> getElevationPassword();
|
FailableSupplier<SecretValue> getElevationPassword();
|
||||||
|
|
||||||
default ShellControl subShell(@NonNull ShellDialect type) {
|
default ShellControl subShell(@NonNull ShellDialect type) {
|
||||||
return subShell(p -> type.getOpenCommand(), (sc, command) -> command)
|
return subShell(p -> type.getOpenCommand(), (sc, command) -> command != null ? command : type.getLoginOpenCommand())
|
||||||
.elevationPassword(getElevationPassword());
|
.elevationPassword(getElevationPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ public interface ShellDialect {
|
||||||
|
|
||||||
void prepareDumbTerminalCommands(ShellControl sc) throws Exception;
|
void prepareDumbTerminalCommands(ShellControl sc) throws Exception;
|
||||||
|
|
||||||
String prepareProperTerminalCommands();
|
String prepareTerminalEnvironmentCommands();
|
||||||
|
|
||||||
String appendToPathVariableCommand(String entry);
|
String appendToPathVariableCommand(String entry);
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@ import lombok.Value;
|
||||||
public class TerminalInitScriptConfig {
|
public class TerminalInitScriptConfig {
|
||||||
|
|
||||||
public static TerminalInitScriptConfig ofName(String name) {
|
public static TerminalInitScriptConfig ofName(String name) {
|
||||||
return new TerminalInitScriptConfig(name, true);
|
return new TerminalInitScriptConfig(name, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
String displayName;
|
String displayName;
|
||||||
boolean clearScreen;
|
boolean clearScreen;
|
||||||
|
boolean hasColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.core.store;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
import io.xpipe.core.process.ProcessControlProvider;
|
import io.xpipe.core.process.ProcessControlProvider;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
|
||||||
import io.xpipe.core.process.ShellStoreState;
|
import io.xpipe.core.process.ShellStoreState;
|
||||||
import io.xpipe.core.util.JacksonizedValue;
|
import io.xpipe.core.util.JacksonizedValue;
|
||||||
|
|
||||||
|
@ -15,44 +14,6 @@ public class LocalStore extends JacksonizedValue implements ShellStore, Stateful
|
||||||
return ShellStoreState.class;
|
return ShellStoreState.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ShellControl local;
|
|
||||||
private static ShellControl localPowershell;
|
|
||||||
private static FileSystem localFileSystem;
|
|
||||||
|
|
||||||
public static void init() throws Exception {
|
|
||||||
local = ProcessControlProvider.get().createLocalProcessControl(false).start();
|
|
||||||
localFileSystem = new ConnectionFileSystem(ProcessControlProvider.get().createLocalProcessControl(false), new LocalStore());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ShellControl getLocalPowershell() throws Exception {
|
|
||||||
if (localPowershell == null) {
|
|
||||||
localPowershell = ProcessControlProvider.get().createLocalProcessControl(true)
|
|
||||||
.subShell(ShellDialects.POWERSHELL)
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
return localPowershell;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isLocalShellInitialized() {
|
|
||||||
return local != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ShellControl getShell() {
|
|
||||||
if (local == null) {
|
|
||||||
throw new IllegalStateException("Local shell not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileSystem getFileSystem() {
|
|
||||||
if (localFileSystem == null) {
|
|
||||||
throw new IllegalStateException("Local file system not initialized yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
return localFileSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ShellControl control() {
|
public ShellControl control() {
|
||||||
var pc = ProcessControlProvider.get().createLocalProcessControl(true);
|
var pc = ProcessControlProvider.get().createLocalProcessControl(true);
|
||||||
|
|
|
@ -3,8 +3,8 @@ package io.xpipe.ext.base.browser;
|
||||||
import io.xpipe.app.browser.BrowserEntry;
|
import io.xpipe.app.browser.BrowserEntry;
|
||||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||||
import io.xpipe.app.browser.action.LeafAction;
|
import io.xpipe.app.browser.action.LeafAction;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.core.store.FileNames;
|
import io.xpipe.core.store.FileNames;
|
||||||
import io.xpipe.core.store.LocalStore;
|
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ public class OpenNativeFileDetailsAction implements LeafAction {
|
||||||
// The Windows shell invoke verb functionality behaves kinda weirdly and only shows the window as
|
// The Windows shell invoke verb functionality behaves kinda weirdly and only shows the window as
|
||||||
// long as the parent process is running.
|
// long as the parent process is running.
|
||||||
// So let's keep one process running
|
// So let's keep one process running
|
||||||
LocalStore.getLocalPowershell().command(content).notComplex().execute();
|
LocalShell.getLocalPowershell().command(content).notComplex().execute();
|
||||||
}
|
}
|
||||||
case OsType.Linux linux -> {
|
case OsType.Linux linux -> {
|
||||||
var dbus = String.format(
|
var dbus = String.format(
|
||||||
|
|
Loading…
Reference in a new issue