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;
        }
        private void processLinuxInput(TermKeyKey key)
        {
            // If any special button has been pressed (Tab, Enter, etc)
            // we should convert its code to INPUT_RECORD.KeyEvent
            // Because INPUT_RECORD.KeyEvent depends on Windows' scan codes,
            // we convert codes retrieved from LibTermKey to Windows virtual scan codes
            // In the future, this logic may be changed (for example, both Windows and Linux
            // raw codes can be converted into ConsoleFramework's own abstract enum)
            if (key.type == TermKeyType.TERMKEY_TYPE_KEYSYM)
            {
                INPUT_RECORD inputRecord = new INPUT_RECORD();
                inputRecord.EventType             = EventType.KEY_EVENT;
                inputRecord.KeyEvent.bKeyDown     = true;
                inputRecord.KeyEvent.wRepeatCount = 1;
                switch (key.code.sym)
                {
                case TermKeySym.TERMKEY_SYM_TAB:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Tab;
                    break;

                case TermKeySym.TERMKEY_SYM_ENTER:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Return;
                    break;

                // in gnome-terminal it is backspace by default
                // (see default compatibility settings in Profile's settings)
                case TermKeySym.TERMKEY_SYM_DEL:
                case TermKeySym.TERMKEY_SYM_BACKSPACE:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Back;
                    break;

                case TermKeySym.TERMKEY_SYM_DELETE:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Delete;
                    break;

                case TermKeySym.TERMKEY_SYM_HOME:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Home;
                    break;

                case TermKeySym.TERMKEY_SYM_END:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.End;
                    break;

                case TermKeySym.TERMKEY_SYM_PAGEUP:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Prior;
                    break;

                case TermKeySym.TERMKEY_SYM_PAGEDOWN:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Next;
                    break;

                case TermKeySym.TERMKEY_SYM_SPACE:
                    inputRecord.KeyEvent.UnicodeChar     = ' ';
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Space;
                    break;

                case TermKeySym.TERMKEY_SYM_ESCAPE:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Escape;
                    break;

                case TermKeySym.TERMKEY_SYM_INSERT:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Insert;
                    break;

                case TermKeySym.TERMKEY_SYM_UP:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Up;
                    break;

                case TermKeySym.TERMKEY_SYM_DOWN:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Down;
                    break;

                case TermKeySym.TERMKEY_SYM_LEFT:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Left;
                    break;

                case TermKeySym.TERMKEY_SYM_RIGHT:
                    inputRecord.KeyEvent.wVirtualKeyCode = VirtualKeys.Right;
                    break;

                default:
                    throw new NotSupportedException("Not supported keyboard code detected: " + key.code.sym);
                }
                inputRecord.KeyEvent.dwControlKeyState = 0;
                if ((key.modifiers & 4) == 4)
                {
                    inputRecord.KeyEvent.dwControlKeyState |= ControlKeyState.LEFT_CTRL_PRESSED;
                }
                if ((key.modifiers & 2) == 2)
                {
                    inputRecord.KeyEvent.dwControlKeyState |= ControlKeyState.LEFT_ALT_PRESSED;
                }
                processInputEvent(inputRecord);
            }
            else if (key.type == TermKeyType.TERMKEY_TYPE_UNICODE)
            {
                byte[] data = new byte[7];
                data[0] = key.utf8_0;
                data[1] = key.utf8_1;
                data[2] = key.utf8_2;
                data[3] = key.utf8_3;
                data[4] = key.utf8_4;
                data[5] = key.utf8_5;
                data[6] = key.utf8_6;
                string       d = System.Text.Encoding.UTF8.GetString(data);
                char         unicodeCharacter = d[0];
                INPUT_RECORD inputRecord      = new INPUT_RECORD();
                inputRecord.EventType                  = EventType.KEY_EVENT;
                inputRecord.KeyEvent.bKeyDown          = true;
                inputRecord.KeyEvent.wRepeatCount      = 1;
                inputRecord.KeyEvent.UnicodeChar       = unicodeCharacter;
                inputRecord.KeyEvent.dwControlKeyState = 0;
                if (char.IsLetterOrDigit(unicodeCharacter))
                {
                    if (char.IsDigit(unicodeCharacter))
                    {
                        inputRecord.KeyEvent.wVirtualKeyCode =
                            (VirtualKeys)(unicodeCharacter - '0' + (int)VirtualKeys.N0);
                    }
                    else
                    {
                        char lowercased = char.ToLowerInvariant(unicodeCharacter);

                        // Only english characters can be converted to VirtualKeys
                        if (lowercased >= 'a' && lowercased <= 'z')
                        {
                            inputRecord.KeyEvent.wVirtualKeyCode =
                                (VirtualKeys)(lowercased - 'a' + (int)VirtualKeys.A);
                        }
                    }
                }
                if ((key.modifiers & 4) == 4)
                {
                    inputRecord.KeyEvent.dwControlKeyState |= ControlKeyState.LEFT_CTRL_PRESSED;
                }
                if ((key.modifiers & 2) == 2)
                {
                    inputRecord.KeyEvent.dwControlKeyState |= ControlKeyState.LEFT_ALT_PRESSED;
                }
                // todo : remove hardcoded exit combo after testing
                if (unicodeCharacter == 'd' && key.modifiers == 4)
                {
                    Exit();
                }
                processInputEvent(inputRecord);
                //
            }
            else if (key.type == TermKeyType.TERMKEY_TYPE_MOUSE)
            {
                TermKeyMouseEvent ev;
                int button;
                int line, col;
                LibTermKey.termkey_interpret_mouse(termkeyHandle, ref key, out ev, out button, out line, out col);
                //
                INPUT_RECORD inputRecord = new INPUT_RECORD();
                inputRecord.EventType = EventType.MOUSE_EVENT;
                if (ev == TermKeyMouseEvent.TERMKEY_MOUSE_PRESS || ev == TermKeyMouseEvent.TERMKEY_MOUSE_RELEASE)
                {
                    inputRecord.MouseEvent.dwEventFlags = MouseEventFlags.PRESSED_OR_RELEASED;
                }
                if (ev == TermKeyMouseEvent.TERMKEY_MOUSE_DRAG)
                {
                    inputRecord.MouseEvent.dwEventFlags = MouseEventFlags.MOUSE_MOVED;
                }
                inputRecord.MouseEvent.dwMousePosition = new COORD((short)(col - 1), (short)(line - 1));
                if (ev == TermKeyMouseEvent.TERMKEY_MOUSE_RELEASE)
                {
                    inputRecord.MouseEvent.dwButtonState = 0;
                }
                else if (ev == TermKeyMouseEvent.TERMKEY_MOUSE_DRAG || ev == TermKeyMouseEvent.TERMKEY_MOUSE_PRESS)
                {
                    if (1 == button)
                    {
                        inputRecord.MouseEvent.dwButtonState = MOUSE_BUTTON_STATE.FROM_LEFT_1ST_BUTTON_PRESSED;
                    }
                    else if (2 == button)
                    {
                        inputRecord.MouseEvent.dwButtonState = MOUSE_BUTTON_STATE.FROM_LEFT_2ND_BUTTON_PRESSED;
                    }
                    else if (3 == button)
                    {
                        inputRecord.MouseEvent.dwButtonState = MOUSE_BUTTON_STATE.RIGHTMOST_BUTTON_PRESSED;
                    }
                }
                //
                processInputEvent(inputRecord);
            }
        }
