Desktop.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. namespace ConFrames
  6. {
  7. public class Desktop
  8. {
  9. public Desktop(int width, int height)
  10. {
  11. // Save stdout
  12. _stdout = Interop.GetStdHandle(Interop.STD_OUTPUT_HANDLE);
  13. // Create buffer
  14. _buffer = Interop.CreateConsoleScreenBuffer(Interop.GENERIC_READWRITE, (uint)0, IntPtr.Zero, Interop.CONSOLE_TEXTMODE_BUFFER, IntPtr.Zero);
  15. // Default colors
  16. ActiveBorderBackgroundColor = ConsoleColor.Blue;
  17. ActiveBorderLineColor = ConsoleColor.White;
  18. InactiveBorderLineColor = ConsoleColor.Gray;
  19. InactiveBorderBackgroundColor = ConsoleColor.DarkBlue;
  20. // Default desktop size
  21. DesktopSize = new Size(width, height);
  22. DesktopColor = ConsoleColor.DarkBlue;
  23. }
  24. // Handle to the screen and stdout buffers
  25. IntPtr _stdout;
  26. IntPtr _buffer;
  27. // Desktop size
  28. Size _desktopSize;
  29. public Size DesktopSize
  30. {
  31. get { return _desktopSize; }
  32. set
  33. {
  34. try
  35. {
  36. // Try to set it
  37. Interop.SetBufferAndScreenSize(_buffer, (short)value.Width, (short)value.Height);
  38. // Save it
  39. _desktopSize = value;
  40. }
  41. catch
  42. {
  43. try
  44. {
  45. // try to set it back
  46. Interop.SetBufferAndScreenSize(_buffer, (short)_desktopSize.Width, (short)_desktopSize.Height);
  47. }
  48. catch { }
  49. throw;
  50. }
  51. }
  52. }
  53. // List of all windows
  54. List<Window> _windows = new List<Window>();
  55. // Register a new window
  56. internal void AddWindow(Window window)
  57. {
  58. if (_windows.IndexOf(window)<0)
  59. {
  60. _windows.Add(window);
  61. InvalidateDesktop();
  62. }
  63. }
  64. // Remove a window
  65. internal void RemoveWindow(Window window)
  66. {
  67. _windows.Remove(window);
  68. InvalidateDesktop();
  69. }
  70. // Colors
  71. public ConsoleColor ActiveBorderBackgroundColor
  72. {
  73. get;
  74. set;
  75. }
  76. public ConsoleColor ActiveBorderLineColor
  77. {
  78. get;
  79. set;
  80. }
  81. public ConsoleColor InactiveBorderBackgroundColor
  82. {
  83. get;
  84. set;
  85. }
  86. public ConsoleColor InactiveBorderLineColor
  87. {
  88. get;
  89. set;
  90. }
  91. // Color of area behind all windows
  92. ConsoleColor _desktopColor;
  93. public ConsoleColor DesktopColor
  94. {
  95. get
  96. {
  97. return _desktopColor;
  98. }
  99. set
  100. {
  101. _desktopColor = value;
  102. InvalidateDesktop();
  103. }
  104. }
  105. // Called just before repainting the screen
  106. protected virtual void OnWillUpdate()
  107. {
  108. }
  109. // Called just after repainting the screen
  110. protected virtual void OnDidUpdate()
  111. {
  112. }
  113. // Called just before waiting for input
  114. protected virtual void OnEnterProcessing()
  115. {
  116. }
  117. // Called just after waiting for input
  118. protected virtual void OnLeaveProcessing()
  119. {
  120. }
  121. // Preview received keys - return true if handled
  122. protected virtual bool OnPreviewKey(ConsoleKeyInfo key)
  123. {
  124. if (PreviewKey != null)
  125. return PreviewKey(key);
  126. return false;
  127. }
  128. // Handler
  129. public Func<ConsoleKeyInfo, bool> PreviewKey;
  130. // Get/Set the currently active window
  131. public Window ActiveWindow
  132. {
  133. get { return _windows.Count == 0 ? null : _windows[_windows.Count - 1]; }
  134. set
  135. {
  136. var oldActive = ActiveWindow;
  137. int pos = _windows.IndexOf(value);
  138. if (pos < _windows.Count-1)
  139. {
  140. _windows.RemoveAt(pos);
  141. _windows.Add(value);
  142. }
  143. if (oldActive!=ActiveWindow)
  144. {
  145. Invalidate(oldActive);
  146. Invalidate(ActiveWindow);
  147. }
  148. }
  149. }
  150. // Invalidate flags
  151. bool _needRedraw = false;
  152. bool _needClear = false;
  153. public void Invalidate(Window w)
  154. {
  155. _needRedraw = true;
  156. }
  157. public void InvalidateDesktop()
  158. {
  159. _needClear = true;
  160. _needRedraw = true;
  161. }
  162. // Update
  163. public void Update()
  164. {
  165. // Quit if we don't need a redraw
  166. if (_needRedraw)
  167. {
  168. // Notify
  169. OnWillUpdate();
  170. // Clear flag
  171. _needRedraw = false;
  172. // Do we need to clear?
  173. if (_needClear)
  174. {
  175. _needClear = false;
  176. // Get screens size
  177. Interop.CONSOLE_SCREEN_BUFFER_INFO info;
  178. Interop.GetConsoleScreenBufferInfo(_buffer, out info);
  179. // Create buffer
  180. CharInfo[] buf = new CharInfo[info.dwSize.X * info.dwSize.Y];
  181. // Clear buffer
  182. var defAttributes = (ushort)((ushort)0 | ((ushort)_desktopColor << 4));
  183. for (int i = 0; i < buf.Length; ++i)
  184. {
  185. buf[i].Attributes = defAttributes;
  186. buf[i].Char = (char)' ';
  187. }
  188. // Copy it
  189. var r = new Interop.SmallRect()
  190. {
  191. Top = (short)0,
  192. Left = (short)0,
  193. Right = (short)info.dwSize.X,
  194. Bottom = (short)info.dwSize.Y,
  195. };
  196. Interop.WriteConsoleOutput(_buffer,
  197. buf,
  198. new Interop.Coord() { X = (short)info.dwSize.X, Y = info.dwSize.Y },
  199. new Interop.Coord() { X = 0, Y = 0 },
  200. ref r);
  201. }
  202. // Draw all windows
  203. foreach (var w in _windows)
  204. {
  205. var buf = w.Draw();
  206. var r = new Interop.SmallRect()
  207. {
  208. Top = (short)w.FrameRectangle.Top,
  209. Left = (short)w.FrameRectangle.Left,
  210. Right = (short)w.FrameRectangle.Right,
  211. Bottom = (short)w.FrameRectangle.Bottom,
  212. };
  213. Interop.WriteConsoleOutput(_buffer,
  214. buf,
  215. new Interop.Coord() { X = (short)w.FrameRectangle.Width, Y = (short)w.FrameRectangle.Height },
  216. new Interop.Coord() { X = 0, Y = 0 },
  217. ref r);
  218. }
  219. // Finished
  220. OnDidUpdate();
  221. }
  222. // Reposition cursor according to how the active window wants it
  223. var active = ActiveWindow;
  224. if (active != null)
  225. {
  226. if (active.CursorPosition.X >= 0 && active.CursorPosition.Y >= 0 &&
  227. active.CursorPosition.X < active.FrameRectangle.Width - 2 &&
  228. active.CursorPosition.Y < active.FrameRectangle.Height - 2)
  229. {
  230. Interop.SetConsoleCursorPosition(_buffer, new Interop.Coord(
  231. (short)(active.FrameRectangle.Left + active.CursorPosition.X + 1),
  232. (short)(active.FrameRectangle.Top + active.CursorPosition.Y + 1)
  233. ));
  234. Interop.SetConsoleCursorVisible(_buffer, active.CursorVisible);
  235. }
  236. else
  237. {
  238. Interop.SetConsoleCursorVisible(_buffer, false);
  239. }
  240. }
  241. else
  242. {
  243. Interop.SetConsoleCursorVisible(_buffer, false);
  244. }
  245. }
  246. // Cancel from the process loop
  247. bool _continueProcessing = false;
  248. public void EndProcessing()
  249. {
  250. _continueProcessing = false;
  251. }
  252. // Process
  253. public void Process()
  254. {
  255. // Notfiy
  256. OnEnterProcessing();
  257. // Make active
  258. ViewMode = ViewMode.Desktop;
  259. // Process loop
  260. _continueProcessing = true;
  261. while (_continueProcessing)
  262. {
  263. // Do any update
  264. Update();
  265. // Bring console to front
  266. BringToFront();
  267. // Read the next key
  268. var key = Console.ReadKey(true);
  269. // Switch windows?
  270. if (key.Key == ConsoleKey.Tab)
  271. {
  272. if (key.Modifiers == ConsoleModifiers.Control)
  273. {
  274. if (_windows.Any())
  275. {
  276. var top = _windows[_windows.Count - 1];
  277. _windows.RemoveAt(_windows.Count-1);
  278. _windows.Insert(0, top);
  279. _needRedraw = true;
  280. }
  281. continue;
  282. }
  283. if (key.Modifiers == (ConsoleModifiers.Control | ConsoleModifiers.Shift))
  284. {
  285. if (_windows.Any())
  286. {
  287. var top = _windows[0];
  288. _windows.RemoveAt(0);
  289. _windows.Add(top);
  290. _needRedraw = true;
  291. }
  292. continue;
  293. }
  294. }
  295. // Toggle to stdout?
  296. if (key.Key == ConsoleKey.F4 && key.Modifiers == 0)
  297. {
  298. ViewMode = ViewMode.StdOut;
  299. Console.ReadKey(true);
  300. ViewMode = ViewMode.Desktop;
  301. continue;
  302. }
  303. // Preview key event
  304. if (OnPreviewKey(key))
  305. continue;
  306. // Send key to the active window
  307. var aw = ActiveWindow;
  308. if (aw!= null)
  309. {
  310. aw.OnKey(key);
  311. }
  312. }
  313. // Notify
  314. OnLeaveProcessing();
  315. // Finished
  316. return;
  317. }
  318. // Bring the console window to foreground
  319. IntPtr _oldForegroundWindow;
  320. public void BringToFront()
  321. {
  322. _oldForegroundWindow = Interop.GetActiveWindow();
  323. Interop.SetForegroundWindow(Interop.GetConsoleWindow());
  324. }
  325. // Restore the old active foreground window
  326. public void RestoreForegroundWindow()
  327. {
  328. if (_oldForegroundWindow==IntPtr.Zero)
  329. {
  330. _oldForegroundWindow = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
  331. }
  332. if (_oldForegroundWindow !=IntPtr.Zero)
  333. {
  334. Interop.SetForegroundWindow(_oldForegroundWindow);
  335. _oldForegroundWindow = IntPtr.Zero;
  336. }
  337. }
  338. // Set the view mode (desktop or stdout)
  339. ViewMode _viewMode = ViewMode.StdOut;
  340. public ViewMode ViewMode
  341. {
  342. get
  343. {
  344. return _viewMode;
  345. }
  346. set
  347. {
  348. if (_viewMode !=value)
  349. {
  350. _viewMode = value;
  351. if (_viewMode==ViewMode.Desktop)
  352. {
  353. Interop.SetConsoleActiveScreenBuffer(_buffer);
  354. }
  355. else
  356. {
  357. Interop.SetConsoleActiveScreenBuffer(_stdout);
  358. }
  359. }
  360. }
  361. }
  362. }
  363. }