Example #1
0
        public void SetSize(int width, int height)
        {
            var ws = new winsize {
                ws_col = (ushort)width, ws_row = (ushort)height
            };

            ioctl(_masterPtyFd, 0x5414, ref ws);
        }
Example #2
0
        private void SetWindowSizeCore(int height, int width)
        {
            var size = new winsize
            {
                ws_col = (ushort)width,
                ws_row = (ushort)height,
            };

            // TODO: add some error handling, or find a better way to key a reference to the master fd
            var stream = (UnixFileDescriptorStream)Stdin.BaseStream;

            int retVal;

            do
            {
                retVal = Libc.ioctl((int)stream.Fd, Libc.TIOCSWINSZ, ref size);
            } while (Error.ShouldRetrySyscall(retVal));

            Error.ThrowExceptionForLastErrorIf(retVal);
        }
        private void runLinux(Control control)
        {
            this.mainControl = control;

            if (userCanvasSize.IsEmpty)
            {
                // Create physical canvas with actual terminal size
                winsize ws = Libc.GetTerminalSize(isDarwin);
                canvas = new PhysicalCanvas(ws.ws_col, ws.ws_row);
            }
            else
            {
                canvas = new PhysicalCanvas(userCanvasSize.Width, userCanvasSize.Height);
            }
            renderer.Canvas          = canvas;
            renderer.RootElementRect = userRootElementRect.IsEmpty
                ? new Rect(canvas.Size) : userRootElementRect;
            renderer.RootElement = mainControl;
            //
            mainControl.Invalidate();

            // Terminal initialization sequence

            // This is magic workaround to avoid messing up terminal after program finish
            // The bug is described at https://bugzilla.xamarin.com/show_bug.cgi?id=15118
            bool ignored = Console.KeyAvailable;

            IntPtr stdscr = NCurses.initscr();

            NCurses.cbreak();
            NCurses.noecho();
            NCurses.nonl();
            NCurses.intrflush(stdscr, false);
            NCurses.keypad(stdscr, true);
            NCurses.start_color();

            HideCursor();
            try {
                renderer.UpdateLayout( );
                renderer.FinallyApplyChangesToCanvas(  );

                termkeyHandle = LibTermKey.termkey_new(0, TermKeyFlag.TERMKEY_FLAG_SPACESYMBOL);

                // Setup the input mode
                Console.Write("\x1B[?1002h");
                pollfd fd = new pollfd( );
                fd.fd     = 0;
                fd.events = POLL_EVENTS.POLLIN;

                pollfd[] fds = new pollfd[2];
                fds[0] = fd;
                fds[1] = new pollfd( );
                int pipeResult = Libc.pipe(pipeFds);
                if (pipeResult == -1)
                {
                    throw new InvalidOperationException("Cannot create self-pipe.");
                }
                fds[1].fd     = pipeFds[0];
                fds[1].events = POLL_EVENTS.POLLIN;

                try {
#if !WIN32
                    // Catch SIGWINCH to handle terminal resizing
                    UnixSignal[] signals = new UnixSignal [] {
                        new UnixSignal(Signum.SIGWINCH)
                    };
                    Thread signal_thread = new Thread(delegate() {
                        while (true)
                        {
                            // Wait for a signal to be delivered
                            int index     = UnixSignal.WaitAny(signals, -1);
                            Signum signal = signals [index].Signum;
                            Libc.writeInt64(pipeFds[1], 2);
                        }
                    }
                                                      );
                    signal_thread.IsBackground = false;
                    signal_thread.Start();
#endif
                    TermKeyKey key = new TermKeyKey( );
                    //
                    this.running      = true;
                    this.mainThreadId = Thread.CurrentThread.ManagedThreadId;
                    //
                    int nextwait = -1;
                    while (true)
                    {
                        int pollRes = Libc.poll(fds, 2, nextwait);
                        if (pollRes == 0)
                        {
                            if (nextwait == -1)
                            {
                                throw new InvalidOperationException("Assertion failed.");
                            }
                            if (TermKeyResult.TERMKEY_RES_KEY == LibTermKey.termkey_getkey_force(termkeyHandle, ref key))
                            {
                                processLinuxInput(key);
                            }
                        }
                        if (pollRes == -1)
                        {
                            int errorCode = Marshal.GetLastWin32Error();
                            if (errorCode != Libc.EINTR)
                            {
                                throw new InvalidOperationException(string.Format("poll() returned with error code {0}", errorCode));
                            }
                        }

                        if (fds[1].revents != POLL_EVENTS.NONE)
                        {
                            UInt64 u;
                            Libc.readInt64(fds[1].fd, out u);
                            if (u == 1)
                            {
                                // Exit from application
#if !WIN32
                                signal_thread.Abort();
#endif
                                break;
                            }
                            if (u == 2)
                            {
                                // Get new term size and process appropriate INPUT_RECORD event
                                INPUT_RECORD inputRecord = new INPUT_RECORD( );
                                inputRecord.EventType = EventType.WINDOW_BUFFER_SIZE_EVENT;

                                winsize ws = Libc.GetTerminalSize(isDarwin);

                                inputRecord.WindowBufferSizeEvent.dwSize.X = ( short )ws.ws_col;
                                inputRecord.WindowBufferSizeEvent.dwSize.Y = ( short )ws.ws_row;
                                processInputEvent(inputRecord);
                            }
                            if (u == 3)
                            {
                                // It is signal from async actions invocation stuff
                            }
                        }

                        if ((fds[0].revents & POLL_EVENTS.POLLIN) == POLL_EVENTS.POLLIN ||
                            (fds[0].revents & POLL_EVENTS.POLLHUP) == POLL_EVENTS.POLLHUP ||
                            (fds[0].revents & POLL_EVENTS.POLLERR) == POLL_EVENTS.POLLERR)
                        {
                            LibTermKey.termkey_advisereadable(termkeyHandle);
                        }

                        TermKeyResult result = (LibTermKey.termkey_getkey(termkeyHandle, ref key));
                        while (result == TermKeyResult.TERMKEY_RES_KEY)
                        {
                            processLinuxInput(key);
                            result = (LibTermKey.termkey_getkey(termkeyHandle, ref key));
                        }

                        if (result == TermKeyResult.TERMKEY_RES_AGAIN)
                        {
                            nextwait = LibTermKey.termkey_get_waittime(termkeyHandle);
                        }
                        else
                        {
                            nextwait = -1;
                        }

                        while (true)
                        {
                            bool anyInvokeActions      = isAnyInvokeActions( );
                            bool anyRoutedEvent        = !EventManager.IsQueueEmpty( );
                            bool anyLayoutToRevalidate = renderer.AnyControlInvalidated;

                            if (!anyInvokeActions && !anyRoutedEvent && !anyLayoutToRevalidate)
                            {
                                break;
                            }

                            EventManager.ProcessEvents();
                            processInvokeActions(  );
                            renderer.UpdateLayout(  );
                        }

                        renderer.FinallyApplyChangesToCanvas( );
                    }
                } finally {
                    LibTermKey.termkey_destroy(termkeyHandle);
                    Libc.close(pipeFds[0]);
                    Libc.close(pipeFds[1]);
                    Console.Write("\x1B[?1002l");
                }
            } finally {
                // Restore cursor visibility before exit
                ShowCursor( );
                NCurses.endwin( );
            }

            renderer.RootElement = null;
        }
