1 // Copyright 2022 Garrett D'Amore 2 // 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE or https://www.boost.org/LICENSE_1_0.txt) 5 6 /** 7 * Mouse demo (demonstrates input handling) 8 */ 9 module mouse; 10 11 import std.stdio; 12 import std.string; 13 import std.concurrency; 14 import core.stdc.stdlib; 15 16 import dcell; 17 18 void emitStr(Screen s, Coord pos, Style style, dstring str) 19 { 20 while (str != "") 21 { 22 s[pos] = Cell(str[0], style, 1); 23 str = str[1 .. $]; 24 pos.x++; 25 } 26 } 27 28 void order(ref int i1, ref int i2) 29 { 30 if (i2 < i1) 31 { 32 int v = i1; 33 i1 = i2; 34 i2 = v; 35 } 36 } 37 38 void order(ref Coord c1, ref Coord c2) 39 { 40 order(c1.x, c2.x); 41 order(c1.y, c2.y); 42 } 43 44 void drawBox(Screen s, Coord c1, Coord c2, Style style, dchar fill = ' ') 45 { 46 order(c1, c2); 47 Coord pos; 48 for (pos.x = c1.x; pos.x <= c2.x; pos.x++) 49 { 50 s[Coord(pos.x, c1.y)] = Cell(Glyph.horizLine, style); 51 s[Coord(pos.x, c2.y)] = Cell(Glyph.horizLine, style); 52 } 53 for (pos.y = c1.y + 1; pos.y < c2.y; pos.y++) 54 { 55 s[Coord(c1.x, pos.y)] = Cell(Glyph.vertLine, style); 56 s[Coord(c2.x, pos.y)] = Cell(Glyph.vertLine, style); 57 } 58 if (c1.y != c2.y && c1.x != c2.x) 59 { 60 s[Coord(c1.x, c1.y)] = Cell(Glyph.upperLeftCorner, style); 61 s[Coord(c2.x, c1.y)] = Cell(Glyph.upperRightCorner, style); 62 s[Coord(c1.x, c2.y)] = Cell(Glyph.lowerLeftCorner, style); 63 s[Coord(c2.x, c2.y)] = Cell(Glyph.lowerRightCorner, style); 64 } 65 for (pos.y = c1.y + 1; pos.y < c2.y; pos.y++) 66 { 67 for (pos.x = c1.x + 1; pos.x < c2.x; pos.x++) 68 { 69 s[pos] = Cell(fill, style); 70 } 71 } 72 } 73 74 void drawSelect(Screen s, Coord c1, Coord c2, bool sel) 75 { 76 order(c1, c2); 77 Coord pos; 78 for (pos.y = c1.y; pos.y < c2.y; pos.y++) 79 { 80 for (pos.x = c1.x; pos.x < c2.x; pos.x++) 81 { 82 if (sel) 83 s[pos].style.attr |= Attr.reverse; 84 else 85 s[pos].style.attr &= ~Attr.reverse; 86 } 87 } 88 } 89 90 void main() 91 { 92 import std.stdio; 93 94 auto s = newScreen(); 95 assert(s !is null); 96 97 dstring posFmt = "Mouse: %d, %d"; 98 dstring btnFmt = "Buttons: %s"; 99 dstring keyFmt = "Keys: %s"; 100 dstring pasteFmt = "Paste: [%d] %s"; 101 dstring bStr = ""; 102 dstring kStr = ""; 103 dstring pStr = ""; 104 bool pasting = false; 105 106 s.start(thisTid()); 107 s.showCursor(Cursor.hidden); 108 s.enableMouse(MouseEnable.all); 109 s.enablePaste(true); 110 Style white; 111 white.fg = Color.midnightBlue; 112 white.bg = Color.lightCoral; 113 Coord mousePos = Coord(-1, -1); 114 Coord oldTop = Coord(-1, -1); 115 Coord oldBot = Coord(-1, -1); 116 Coord bPos; 117 int esc = 0; 118 dchar lb; 119 120 for (;;) 121 { 122 auto ps = pStr; 123 if (ps.length > 25) 124 ps = "..." ~ ps[$ - 23 .. $]; 125 drawBox(s, Coord(1, 1), Coord(42, 7), white); 126 Coord pos = Coord(3, 2); 127 emitStr(s, pos, white, "Press ESC twice to exit, C to clear."); 128 pos.y++; 129 emitStr(s, pos, white, format(posFmt, mousePos.x, mousePos.y)); 130 pos.y++; 131 emitStr(s, pos, white, format(btnFmt, bStr)); 132 pos.y++; 133 emitStr(s, pos, white, format(keyFmt, kStr)); 134 pos.y++; 135 emitStr(s, pos, white, format(pasteFmt, pStr.length, ps)); 136 s.show(); 137 bStr = ""; 138 Event ev; 139 receive( 140 (Event ee) { ev = ee; } 141 ); 142 Style st; 143 st.bg = Color.red; 144 Style up; 145 up.bg = Color.blue; 146 up.fg = Color.black; 147 // clear previous selection 148 if (oldTop.x >= 0 && oldTop.y >= 0 && oldBot.x >= 0) 149 { 150 drawSelect(s, oldTop, oldBot, false); 151 } 152 pos = s.size(); 153 pos.x--; 154 pos.y--; 155 156 switch (ev.type) 157 { 158 case EventType.resize: 159 s.resize(); 160 s.sync(); 161 break; 162 case EventType.paste: 163 pStr = ev.paste.content; 164 break; 165 case EventType.key: 166 pStr = ""; 167 s[pos] = Cell('K', st); 168 switch (ev.key.key) 169 { 170 case Key.esc: 171 esc++; 172 if (esc > 1) 173 { 174 s.stop(); 175 exit(0); 176 } 177 break; 178 case Key.ctrlL: 179 s.sync(); 180 esc = 0; 181 break; 182 case Key.rune: 183 if (ev.key.ch == 'C' || ev.key.ch == 'c') 184 { 185 s.clear(); 186 } 187 esc = 0; 188 s[pos] = Cell('R', st); 189 break; 190 default: 191 break; 192 } 193 kStr = ev.key.toString(); 194 break; 195 case EventType.mouse: 196 if (ev.mouse.mod & Modifiers.shift) 197 bStr ~= " S"; 198 if (ev.mouse.mod & Modifiers.ctrl) 199 bStr ~= " C"; 200 if (ev.mouse.mod & Modifiers.alt) 201 bStr ~= " A"; 202 if (ev.mouse.mod & Modifiers.meta) 203 bStr ~= " M"; 204 if (ev.mouse.btn & Buttons.wheelUp) 205 bStr ~= " WU"; 206 if (ev.mouse.btn & Buttons.wheelDown) 207 bStr ~= " WD"; 208 if (ev.mouse.btn & Buttons.wheelLeft) 209 bStr ~= " WL"; 210 if (ev.mouse.btn & Buttons.wheelRight) 211 bStr ~= " WR"; 212 // we only want buttons, not wheel events 213 auto button = ev.mouse.btn; 214 button &= ~Buttons.wheelUp; 215 button &= ~Buttons.wheelDown; 216 button &= ~Buttons.wheelLeft; 217 button &= ~Buttons.wheelRight; 218 if (button == Buttons.none) 219 { 220 if (oldTop.x >= 0) 221 { 222 Style ns = up; 223 ns.bg = (cast(Color)(lb - '0')); 224 drawBox(s, oldTop, ev.mouse.pos, ns, lb); 225 oldTop = Coord(-1, -1); 226 oldBot = Coord(-1, -1); 227 } 228 } 229 else if (oldTop.x < 0) 230 { 231 oldTop = ev.mouse.pos; 232 } 233 if (button & Buttons.button1) 234 bStr ~= " B1"; 235 if (button & Buttons.button2) 236 bStr ~= " B2"; 237 if (button & Buttons.button3) 238 bStr ~= " B3"; 239 if (button & Buttons.button4) 240 bStr ~= " B4"; 241 if (button & Buttons.button5) 242 bStr ~= " B5"; 243 if (button & Buttons.button6) 244 bStr ~= " B6"; 245 if (button & Buttons.button7) 246 bStr ~= " B7"; 247 if (button & Buttons.button8) 248 bStr ~= " B8"; 249 if (bStr.length > 0) 250 lb = bStr[$ - 1]; 251 mousePos = ev.mouse.pos; 252 if (button != Buttons.none) 253 oldBot = ev.mouse.pos; 254 255 s[pos] = Cell('M', st); 256 break; 257 default: 258 s[pos] = Cell('X', st); 259 break; 260 } 261 if (oldTop.x >= 0 && oldBot.x >= 0) 262 { 263 drawSelect(s, oldTop, oldBot, true); 264 } 265 } 266 }