Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion EasyWindowsTerminalControl/EasyTerminalControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ public string StartupCommandLine {
set => SetValue(StartupCommandLineProperty, value);
}

public string WorkingDirectory {
get => (string)GetValue(WorkingDirectoryProperty);
set => SetValue(WorkingDirectoryProperty, value);
}

public bool LogConPTYOutput {
get => (bool)GetValue(LogConPTYOutputProperty);
set => SetValue(LogConPTYOutputProperty, value);
Expand Down Expand Up @@ -215,7 +220,12 @@ private void StartTerm(int column_width, int row_height) {
var cmd = StartupCommandLine;//thread safety for dp
var term = ConPTYTerm;
var logOutput = LogConPTYOutput;
Task.Run(() => term.Start(cmd, column_width, row_height, logOutput));
var workingDir = WorkingDirectory;
//fallback to UserProfile
if (workingDir != null && (!System.IO.Directory.Exists(workingDir) || workingDir == "%USERPROFILE%")) {
workingDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
}
Task.Run(() => term.Start(cmd, column_width, row_height, logOutput, null, workingDir));
});
}
private async void Terminal_Loaded(object sender, RoutedEventArgs e) {
Expand Down Expand Up @@ -247,6 +257,8 @@ private async Task TermInit() {
public static readonly DependencyProperty ConPTYTermProperty = DependencyProperty.Register(nameof(ConPTYTerm), typeof(TermPTY), typeof(EasyTerminalControl), new(null, OnTermChanged));
public static readonly DependencyProperty StartupCommandLineProperty = DependencyProperty.Register(nameof(StartupCommandLine), typeof(string), typeof(EasyTerminalControl), new PropertyMetadata("powershell.exe"));

public static readonly DependencyProperty WorkingDirectoryProperty = DependencyProperty.Register(nameof(WorkingDirectory), typeof(string), typeof(EasyTerminalControl), new PropertyMetadata(null));

public static readonly DependencyProperty LogConPTYOutputProperty = DependencyProperty.Register(nameof(LogConPTYOutput), typeof(bool), typeof(EasyTerminalControl), new PropertyMetadata(false));
public static readonly DependencyProperty Win32InputModeProperty = DependencyProperty.Register(nameof(Win32InputMode), typeof(bool), typeof(EasyTerminalControl), new PropertyMetadata(true));
public static readonly DependencyProperty IsReadOnlyProperty = PropHelper.GenerateWriteOnlyProperty((c) => c.IsReadOnly);
Expand Down
10 changes: 5 additions & 5 deletions EasyWindowsTerminalControl/Internals/ProcessFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public interface IProcess : IDisposable {
void Kill(bool EntireProcessTree = false);
}
public interface IProcessFactory {
IProcess Start(string command, nuint attributes, PseudoConsole console);
IProcess Start(string command, nuint attributes, PseudoConsole console, string workingDirectory = null);
}
/// <summary>
/// Support for starting and configuring processes.
Expand Down Expand Up @@ -49,9 +49,9 @@ public void Dispose() {
/// <summary>
/// Start and configure a process. The return value represents the process and should be disposed.
/// </summary>
public static WrappedProcess Start(string command, nuint attributes, PseudoConsole console) {
public static WrappedProcess Start(string command, nuint attributes, PseudoConsole console, string workingDirectory = null) {
var startupInfo = ConfigureProcessThread(console.Handle, attributes);
var processInfo = RunProcess(ref startupInfo, command);
var processInfo = RunProcess(ref startupInfo, command, workingDirectory);
return new(new Process(startupInfo, processInfo));
}

Expand Down Expand Up @@ -100,14 +100,14 @@ unsafe private static STARTUPINFOEXW ConfigureProcessThread(PseudoConsole.ConPty
return startupInfo;
}

unsafe private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEXW sInfoEx, string commandLine) {
unsafe private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEXW sInfoEx, string commandLine, string workingDirectory = null) {
uint securityAttributeSize = (uint)Marshal.SizeOf<SECURITY_ATTRIBUTES>();
var pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
var tSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize };
var info = sInfoEx;
Span<char> spanChar = (commandLine + '\0').ToCharArray();

var success = PInvoke.CreateProcess(null, ref spanChar, pSec, tSec, false, PROCESS_CREATION_FLAGS.EXTENDED_STARTUPINFO_PRESENT, null, null, info.StartupInfo, out var pInfo);
var success = PInvoke.CreateProcess(null, ref spanChar, pSec, tSec, false, PROCESS_CREATION_FLAGS.EXTENDED_STARTUPINFO_PRESENT, null, workingDirectory, info.StartupInfo, out var pInfo);

if (!success)
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not create process.");
Expand Down
7 changes: 4 additions & 3 deletions EasyWindowsTerminalControl/TermPTY.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace EasyWindowsTerminalControl {
/// </summary>
public class TermPTY : ITerminalConnection {
protected class InternalProcessFactory : IProcessFactory {
public IProcess Start(string command, nuint attributes, PseudoConsole console) => ProcessFactory.Start(command, attributes, console);
public IProcess Start(string command, nuint attributes, PseudoConsole console, string workingDirectory = null) => ProcessFactory.Start(command, attributes, console, workingDirectory);
}
#if WPF
private static bool IsDesignMode = System.ComponentModel.DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject());
Expand Down Expand Up @@ -61,7 +61,8 @@ public static string StripColors(String str) {
/// <param name="consoleWidth">The width (in characters) to start the pseudoconsole with. Defaults to 30.</param>
/// <param name="logOutput">Whether to log the output of the console to a file. Defaults to false.</param>
/// <param name="factory">While not recommended, you can provide your own process factory for more granular control over process creation</param>
public void Start(string command, int consoleWidth = 80, int consoleHeight = 30, bool logOutput = false, IProcessFactory factory = null) {
/// <param name="workingDirectory">The working directory for the process. Defaults to null (inherits current process working directory).</param>
public void Start(string command, int consoleWidth = 80, int consoleHeight = 30, bool logOutput = false, IProcessFactory factory = null, string workingDirectory = null) {
if (Process != null)
throw new Exception("Called Start on ConPTY term after already started");
factory ??= new InternalProcessFactory();
Expand All @@ -75,7 +76,7 @@ public void Start(string command, int consoleWidth = 80, int consoleHeight = 30,
using (var inputPipe = new PseudoConsolePipe())
using (var outputPipe = new PseudoConsolePipe())
using (var pseudoConsole = PseudoConsole.Create(inputPipe.ReadSide, outputPipe.WriteSide, consoleWidth, consoleHeight))
using (var process = factory.Start(command, PInvoke.PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, pseudoConsole)) {
using (var process = factory.Start(command, PInvoke.PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, pseudoConsole, workingDirectory)) {
Process = process;
TheConsole = pseudoConsole;
// copy all pseudoconsole output to a FileStream and expose it to the rest of the app
Expand Down
2 changes: 1 addition & 1 deletion TermExample/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<term:EasyTerminalControl x:Name="basicTermControl" StartupCommandLine="{Binding StartupCommand}" Theme="{Binding Theme, Mode=OneWay}" FontFamilyWhenSettingTheme="Consolas" LogConPTYOutput="True" Win32InputMode="True" InputCapture="TabKey,DirectionKeys" >
<term:EasyTerminalControl x:Name="basicTermControl" StartupCommandLine="{Binding StartupCommand}" WorkingDirectory="{Binding WorkingDirectory}" Theme="{Binding Theme, Mode=OneWay}" FontFamilyWhenSettingTheme="Consolas" LogConPTYOutput="True" Win32InputMode="True" InputCapture="TabKey,DirectionKeys" >
<term:EasyTerminalControl.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource {x:Type ScrollBar}}">
<!--Lazy Theming-->
Expand Down
2 changes: 2 additions & 0 deletions TermExample/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public class DataBinds : INotifyPropertyChanged {
public void TriggerPropChanged(string prop) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));

public string StartupCommand => "pwsh.exe";
public string WorkingDirectory => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

private static Color AlphaOverrideColor(Color color, byte alphaOverride) => Color.FromArgb(alphaOverride, color.R, color.G, color.B);

private static readonly Color BackroundColor = Color.FromArgb(255, 0, 0, 30);
Expand Down