Ejemplo n.º 3
0
        public static void Main(string[] args)
        {
            Thread thread = new Thread(new ThreadStart(() => {
                Thread.Sleep(TimeSpan.FromSeconds(5));
                Console.WriteLine("Message from thread");
                int res = LibTermKey.writeInt64(eventfd, 1);
                Console.WriteLine("write(1) returned {0}\n", res);
                if (res == -1)
                {
                    int lastError = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
                    Console.WriteLine("Last error is {0}\n", lastError);
                }
            }));

            thread.IsBackground = true;
            thread.Start();

            IntPtr handle = LibTermKey.termkey_new(0, TermKeyFlag.TERMKEY_FLAG_SPACESYMBOL);

            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();
            eventfd = LibTermKey.eventfd(0, EVENTFD_FLAGS.EFD_CLOEXEC);
            if (eventfd == -1)
            {
                Console.WriteLine("Cannot create eventfd\n");
                int lastError = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
                Console.WriteLine("Last error is {0}\n", lastError);
            }
            fds[1].fd     = eventfd;
            fds[1].events = POLL_EVENTS.POLLIN;

            TermKeyKey key = new TermKeyKey();

            while (true)
            {
                int pollRes = LibTermKey.poll(fds, 2, -1);
                if (0 == pollRes)
                {
                    // timed out
                    Console.WriteLine("Timed out");
                    if (LibTermKey.termkey_getkey_force(handle, ref key) == TermKeyResult.TERMKEY_RES_KEY)
                    {
                        Console.WriteLine("got TERMKEY_RES_KEY");
                    }
                }
                else if (-1 == pollRes)
                {
                    int errorCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
                    Console.WriteLine(string.Format("ErrorCode = {0}", errorCode));
                }
                Console.WriteLine(string.Format("PollRes is {0}", pollRes));

                for (int i = 0; i < 2; i++)
                {
                    if (fds[i].revents != POLL_EVENTS.NONE)
                    {
                        if (i == 1)
                        {
                            UInt64 u;
                            LibTermKey.readInt64(fds[i].fd, out u);
                            Console.WriteLine("Readed eventfd counter : {0}\n", u);
                        }
                    }
                }

                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)
                {
                    // todo : log return value
                    LibTermKey.termkey_advisereadable(handle);
                }

                TermKeyResult result;
                while ((result = LibTermKey.termkey_getkey(handle, ref key)) == TermKeyResult.TERMKEY_RES_KEY)
                {
                    Console.WriteLine("Received some key.");
                    string descr = String.Format("Type : {0} Modifiers: {1} Utf8 bytes: {2}{3}{4}{5}{6}{7}{8}",
                                                 key.type, key.modifiers, key.utf8_0,
                                                 key.utf8_1, key.utf8_2, key.utf8_3,
                                                 key.utf8_4, key.utf8_5, key.utf8_6);
                    //dump the retrieved structure
                    //byte[] buffer = new byte[30];
                    //IntPtr nativeBuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(30);
                    //System.Runtime.InteropServices.Marshal.StructureToPtr(key, nativeBuffer, false);
                    //System.Runtime.InteropServices.Marshal.Copy(nativeBuffer, buffer, 0, 30);
                    //for (int i = 0; i < 30; i++ ) {
                    //	Console.Write("{0} ", buffer[i]);
                    //	if ((i + 1) % 10 == 0)
                    //		Console.WriteLine();
                    //}
                    //System.Runtime.InteropServices.Marshal.FreeHGlobal(nativeBuffer);
                    Console.WriteLine(descr);
                    if (key.type == TermKeyType.TERMKEY_TYPE_UNICODE)
                    {
                        byte[] data = new byte[7];
                        data[0] = key.utf8_0;
                        data[1] = key.utf8_1;
                        data[2] = key.utf8_2;
                        data[3] = key.utf8_3;
                        data[4] = key.utf8_4;
                        data[5] = key.utf8_5;
                        data[6] = key.utf8_6;
                        string d = System.Text.Encoding.UTF8.GetString(data);
                        Console.WriteLine(String.Format("Unicode symbol : {0}", d));
                    }
                    else if (key.type == TermKeyType.TERMKEY_TYPE_MOUSE)
                    {
                        TermKeyMouseEvent ev;
                        int button;
                        int line, col;
                        LibTermKey.termkey_interpret_mouse(handle, ref key, out ev, out button, out line, out col);
                        Console.WriteLine("MouseEvent : {0} (button {1}) at {2}:{3}", ev, button, line, col);
                    }
                }
            }

            LibTermKey.termkey_destroy(handle);
            LibTermKey.close(eventfd);
        }