Пример #1
0
        public void ParseInputEvent(INPUT_RECORD inputRecord, Control rootElement) {
            if (inputRecord.EventType == EventType.MOUSE_EVENT) {
                MOUSE_EVENT_RECORD mouseEvent = inputRecord.MouseEvent;

                if (mouseEvent.dwEventFlags != MouseEventFlags.PRESSED_OR_RELEASED &&
                    mouseEvent.dwEventFlags != MouseEventFlags.MOUSE_MOVED &&
                    mouseEvent.dwEventFlags != MouseEventFlags.DOUBLE_CLICK &&
                    mouseEvent.dwEventFlags != MouseEventFlags.MOUSE_WHEELED &&
                    mouseEvent.dwEventFlags != MouseEventFlags.MOUSE_HWHEELED) {
                    //
                    throw new InvalidOperationException("Flags combination in mouse event was not expected.");
                }
                Point rawPosition;
                if (mouseEvent.dwEventFlags == MouseEventFlags.MOUSE_MOVED ||
                    mouseEvent.dwEventFlags == MouseEventFlags.PRESSED_OR_RELEASED) {
                    rawPosition = new Point(mouseEvent.dwMousePosition.X, mouseEvent.dwMousePosition.Y);
                    lastMousePosition = rawPosition;
                } else {
                    // При событии MOUSE_WHEELED в Windows некорректно устанавливается mouseEvent.dwMousePosition
                    // Поэтому для определения элемента, над которым производится прокручивание колёсика, мы
                    // вынуждены сохранять координаты, полученные при предыдущем событии мыши
                    rawPosition = lastMousePosition;
                }
                Control topMost = findSource(rawPosition, rootElement);

                // если мышь захвачена контролом, то события перемещения мыши доставляются только ему,
                // события, связанные с нажатием мыши - тоже доставляются только ему, вместо того
                // контрола, над которым событие было зарегистрировано. Такой механизм необходим,
                // например, для корректной обработки перемещений окон (вверх или в стороны)
                Control source = (inputCaptureStack.Count != 0) ? inputCaptureStack.Peek() : topMost;
                
                if (mouseEvent.dwEventFlags == MouseEventFlags.MOUSE_MOVED) {
                    MouseButtonState leftMouseButtonState = getLeftButtonState(mouseEvent.dwButtonState);
                    MouseButtonState middleMouseButtonState = getMiddleButtonState(mouseEvent.dwButtonState);
                    MouseButtonState rightMouseButtonState = getRightButtonState(mouseEvent.dwButtonState);
                    //
                    MouseEventArgs mouseEventArgs = new MouseEventArgs(source, Control.PreviewMouseMoveEvent,
                                                                       rawPosition,
                                                                       leftMouseButtonState,
                                                                       middleMouseButtonState,
                                                                       rightMouseButtonState
                        );
                    eventsQueue.Enqueue(mouseEventArgs);
                    //
                    lastLeftMouseButtonState = leftMouseButtonState;
                    lastMiddleMouseButtonState = middleMouseButtonState;
                    lastRightMouseButtonState = rightMouseButtonState;

                    // detect mouse enter / mouse leave events

                    // path to source from root element down
                    List<Control> mouseOverStack = new List<Control>();
                    Control current = topMost;
                    while (null != current) {
                        mouseOverStack.Insert(0, current);
                        current = current.Parent;
                    }

                    int index;
                    for (index = 0; index < Math.Min(mouseOverStack.Count, prevMouseOverStack.Count); index++) {
                        if (mouseOverStack[index] != prevMouseOverStack[index])
                            break;
                    }

                    for (int i = prevMouseOverStack.Count - 1; i >= index; i-- ) {
                        Control control = prevMouseOverStack[i];
                        MouseEventArgs args = new MouseEventArgs(control, Control.MouseLeaveEvent,
                                                                    rawPosition,
                                                                    leftMouseButtonState,
                                                                    middleMouseButtonState,
                                                                    rightMouseButtonState
                            );
                        eventsQueue.Enqueue(args);
                    }

                    for (int i = index; i < mouseOverStack.Count; i++ ) {
                        // enqueue MouseEnter event
                        Control control = mouseOverStack[i];
                        MouseEventArgs args = new MouseEventArgs(control, Control.MouseEnterEvent,
                                                                    rawPosition,
                                                                    leftMouseButtonState,
                                                                    middleMouseButtonState,
                                                                    rightMouseButtonState
                            );
                        eventsQueue.Enqueue(args);
                    }

                    prevMouseOverStack.Clear();
                    prevMouseOverStack.AddRange(mouseOverStack);
                }
                if (mouseEvent.dwEventFlags == MouseEventFlags.PRESSED_OR_RELEASED) {
                    //
                    MouseButtonState leftMouseButtonState = getLeftButtonState(mouseEvent.dwButtonState);
                    MouseButtonState middleMouseButtonState = getMiddleButtonState(mouseEvent.dwButtonState);
                    MouseButtonState rightMouseButtonState = getRightButtonState(mouseEvent.dwButtonState);
                    //
                    if (leftMouseButtonState != lastLeftMouseButtonState) {
                        MouseButtonEventArgs eventArgs = new MouseButtonEventArgs(source,
                            leftMouseButtonState == MouseButtonState.Pressed ? Control.PreviewMouseDownEvent : Control.PreviewMouseUpEvent,
                            rawPosition,
                            leftMouseButtonState,
                            lastMiddleMouseButtonState,
                            lastRightMouseButtonState,
                            MouseButton.Left
                            );
                        eventsQueue.Enqueue(eventArgs);
                    }
                    if (middleMouseButtonState != lastMiddleMouseButtonState) {
                        MouseButtonEventArgs eventArgs = new MouseButtonEventArgs(source,
                            middleMouseButtonState == MouseButtonState.Pressed ? Control.PreviewMouseDownEvent : Control.PreviewMouseUpEvent,
                            rawPosition,
                            lastLeftMouseButtonState,
                            middleMouseButtonState,
                            lastRightMouseButtonState,
                            MouseButton.Middle
                            );
                        eventsQueue.Enqueue(eventArgs);
                    }
                    if (rightMouseButtonState != lastRightMouseButtonState) {
                        MouseButtonEventArgs eventArgs = new MouseButtonEventArgs(source,
                            rightMouseButtonState == MouseButtonState.Pressed ? Control.PreviewMouseDownEvent : Control.PreviewMouseUpEvent,
                            rawPosition,
                            lastLeftMouseButtonState,
                            lastMiddleMouseButtonState,
                            rightMouseButtonState,
                            MouseButton.Right
                            );
                        eventsQueue.Enqueue(eventArgs);
                    }
                    //
                    lastLeftMouseButtonState = leftMouseButtonState;
                    lastMiddleMouseButtonState = middleMouseButtonState;
                    lastRightMouseButtonState = rightMouseButtonState;
                }

                if (mouseEvent.dwEventFlags == MouseEventFlags.MOUSE_WHEELED) {
                    MouseWheelEventArgs args = new MouseWheelEventArgs(
                        topMost,
                        Control.PreviewMouseWheelEvent,
                        rawPosition,
                        lastLeftMouseButtonState, lastMiddleMouseButtonState, 
                        lastRightMouseButtonState,
                        mouseEvent.dwButtonState > 0 ? 1 : -1
                    );
                    eventsQueue.Enqueue( args );
                }
            }
            if (inputRecord.EventType == EventType.KEY_EVENT) {
                KEY_EVENT_RECORD keyEvent = inputRecord.KeyEvent;
                KeyEventArgs eventArgs = new KeyEventArgs(
                    ConsoleApplication.Instance.FocusManager.FocusedElement,
                    keyEvent.bKeyDown ? Control.PreviewKeyDownEvent : Control.PreviewKeyUpEvent);
                eventArgs.UnicodeChar = keyEvent.UnicodeChar;
                eventArgs.bKeyDown = keyEvent.bKeyDown;
                eventArgs.dwControlKeyState = keyEvent.dwControlKeyState;
                eventArgs.wRepeatCount = keyEvent.wRepeatCount;
                eventArgs.wVirtualKeyCode = keyEvent.wVirtualKeyCode;
                eventArgs.wVirtualScanCode = keyEvent.wVirtualScanCode;
                eventsQueue.Enqueue(eventArgs);
            }
        }