Example #4
0
 private static extern int ioctl(int fd, uint cmd, ref winsize ws);
Example #5
0
        // TODO: restructure this function
        // For example posix_spawn from chromium seems to have a
        // maintainable flow: https://chromium.googlesource.com/native_client/nacl-newlib/+/bf66148d14c7fca26b9198dd5dc81e743893bb66/newlib/libc/posix/posix_spawn.c
        private static unsafe int ForkAndExec(
            string filename, string[] argv, string[] envp,
            string cwd, bool useTty, bool redirectStdin,
            bool redirectStdout, bool redirectStderr,
            out int stdin, out int stdout, out int stderr
            )
        {
            byte **argvPtr = null;
            byte **envpPtr = null;

            bool success = true;

            ArrayHelpers.AllocNullTerminatedArray(argv, ref argvPtr);
            ArrayHelpers.AllocNullTerminatedArray(envp, ref envpPtr);

            int[] stdInFds  = new[] { -1, -1 };
            int[] stdOutFds = new[] { -1, -1 };
            int[] stdErrFds = new[] { -1, -1 };
            int   masterFd  = -1;
            int   slaveFd   = -1;

            try
            {
                int inFd, outFd, errFd;

                if (filename == null || argv == null || envp == null)
                {
                    success = false;
                    throw new ArgumentException("Provide the correct arguments");
                }

                if (Libc.access(filename, Libc.X_OK) != 0)
                {
                    success = false;
                    throw new Exception("The given file is not accessible");
                }

                if (useTty)
                {
                    var size = new winsize
                    {
                        ws_col = DEFAULT_WIDTH,
                        ws_row = DEFAULT_HEIGHT
                    };

                    if (Libc.openpty(out masterFd, out slaveFd, IntPtr.Zero, IntPtr.Zero, ref size) == -1)
                    {
                        success = false;
                        throw new Exception("Could not open a new pty");
                    }
                }
                else
                {
                    try
                    {
                        if (redirectStdin)
                        {
                            CreateCloseOnExecPipe(stdInFds);
                        }
                        if (redirectStdout)
                        {
                            CreateCloseOnExecPipe(stdOutFds);
                        }
                        if (redirectStderr)
                        {
                            CreateCloseOnExecPipe(stdErrFds);
                        }
                    }
                    catch (Exception ex)
                    {
                        success = false;
                        throw ex;
                    }
                }

                var pid = Libc.fork();

                if (pid < 0)
                {
                    success = false;
                    Error.ThrowExceptionForLastError();
                }

                if (pid == 0)
                {
                    if (useTty)
                    {
                        Libc.close(masterFd);
                        Libc.setsid();

                        if (Libc.ioctl(slaveFd, Libc.TIOCSCTTY) == -1)
                        {
                            success = false;
                            Error.ThrowExceptionForLastError();
                        }

                        inFd = outFd = errFd = slaveFd;
                    }
                    else
                    {
                        inFd  = stdInFds[READ_END_OF_PIPE];
                        outFd = stdOutFds[WRITE_END_OF_PIPE];
                        errFd = stdErrFds[WRITE_END_OF_PIPE];
                    }

                    // TODO: this code is just horrible and the likely hood introducing bugs here is quite high.
                    // I should refactor this asap. But first I would love to get some more tests in place that
                    // could catch any regressions..
                    if (redirectStdin)
                    {
                        while (Error.ShouldRetrySyscall(Libc.dup2(inFd, Libc.STDIN_FILENO)) && Libc.errno == Libc.EBUSY)
                        {
                            ;
                        }
                    }

                    if (redirectStdout)
                    {
                        while (Error.ShouldRetrySyscall(Libc.dup2(outFd, Libc.STDOUT_FILENO)) && Libc.errno == Libc.EBUSY)
                        {
                            ;
                        }
                    }

                    if (redirectStderr)
                    {
                        while (Error.ShouldRetrySyscall(Libc.dup2(errFd, Libc.STDERR_FILENO)) && Libc.errno == Libc.EBUSY)
                        {
                            ;
                        }
                    }

                    if (!string.IsNullOrEmpty(cwd))
                    {
                        var ret = Libc.chdir(cwd);

                        if (ret == -1)
                        {
                            success = false;
                            Error.ThrowExceptionForLastError();
                        }
                    }

                    Libc.execve(filename, argvPtr, envpPtr);

                    // Exec syscall should never return, and thus we should never get here, if we do then exit immediately
                    Libc._exit(Libc.errno != 0 ? Libc.errno : -1);
                }

                Libc.close(slaveFd);

                if (useTty)
                {
                    stdin  = masterFd;
                    stdout = masterFd;
                    stderr = masterFd;
                }
                else
                {
                    stdin  = stdInFds[WRITE_END_OF_PIPE];
                    stdout = stdOutFds[READ_END_OF_PIPE];
                    stderr = stdErrFds[READ_END_OF_PIPE];
                }

                return(pid);
            }
            finally
            {
                // Regardless of success or failure, close the parent's copy of the child's end of
                // any opened pipes. The parent doesn't need them anymore.
                CloseIfOpen(stdInFds[READ_END_OF_PIPE]);
                CloseIfOpen(stdOutFds[WRITE_END_OF_PIPE]);
                CloseIfOpen(stdErrFds[WRITE_END_OF_PIPE]);

                if (!success)
                {
                    // Cleanup all open fd
                    CloseIfOpen(masterFd);
                    CloseIfOpen(slaveFd);

                    CloseIfOpen(stdInFds[WRITE_END_OF_PIPE]);
                    CloseIfOpen(stdOutFds[READ_END_OF_PIPE]);
                    CloseIfOpen(stdErrFds[READ_END_OF_PIPE]);
                }

                ArrayHelpers.FreeArray(argvPtr, argv.Length);
                ArrayHelpers.FreeArray(envpPtr, envp.Length);
            }
        }
Example #6
0
 public extern static int ioctl(int fd, int cmd, out winsize argp);