/* ConFrames - Gui Stuff for Console Windows Copyright (C) 2017-2018 Topten Software. ConFrames is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ConFrames is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ConFrames. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; namespace ConFrames { public class Desktop { public Desktop(int width, int height) { // Save stdout _stdout = Interop.GetStdHandle(Interop.STD_OUTPUT_HANDLE); // Create buffer _buffer = Interop.CreateConsoleScreenBuffer(Interop.GENERIC_READWRITE, (uint)0, IntPtr.Zero, Interop.CONSOLE_TEXTMODE_BUFFER, IntPtr.Zero); // Default colors ActiveBorderBackgroundColor = ConsoleColor.Blue; ActiveBorderLineColor = ConsoleColor.White; InactiveBorderLineColor = ConsoleColor.Gray; InactiveBorderBackgroundColor = ConsoleColor.DarkBlue; // Default desktop size DesktopSize = new Size(width, height); DesktopColor = ConsoleColor.DarkBlue; } // Handle to the screen and stdout buffers IntPtr _stdout; IntPtr _buffer; // Desktop size Size _desktopSize; public Size DesktopSize { get { return _desktopSize; } set { try { // Try to set it Interop.SetBufferAndScreenSize(_buffer, (short)value.Width, (short)value.Height); // Save it _desktopSize = value; } catch { try { // try to set it back Interop.SetBufferAndScreenSize(_buffer, (short)_desktopSize.Width, (short)_desktopSize.Height); } catch { } throw; } } } // List of all windows List _windows = new List(); // Register a new window internal void AddWindow(Window window) { if (_windows.IndexOf(window)<0) { _windows.Add(window); InvalidateDesktop(); } } // Remove a window internal void RemoveWindow(Window window) { _windows.Remove(window); InvalidateDesktop(); } // Colors public ConsoleColor ActiveBorderBackgroundColor { get; set; } public ConsoleColor ActiveBorderLineColor { get; set; } public ConsoleColor InactiveBorderBackgroundColor { get; set; } public ConsoleColor InactiveBorderLineColor { get; set; } // Color of area behind all windows ConsoleColor _desktopColor; public ConsoleColor DesktopColor { get { return _desktopColor; } set { _desktopColor = value; InvalidateDesktop(); } } // Called just before repainting the screen protected virtual void OnWillUpdate() { } // Called just after repainting the screen protected virtual void OnDidUpdate() { } // Called just before waiting for input protected virtual void OnEnterProcessing() { } // Called just after waiting for input protected virtual void OnLeaveProcessing() { } // Preview received keys - return true if handled protected virtual bool OnPreviewKey(ConsoleKeyInfo key) { if (PreviewKey != null) return PreviewKey(key); return false; } // Handler public Func PreviewKey; // Get/Set the currently active window public Window ActiveWindow { get { return _windows.Count == 0 ? null : _windows[_windows.Count - 1]; } set { var oldActive = ActiveWindow; int pos = _windows.IndexOf(value); if (pos < _windows.Count-1) { _windows.RemoveAt(pos); _windows.Add(value); } if (oldActive!=ActiveWindow) { Invalidate(oldActive); Invalidate(ActiveWindow); } } } // Invalidate flags bool _needRedraw = false; bool _needClear = false; public void Invalidate(Window w) { _needRedraw = true; } public void InvalidateDesktop() { _needClear = true; _needRedraw = true; } // Update public void Update() { // Quit if we don't need a redraw if (_needRedraw) { // Notify OnWillUpdate(); // Clear flag _needRedraw = false; // Do we need to clear? if (_needClear) { _needClear = false; // Get screens size Interop.CONSOLE_SCREEN_BUFFER_INFO info; Interop.GetConsoleScreenBufferInfo(_buffer, out info); // Create buffer CharInfo[] buf = new CharInfo[info.dwSize.X * info.dwSize.Y]; // Clear buffer var defAttributes = (ushort)((ushort)0 | ((ushort)_desktopColor << 4)); for (int i = 0; i < buf.Length; ++i) { buf[i].Attributes = defAttributes; buf[i].Char = (char)' '; } // Copy it var r = new Interop.SmallRect() { Top = (short)0, Left = (short)0, Right = (short)info.dwSize.X, Bottom = (short)info.dwSize.Y, }; Interop.WriteConsoleOutput(_buffer, buf, new Interop.Coord() { X = (short)info.dwSize.X, Y = info.dwSize.Y }, new Interop.Coord() { X = 0, Y = 0 }, ref r); } // Draw all windows foreach (var w in _windows) { var buf = w.Draw(); var r = new Interop.SmallRect() { Top = (short)w.FrameRectangle.Top, Left = (short)w.FrameRectangle.Left, Right = (short)w.FrameRectangle.Right, Bottom = (short)w.FrameRectangle.Bottom, }; Interop.WriteConsoleOutput(_buffer, buf, new Interop.Coord() { X = (short)w.FrameRectangle.Width, Y = (short)w.FrameRectangle.Height }, new Interop.Coord() { X = 0, Y = 0 }, ref r); } // Finished OnDidUpdate(); } // Reposition cursor according to how the active window wants it var active = ActiveWindow; if (active != null) { if (active.CursorPosition.X >= 0 && active.CursorPosition.Y >= 0 && active.CursorPosition.X < active.FrameRectangle.Width - 2 && active.CursorPosition.Y < active.FrameRectangle.Height - 2) { Interop.SetConsoleCursorPosition(_buffer, new Interop.Coord( (short)(active.FrameRectangle.Left + active.CursorPosition.X + 1), (short)(active.FrameRectangle.Top + active.CursorPosition.Y + 1) )); Interop.SetConsoleCursorVisible(_buffer, active.CursorVisible); } else { Interop.SetConsoleCursorVisible(_buffer, false); } } else { Interop.SetConsoleCursorVisible(_buffer, false); } } // Cancel from the process loop bool _continueProcessing = false; public void EndProcessing() { _continueProcessing = false; } // Process public void Process() { // Notfiy OnEnterProcessing(); // Make active ViewMode = ViewMode.Desktop; // Process loop _continueProcessing = true; while (_continueProcessing) { // Do any update Update(); // Bring console to front BringToFront(); // Read the next key var key = Console.ReadKey(true); // Switch windows? if (key.Key == ConsoleKey.Tab) { if (key.Modifiers == ConsoleModifiers.Control) { if (_windows.Any()) { var top = _windows[_windows.Count - 1]; _windows.RemoveAt(_windows.Count-1); _windows.Insert(0, top); _needRedraw = true; } continue; } if (key.Modifiers == (ConsoleModifiers.Control | ConsoleModifiers.Shift)) { if (_windows.Any()) { var top = _windows[0]; _windows.RemoveAt(0); _windows.Add(top); _needRedraw = true; } continue; } } // Toggle to stdout? if (key.Key == ConsoleKey.F4 && key.Modifiers == 0) { ViewMode = ViewMode.StdOut; Console.ReadKey(true); ViewMode = ViewMode.Desktop; continue; } // Preview key event if (OnPreviewKey(key)) continue; // Send key to the active window var aw = ActiveWindow; if (aw!= null) { aw.OnKey(key); } } // Notify OnLeaveProcessing(); // Finished return; } // Bring the console window to foreground IntPtr _oldForegroundWindow; public void BringToFront() { _oldForegroundWindow = Interop.GetActiveWindow(); Interop.SetForegroundWindow(Interop.GetConsoleWindow()); } // Restore the old active foreground window public void RestoreForegroundWindow() { if (_oldForegroundWindow==IntPtr.Zero) { _oldForegroundWindow = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle; } if (_oldForegroundWindow !=IntPtr.Zero) { Interop.SetForegroundWindow(_oldForegroundWindow); _oldForegroundWindow = IntPtr.Zero; } } // Set the view mode (desktop or stdout) ViewMode _viewMode = ViewMode.StdOut; public ViewMode ViewMode { get { return _viewMode; } set { if (_viewMode !=value) { _viewMode = value; if (_viewMode==ViewMode.Desktop) { Interop.SetConsoleActiveScreenBuffer(_buffer); } else { Interop.SetConsoleActiveScreenBuffer(_stdout); } } } } } }