/// <summary> /// Копирует affectedRect из буфера на экран консоли с учетом того, что буфер /// находится на экране консоли по смещению offset. /// </summary> /// <param name="canvas"></param> /// <param name="affectedRect">Измененная область относительно this.</param> /// <param name="offset">В какой точке экрана размещен контрол (см <see cref="Renderer.RootElementRect"/>).</param> public void CopyToPhysicalCanvas(PhysicalCanvas canvas, Rect affectedRect, Point offset) { Rect rectToCopy = affectedRect; Rect bufferRect = new Rect(new Point(0, 0), new Size(this.width, this.height)); Rect canvasRect = new Rect(new Point(-offset.X, -offset.Y), canvas.Size); rectToCopy.Intersect(canvasRect); rectToCopy.Intersect(bufferRect); // for (int x = 0; x < rectToCopy.width; x++) { int bufferX = x + rectToCopy.x; int canvasX = x + rectToCopy.x + offset.x; for (int y = 0; y < rectToCopy.height; y++) { int bufferY = y + rectToCopy.y; int canvasY = y + rectToCopy.y + offset.y; CHAR_INFO charInfo = buffer[bufferX, bufferY]; canvas[canvasX][canvasY].Assign(charInfo); } } }
public CHAR_INFO_ref(int x, int y, PhysicalCanvas canvas) { this.x = x; this.y = y; this.canvas = canvas; }
public NestedIndexer(int x, PhysicalCanvas canvas) { this.x = x; this.canvas = canvas; }
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; }