public override void ViewDidLoad() { base.ViewDidLoad(); terminalView = new TerminalView(View.Frame); var t = terminalView.Terminal; var size = new UnixWindowSize(); GetSize(t, ref size); pid = Pty.ForkAndExec("/bin/bash", new string [] { "/bin/bash" }, Terminal.GetEnvironmentVariables(), out fd, size); DispatchIO.Read(fd, (nuint)readBuffer.Length, DispatchQueue.CurrentQueue, ChildProcessRead); terminalView.UserInput += (byte [] data) => { DispatchIO.Write(fd, DispatchData.FromByteBuffer(data), DispatchQueue.CurrentQueue, ChildProcessWrite); }; terminalView.Feed("Welcome to XtermSharp - NSView frontend!\n"); terminalView.TitleChanged += (TerminalView sender, string title) => { View.Window.Title = title; }; terminalView.SizeChanged += (newCols, newRows) => { UnixWindowSize nz = new UnixWindowSize(); GetSize(t, ref nz); var res = Pty.SetWinSize(fd, ref nz); Console.WriteLine(res); }; View.AddSubview(terminalView); }
/// <summary> /// Reads data from the child process /// </summary> void ChildProcessRead(DispatchData data, int error) { using (var map = data.CreateMap(out var buffer, out var size)) { // Faster, but harder to debug: // terminalView.Feed (buffer, (int) size); if (size == 0) { if (!string.IsNullOrEmpty(ExitText)) { terminalView.Terminal.Feed(ExitText); } ShellExited?.Invoke(); return; } byte [] copy = new byte [(int)size]; Marshal.Copy(buffer, copy, 0, (int)size); #if DEBUG System.IO.File.WriteAllBytes("/tmp/log-" + (x++), copy); #endif terminalView.Feed(copy); } DispatchIO.Read(shellFileDescriptor, (nuint)readBuffer.Length, DispatchQueue.CurrentQueue, ChildProcessRead); }
/// <summary> /// Launches the shell /// </summary> public virtual void Start(string shellPath = "/bin/bash", string [] args = null, string [] env = null) { OnStart(); var shellArgs = args == null ? new string [1] : new string [args.Length + 1]; shellArgs [0] = shellPath; args?.CopyTo(shellArgs, 1); ProcessId = Pty.ForkAndExec(shellPath, shellArgs, env ?? Terminal.GetEnvironmentVariables(), out shellFileDescriptor, initialSize); DispatchIO.Read(shellFileDescriptor, (nuint)readBuffer.Length, DispatchQueue.CurrentQueue, ChildProcessRead); }
/// <summary> /// Launches the shell /// </summary> public void StartShell(string shellPath = "/bin/bash", string [] args = null) { // TODO: throw error if already started terminalView.Feed(WelcomeText + "\n"); var size = new UnixWindowSize(); GetUnixWindowSize(terminalView.Frame, terminalView.Terminal.Rows, terminalView.Terminal.Cols, ref size); var shellArgs = args == null ? new string [1] : new string [args.Length + 1]; shellArgs [0] = shellPath; args?.CopyTo(shellArgs, 1); shellPid = Pty.ForkAndExec(shellPath, shellArgs, Terminal.GetEnvironmentVariables(), out shellFileDescriptor, size); DispatchIO.Read(shellFileDescriptor, (nuint)readBuffer.Length, DispatchQueue.CurrentQueue, ChildProcessRead); }
void ChildProcessRead(DispatchData data, int error) { using (var map = data.CreateMap(out var buffer, out var size)) { // Faster, but harder to debug: // terminalView.Feed (buffer, (int) size); //Console.WriteLine ("Read {0} bytes", size); if (size == 0) { View.Window.Close(); return; } byte [] copy = new byte [(int)size]; Marshal.Copy(buffer, copy, 0, (int)size); System.IO.File.WriteAllBytes("/tmp/log-" + (x++), copy); terminalView.Feed(copy); } DispatchIO.Read(fd, (nuint)readBuffer.Length, DispatchQueue.CurrentQueue, ChildProcessRead); }
static void SendLoop(NWConnection connection) { const int STDIN_FILENO = 0; DispatchIO.Read(STDIN_FILENO, 8192, DispatchQueue.MainQueue, (DispatchData readData, int stdinError) => { if (stdinError != 0) { warn($"Standard input error: {stdinError}"); } else if (readData == null || readData.Size == 0) { // Null data represent EOF // Send a "write close" on the connection, by sending // null data with the final message context marked as // complete. Note that it is valid to send with null // data but a non-null context. connection.Send((byte [] )null, context: NWContentContext.FinalMessage, isComplete: true, callback: (NWError error) => { if (error != null) { warn($"send error: {error.ErrorCode}"); } }); // Stop reading from stdin, do not schedule another SendLoop } else { connection.Send(readData, context: NWContentContext.DefaultMessage, isComplete: true, callback: (NWError error) => { if (error != null) { warn($"send error: {error.ErrorCode}"); } else { // continue reading from stdin SendLoop(connection); } }); } }); }
/// <summary> /// Reads data from the child process /// </summary> void ChildProcessRead(DispatchData data, int error) { using (var map = data.CreateMap(out var buffer, out var size)) { // Faster, but harder to debug: // terminalView.Feed (buffer, (int) size); if (size == 0) { OnStop(); return; } byte [] copy = new byte [(int)size]; Marshal.Copy(buffer, copy, 0, (int)size); #if DEBUG_LOG_FILE System.IO.File.WriteAllBytes("/tmp/log-" + (debugFileIndex++), copy); #endif SendOnData(copy); } DispatchIO.Read(shellFileDescriptor, (nuint)readBuffer.Length, DispatchQueue.CurrentQueue, ChildProcessRead); }