public static extern int poll( pollfd[] fds, int fdsCount, int timeout);
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; }