Пример #2
0
        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;
        }
Пример #3
0
        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);
            }
        }
Пример #4
0
        private void processInputEvent(INPUT_RECORD inputRecord)
        {
            if ( inputRecord.EventType == EventType.WINDOW_BUFFER_SIZE_EVENT ) {

                if ( usingLinux ) {
                    // Reinitializing ncurses to deal with new dimensions
                    // http://stackoverflow.com/questions/13707137/ncurses-resizing-glitch
                    NCurses.endwin();
                    // Needs to be called after an endwin() so ncurses will initialize
                    // itself with the new terminal dimensions.
                    NCurses.refresh();
                    NCurses.clear();
                }

                COORD dwSize = inputRecord.WindowBufferSizeEvent.dwSize;

                // Invoke default handler if no custom handlers attached and
                // userCanvasSize and userRootElementRect are not defined
                if ( TerminalSizeChanged == null
                     && userCanvasSize.IsEmpty
                     && userRootElementRect.IsEmpty ) {
                    OnTerminalSizeChangedDefault(this, new TerminalSizeChangedEventArgs( dwSize.X, dwSize.Y ));
                } else if ( TerminalSizeChanged != null ) {
                    TerminalSizeChanged.Invoke(this, new TerminalSizeChangedEventArgs(dwSize.X, dwSize.Y));
                }

                // Refresh whole display
                renderer.FinallyApplyChangesToCanvas( true );

                return;
            }
            eventManager.ParseInputEvent(inputRecord, mainControl);
        }
Пример #5
0
        private void processInput()
        {
            INPUT_RECORD[] buffer = new INPUT_RECORD[10];
            uint read;
            bool bReaded = Win32.ReadConsoleInput(stdInputHandle, buffer, (uint) buffer.Length, out read);
            if (!bReaded) {
                throw new InvalidOperationException("ReadConsoleInput method failed.");
            }

            for (int i = 0; i < read; ++i) {
                processInputEvent(buffer[i]);
            }
        }
Пример #6
0
 public UserInputEventArgs( INPUT_RECORD inputRecord ) {
     this.inputRecord = inputRecord;
 }