1 /++ 2 This module just tries to simplify arsd.terminal.d by removing some features that aren't needed (yet, at least) 3 +/ 4 module qui.termwrap; 5 6 import arsd.terminal; 7 import std.datetime.stopwatch; 8 import std.conv : to; 9 10 public alias Color = arsd.terminal.Color; 11 12 /// Input events 13 public struct Event{ 14 /// Keyboard Event 15 struct Keyboard{ 16 private this(KeyboardEvent event){ 17 key = event.which; 18 } 19 //// what key was pressed 20 dchar key; 21 /// Non character keys (can match against `this.key`) 22 /// 23 /// copied from arsd.terminal 24 enum Key : dchar{ 25 Escape = 0x1b + 0xF0000, 26 F1 = 0x70 + 0xF0000, 27 F2 = 0x71 + 0xF0000, 28 F3 = 0x72 + 0xF0000, 29 F4 = 0x73 + 0xF0000, 30 F5 = 0x74 + 0xF0000, 31 F6 = 0x75 + 0xF0000, 32 F7 = 0x76 + 0xF0000, 33 F8 = 0x77 + 0xF0000, 34 F9 = 0x78 + 0xF0000, 35 F10 = 0x79 + 0xF0000, 36 F11 = 0x7A + 0xF0000, 37 F12 = 0x7B + 0xF0000, 38 LeftArrow = 0x25 + 0xF0000, 39 RightArrow = 0x27 + 0xF0000, 40 UpArrow = 0x26 + 0xF0000, 41 DownArrow = 0x28 + 0xF0000, 42 Insert = 0x2d + 0xF0000, 43 Delete = 0x2e + 0xF0000, 44 Home = 0x24 + 0xF0000, 45 End = 0x23 + 0xF0000, 46 PageUp = 0x21 + 0xF0000, 47 PageDown = 0x22 + 0xF0000, 48 } 49 /// Ctrl+Letter keys 50 enum CtrlKeys : dchar{ 51 CtrlA = 1, 52 CtrlB = 2, 53 CtrlC = 3, 54 CtrlD = 4, 55 CtrlE = 5, 56 CtrlF = 6, 57 CtrlG = 7, 58 //CtrlJ = 10, 59 CtrlK = 11, 60 CtrlL = 12, 61 CtrlM = 13, 62 CtrlN = 14, 63 CtrlO = 15, 64 CtrlP = 16, 65 CtrlQ = 17, 66 CtrlR = 18, 67 CtrlS = 19, 68 CtrlT = 20, 69 CtrlU = 21, 70 CtrlV = 22, 71 CtrlW = 23, 72 CtrlX = 24, 73 CtrlY = 25, 74 CtrlZ = 26, 75 } 76 /// Returns: true if the key pressed is a character 77 /// backspace, space, and tab are characters! 78 @property bool isChar(){ 79 return !(key >= Key.min && key <= Key.max) && !isCtrlKey(); 80 } 81 /// Returns: true if key is a Ctrl+Letter key 82 @property bool isCtrlKey(){ 83 return key >= CtrlKeys.min && key <= CtrlKeys.max && key!=8 && key!=9 && key!=10; 84 } 85 /// Returns: a string representation of the key pressed 86 @property string tostring(){ 87 if (isChar()) 88 return to!string(key); 89 if (isCtrlKey()) 90 return to!string(cast(CtrlKeys)key); 91 return to!string(cast(Key)key); 92 } 93 } 94 /// Mouse Event 95 struct Mouse{ 96 /// Buttons 97 enum Button{ 98 Left, /// Left mouse btn clicked 99 Right, /// Right mouse btn clicked 100 Middle, /// Middle mouse btn clicked 101 ScrollUp, /// Scroll up clicked 102 ScrollDown, /// Scroll Down clicked 103 None, /// no button pressed, mouse was hovered. Not supported on all terminals (works on xterm, not on konsole) 104 } 105 /// x and y position of cursor 106 int x, y; 107 /// what button was clicked 108 Button button; 109 /// constructor 110 this (Button btn, int xPos, int yPos){ 111 x = xPos; 112 y = yPos; 113 btn = button; 114 } 115 /// constructor, from arsd.terminal.MouseEvent 116 private this(MouseEvent mouseE){ 117 if (mouseE.buttons == MouseEvent.Button.Left) 118 this.button = this.Button.Left; 119 else if (mouseE.buttons == MouseEvent.Button.Right) 120 this.button = this.Button.Right; 121 else if (mouseE.buttons == MouseEvent.Button.Middle) 122 this.button = this.Button.Middle; 123 else if (mouseE.buttons == MouseEvent.Button.ScrollUp) 124 this.button = this.Button.ScrollUp; 125 else if (mouseE.buttons == MouseEvent.Button.ScrollDown) 126 this.button = this.Button.ScrollDown; 127 else if (mouseE.buttons == MouseEvent.Button.None) 128 this.button = this.Button.None; 129 this.x = mouseE.x; 130 this.y = mouseE.y; 131 } 132 } 133 /// Resize event 134 struct Resize{ 135 /// the new width and height after resize 136 int width, height; 137 } 138 /// types of events 139 enum Type{ 140 Keyboard, /// Keyboard event 141 Mouse, /// Mouse event 142 Resize, /// Resize event 143 HangupInterrupt, /// terminal closed or interrupt (Ctrl+C) 144 } 145 /// stores the type of event 146 private Type _type; 147 /// union to store events 148 union{ 149 Keyboard _key; /// stores keyboard event 150 Mouse _mouse; /// stores mouse event 151 Resize _resize; /// stores resize event 152 } 153 /// Returns: type of event 154 @property Type type(){ 155 return _type; 156 } 157 /// Returns: keyboard event. Make sure you check this.type so the wrong event isn't read 158 @property Keyboard keyboard(){ 159 return _key; 160 } 161 /// Returns: mouse event. Make sure you check this.type so the wrong event isn't read 162 @property Mouse mouse(){ 163 return _mouse; 164 } 165 /// Returns: resize event. Make sure you check this.type so the wrong event isn't read 166 @property Resize resize(){ 167 return _resize; 168 } 169 170 private this(Keyboard key){ 171 this._type = Type.Keyboard; 172 this._key = key; 173 } 174 175 private this(Mouse mouse){ 176 this._type = Type.Mouse; 177 this._mouse = mouse; 178 } 179 180 private this(Resize rsize){ 181 this._type = Type.Resize; 182 this._resize = rsize; 183 } 184 /// constructor, by default, its a hangupInterrupt 185 this(Type eType){ 186 this._type = eType; 187 } 188 } 189 190 191 192 /// Wrapper to arsd.terminal to make it bit easier to manage 193 public class TermWrapper{ 194 private: 195 Terminal _term; 196 RealTimeConsoleInput _input; 197 public: 198 /// constructor 199 this(){ 200 _term = Terminal(ConsoleOutputType.cellular); 201 _input = RealTimeConsoleInput(&_term,ConsoleInputFlags.allInputEvents | ConsoleInputFlags.raw); 202 } 203 ~this(){ 204 _term.clear; 205 _term.reset; 206 } 207 /// Returns: width of termial 208 @property int width(){ 209 return _term.width; 210 } 211 /// Returns: height of terminal 212 @property int height(){ 213 return _term.height; 214 } 215 /// fills all cells with a character 216 void fill(dchar ch){ 217 const int _w = width, _h = height; 218 dchar[] line; 219 line.length = _w; 220 line[] = ch; 221 // write line _h times 222 for (uint i = 0; i < _h; i ++){ 223 _term.moveTo(0,i); 224 _term.write(line); 225 } 226 } 227 /// fills a rectangle with a character 228 void fill(dchar ch, int x1, int x2, int y1, int y2){ 229 dchar[] line; 230 line.length = (x2 - x1) + 1; 231 line[] = ch; 232 foreach(i; y1 .. y2 +1){ 233 _term.moveTo(x1, i); 234 _term.write(line); 235 } 236 } 237 /// flush to terminal 238 void flush(){ 239 _term.flush(); 240 } 241 /// writes a character `ch` at a position `(x, y)` 242 void put(int x, int y, dchar ch){ 243 _term.moveTo(x, y); 244 _term.write(ch); 245 } 246 /// writes a character `ch` at a position `(x, y)` with `fg` as foreground and `bg` as background color 247 void put(int x, int y, dchar ch, Color fg, Color bg){ 248 _term.color(fg, bg); 249 _term.moveTo(x, y); 250 _term.write(ch); 251 } 252 /// sets colors 253 void color(Color fg, Color bg){ 254 _term.color(fg, bg); 255 } 256 /// writes a string at position `(x, y)` 257 void write(int x, int y, dstring str){ 258 _term.moveTo(x, y); 259 _term.write(str); 260 } 261 /// writes a string at position `(x, y)` with `fg` as foreground and `bg` as background color 262 void write(int x, int y, dstring str, Color fg, Color bg){ 263 _term.color(fg, bg); 264 _term.moveTo(x, y); 265 _term.write(str); 266 } 267 /// moves cursor to position 268 void moveCursor(int x, int y){ 269 _term.moveTo(x, y); 270 } 271 /// Set true to show cursor, false to hide cursor 272 @property bool cursorVisible(bool visibility){ 273 if (visibility) 274 _term.showCursor; 275 else 276 _term.hideCursor; 277 return visibility; 278 } 279 /// waits `msecTimeout` msecs for event to occur. Returns as soon as it occurs (or if one had occurred before calling it) 280 /// 281 /// Returns: true if event occured 282 bool getEvent(int msecTimeout, ref Event event){ 283 StopWatch sw; 284 sw.start; 285 while (msecTimeout - sw.peek.total!"msecs" > 0){ 286 if (_input.timedCheckForInput(cast(int)(msecTimeout - sw.peek.total!"msecs"))){ 287 InputEvent e = _input.nextEvent; 288 if (e.type == InputEvent.Type.HangupEvent || e.type == InputEvent.Type.UserInterruptionEvent){ 289 event = Event(Event.Type.HangupInterrupt); 290 return true; 291 } 292 if (e.type == InputEvent.Type.KeyboardEvent && 293 e.get!(InputEvent.Type.KeyboardEvent).pressed){ 294 event = Event(Event.Keyboard(e.get!(InputEvent.Type.KeyboardEvent))); 295 return true; 296 } 297 if (e.type == InputEvent.Type.MouseEvent){ 298 MouseEvent mouseE = e.get!(InputEvent.Type.MouseEvent); 299 event = Event(Event.Mouse(mouseE)); 300 return true; 301 }else if (e.type == InputEvent.Type.SizeChangedEvent){ 302 SizeChangedEvent resize = e.get!(InputEvent.Type.SizeChangedEvent); 303 event = Event(Event.Resize(resize.newWidth, resize.newHeight)); 304 return true; 305 } 306 } 307 } 308 sw.stop; 309 return false; 310 } 311 }