driver.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Collections.Generic;
  3. using Unix.Terminal;
  4. namespace Terminal {
  5. public struct Color {
  6. internal int value;
  7. public Color (int v)
  8. {
  9. value = v;
  10. }
  11. public static implicit operator int (Color c) => c.value;
  12. public static implicit operator Color (int v) => new Color (v);
  13. }
  14. public class ColorScheme {
  15. public Color Normal;
  16. public Color Focus;
  17. public Color HotNormal;
  18. public Color HotFocus;
  19. public Color Marked => HotNormal;
  20. public Color MarkedSelected => HotFocus;
  21. }
  22. public static class Colors {
  23. public static ColorScheme Base, Dialog, Menu, Error;
  24. }
  25. public abstract class ConsoleDriver {
  26. public abstract int Cols {get;}
  27. public abstract int Rows {get;}
  28. public abstract void Init ();
  29. public abstract void Move (int col, int row);
  30. public abstract void AddCh (int ch);
  31. public abstract void AddStr (string str);
  32. public abstract void PrepareToRun ();
  33. public abstract void Refresh ();
  34. public abstract void End ();
  35. public abstract void RedrawTop ();
  36. public abstract void SetColor (Color c);
  37. public abstract void DrawFrame (Rect region, bool fill);
  38. Rect clip;
  39. public Rect Clip {
  40. get => clip;
  41. set => this.clip = value;
  42. }
  43. }
  44. public class CursesDriver : ConsoleDriver {
  45. public override int Cols => Curses.Cols;
  46. public override int Rows => Curses.Lines;
  47. // Current row, and current col, tracked by Move/AddCh only
  48. int ccol, crow;
  49. bool needMove;
  50. public override void Move (int col, int row)
  51. {
  52. ccol = col;
  53. crow = row;
  54. if (Clip.Contains (col, row)) {
  55. Curses.move (row, col);
  56. needMove = false;
  57. } else {
  58. Curses.move (Clip.Y, Clip.X);
  59. needMove = true;
  60. }
  61. }
  62. public override void AddCh (int ch)
  63. {
  64. if (Clip.Contains (ccol, crow)) {
  65. if (needMove) {
  66. Curses.move (crow, ccol);
  67. needMove = false;
  68. }
  69. Curses.addch (ch);
  70. } else
  71. needMove = true;
  72. ccol++;
  73. }
  74. public override void AddStr (string str)
  75. {
  76. foreach (var c in str)
  77. AddCh ((int) c);
  78. }
  79. public override void Refresh() => Curses.refresh ();
  80. public override void End() => Curses.endwin ();
  81. public override void RedrawTop() => window.redrawwin ();
  82. public override void SetColor (Color c) => Curses.attrset (c.value);
  83. public Curses.Window window;
  84. static short last_color_pair;
  85. static Color MakeColor (short f, short b)
  86. {
  87. Curses.InitColorPair (++last_color_pair, f, b);
  88. return new Color () { value = Curses.ColorPair (last_color_pair) };
  89. }
  90. public override void PrepareToRun()
  91. {
  92. Curses.timeout (-1);
  93. }
  94. public override void DrawFrame (Rect region, bool fill)
  95. {
  96. int width = region.Width;
  97. int height = region.Height;
  98. int b;
  99. Move (region.X, region.Y);
  100. AddCh (Curses.ACS_ULCORNER);
  101. for (b = 0; b < width - 2; b++)
  102. AddCh (Curses.ACS_HLINE);
  103. AddCh (Curses.ACS_URCORNER);
  104. for (b = 1; b < height - 1; b++) {
  105. Move (region.X, region.Y + b);
  106. AddCh (Curses.ACS_VLINE);
  107. if (fill) {
  108. for (int x = 1; x < width - 1; x++)
  109. AddCh (' ');
  110. } else
  111. Move (region.X + width - 1, region.Y + b);
  112. AddCh (Curses.ACS_VLINE);
  113. }
  114. Move (region.X, region.Y + height - 1);
  115. AddCh (Curses.ACS_LLCORNER);
  116. for (b = 0; b < width - 2; b++)
  117. AddCh (Curses.ACS_HLINE);
  118. AddCh (Curses.ACS_LRCORNER);
  119. }
  120. public override void Init()
  121. {
  122. if (window != null)
  123. return;
  124. try {
  125. window = Curses.initscr ();
  126. } catch (Exception e){
  127. Console.WriteLine ("Curses failed to initialize, the exception is: " + e);
  128. }
  129. Curses.raw ();
  130. Curses.noecho ();
  131. Curses.Window.Standard.keypad (true);
  132. Colors.Base = new ColorScheme ();
  133. Colors.Dialog = new ColorScheme ();
  134. Colors.Menu = new ColorScheme ();
  135. Colors.Error = new ColorScheme ();
  136. Clip = new Rect (0, 0, Cols, Rows);
  137. if (Curses.HasColors){
  138. Curses.StartColor ();
  139. Curses.UseDefaultColors ();
  140. Colors.Base.Normal = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLUE);
  141. Colors.Base.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
  142. Colors.Base.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLUE);
  143. Colors.Base.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
  144. Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
  145. Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
  146. Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
  147. Colors.Menu.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLACK);
  148. Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
  149. Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
  150. Colors.Dialog.HotNormal = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_WHITE);
  151. Colors.Dialog.HotFocus = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_CYAN);
  152. Colors.Error.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_RED);
  153. Colors.Error.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
  154. Colors.Error.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_RED);
  155. Colors.Error.HotFocus = Colors.Error.HotNormal;
  156. } else {
  157. Colors.Base.Normal = Curses.A_NORMAL;
  158. Colors.Base.Focus = Curses.A_REVERSE;
  159. Colors.Base.HotNormal = Curses.A_BOLD;
  160. Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE;
  161. Colors.Menu.Normal = Curses.A_REVERSE;
  162. Colors.Menu.Focus = Curses.A_NORMAL;
  163. Colors.Menu.HotNormal = Curses.A_BOLD;
  164. Colors.Menu.HotFocus = Curses.A_NORMAL;
  165. Colors.Dialog.Normal = Curses.A_REVERSE;
  166. Colors.Dialog.Focus = Curses.A_NORMAL;
  167. Colors.Dialog.HotNormal = Curses.A_BOLD;
  168. Colors.Dialog.HotFocus = Curses.A_NORMAL;
  169. Colors.Error.Normal = Curses.A_BOLD;
  170. Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE;
  171. Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE;
  172. Colors.Error.HotFocus = Curses.A_REVERSE;
  173. }
  174. }
  175. }
  176. }