private void runWindows(Control control)
        {
            this.mainControl = control;
            //
            stdInputHandle  = Win32.GetStdHandle(StdHandleType.STD_INPUT_HANDLE);
            stdOutputHandle = Win32.GetStdHandle(StdHandleType.STD_OUTPUT_HANDLE);
            IntPtr[] handles = new[] {
                exitWaitHandle.SafeWaitHandle.DangerousGetHandle(),
                     stdInputHandle,
                     invokeWaitHandle.SafeWaitHandle.DangerousGetHandle(  )
            };

            // Set console mode to enable mouse and window resizing events
            const uint ENABLE_WINDOW_INPUT = 0x0008;
            const uint ENABLE_MOUSE_INPUT  = 0x0010;
            uint       consoleMode;

            Win32.GetConsoleMode(stdInputHandle, out consoleMode);
            Win32.SetConsoleMode(stdInputHandle, consoleMode | ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);

            // Get console screen buffer size
            CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;

            Win32.GetConsoleScreenBufferInfo(stdOutputHandle, out screenBufferInfo);

            // Set Canvas size to current console window size (not to whole buffer size)
            savedWindowRect = new Rect(new Point(Console.WindowLeft, Console.WindowTop),
                                       new Size(Console.WindowWidth, Console.WindowHeight));
            CanvasSize = new Size(savedWindowRect.Width, savedWindowRect.Height);

            canvas = userCanvasSize.IsEmpty
                ? new PhysicalCanvas(screenBufferInfo.dwSize.X, screenBufferInfo.dwSize.Y, stdOutputHandle)
                : new PhysicalCanvas(userCanvasSize.Width, userCanvasSize.Height, stdOutputHandle);
            renderer.Canvas = canvas;

            // Fill the canvas by default
            renderer.RootElementRect = userRootElementRect.IsEmpty
                ? new Rect(new Point(0, 0), canvas.Size) : userRootElementRect;
            renderer.RootElement = mainControl;
            //
            mainControl.Invalidate();
            renderer.UpdateLayout();
            renderer.FinallyApplyChangesToCanvas(  );

            // Initially hide the console cursor
            HideCursor();


            this.running      = true;
            this.mainThreadId = Thread.CurrentThread.ManagedThreadId;
            //
            while (true)
            {
                // 100 ms instead of Win32.INFINITE to check console window Zoomed and Iconic
                // state periodically (because if user presses Maximize/Restore button
                // there are no input event generated).
                uint waitResult = Win32.WaitForMultipleObjects(3, handles, false, 100);
                if (waitResult == 0)
                {
                    break;
                }
                if (waitResult == 1)
                {
                    processInput();
                }
                if (waitResult == 2)
                {
                    // Do nothing special - because invokeActions will be invoked in loop anyway
                }

                // If we received WAIT_TIMEOUT - check window Zoomed and Iconic state
                // and correct buffer size and console window size
                if (waitResult == 0x00000102)
                {
                    IntPtr consoleWindow = getConsoleWindowHwnd( );
                    bool   isZoomed      = Win32.IsZoomed(consoleWindow);
                    bool   isIconic      = Win32.IsIconic(consoleWindow);
                    if (maximized != isZoomed && !isIconic)
                    {
                        if (isZoomed)
                        {
                            Maximize();
                        }
                        else
                        {
                            Restore();
                        }
                    }
                    if (!maximized)
                    {
                        savedWindowRect = new Rect(new Point(Console.WindowLeft, Console.WindowTop),
                                                   new Size(Console.WindowWidth, Console.WindowHeight));
                    }
                }
                // WAIT_FAILED
                if (waitResult == 0xFFFFFFFF)
                {
                    throw new InvalidOperationException("Invalid wait result of WaitForMultipleObjects.");
                }

                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( );
            }

            // Restore cursor visibility before exit
            ShowCursor();

            // Restore console mode before exit
            Win32.SetConsoleMode(stdInputHandle, consoleMode);

            renderer.RootElement = null;

            // todo : restore attributes of console output
        }
        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;
        }