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 module dcell.key;
7 
8 import std.string;
9 import std.algorithm;
10 
11 /**
12  * Key represents a single, unmodified key stroke.  Modifier keys are
13  * not considered as Keys.
14  */
15 enum Key
16 {
17     none = 0,
18 
19     // control keys are assigned their ASCII values
20     // these definitions should not be used by apps, but instead
21     // by using key rune, the character, and a modifier.
22     // TODO: consider just removing these.
23     ctrlSpace = 0,
24     ctrlA,
25     ctrlB,
26     ctrlC,
27     ctrlD,
28     ctrlE,
29     ctrlF,
30     ctrlG,
31     ctrlH,
32     ctrlI,
33     ctrlJ,
34     ctrlK,
35     ctrlL,
36     ctrlM,
37     ctrlN,
38     ctrlO,
39     ctrlP,
40     ctrlQ,
41     ctrlR,
42     ctrlS,
43     ctrlT,
44     ctrlU,
45     ctrlV,
46     ctrlW,
47     ctrlX,
48     ctrlY,
49     ctrlZ,
50     ctrlLeftSq, // Escape
51     ctrlBackslash,
52     ctrlRightSq,
53     ctrlCarat,
54     ctrlUnderscore,
55 
56     rune = 256, // start of defined keys, numbered high to avoid conflicts
57     up,
58     down,
59     right,
60     left,
61     upLeft,
62     upRight,
63     downLeft,
64     downRight,
65     center,
66     pgUp,
67     pgDn,
68     home,
69     end,
70     insert,
71     del2, // secondary delete button, apart from DEL
72     help,
73     exit,
74     clear,
75     cancel,
76     print,
77     pause,
78     backtab,
79     f1,
80     f2,
81     f3,
82     f4,
83     f5,
84     f6,
85     f7,
86     f8,
87     f9,
88     f10,
89     f11,
90     f12,
91     f13,
92     f14,
93     f15,
94     f16,
95     f17,
96     f18,
97     f19,
98     f20,
99     f21,
100     f22,
101     f23,
102     f24,
103     f25,
104     f26,
105     f27,
106     f28,
107     f29,
108     f30,
109     f31,
110     f32,
111     f33,
112     f34,
113     f35,
114     f36,
115     f37,
116     f38,
117     f39,
118     f40,
119     f41,
120     f42,
121     f43,
122     f44,
123     f45,
124     f46,
125     f47,
126     f48,
127     f49,
128     f50,
129     f51,
130     f52,
131     f53,
132     f54,
133     f55,
134     f56,
135     f57,
136     f58,
137     f59,
138     f60,
139     f61,
140     f62,
141     f63,
142     f64,
143 
144     // convenience aliases
145     backspace = ctrlH,
146     tab = ctrlI,
147     esc = ctrlLeftSq,
148     enter = ctrlM,
149     del = 0x7F, // Note del2 has a different value
150 }
151 
152 static immutable dstring[Key] keyNames;
153 shared static this()
154 {
155     keyNames[Key.enter] = "Enter";
156     keyNames[Key.backspace] = "Backspace";
157     keyNames[Key.tab] = "Tab";
158     keyNames[Key.backtab] = "Backtab";
159     keyNames[Key.esc] = "Esc";
160     keyNames[Key.del2] = "Delete2";
161     keyNames[Key.del] = "Delete";
162     keyNames[Key.insert] = "Insert";
163     keyNames[Key.up] = "Up";
164     keyNames[Key.down] = "Down";
165     keyNames[Key.left] = "Left";
166     keyNames[Key.right] = "Right";
167     keyNames[Key.home] = "Home";
168     keyNames[Key.end] = "End";
169     keyNames[Key.upLeft] = "UpLeft";
170     keyNames[Key.upRight] = "UpRight";
171     keyNames[Key.downLeft] = "DownLeft";
172     keyNames[Key.downRight] = "DownRight";
173     keyNames[Key.center] = "Center";
174     keyNames[Key.pgDn] = "PgDn";
175     keyNames[Key.pgUp] = "PgUp";
176     keyNames[Key.clear] = "Clear";
177     keyNames[Key.exit] = "Exit";
178     keyNames[Key.cancel] = "Cancel";
179     keyNames[Key.pause] = "Pause";
180     keyNames[Key.print] = "Print";
181     keyNames[Key.f1] = "F1";
182     keyNames[Key.f2] = "F2";
183     keyNames[Key.f3] = "F3";
184     keyNames[Key.f4] = "F4";
185     keyNames[Key.f5] = "F5";
186     keyNames[Key.f6] = "F6";
187     keyNames[Key.f7] = "F7";
188     keyNames[Key.f8] = "F8";
189     keyNames[Key.f9] = "F9";
190     keyNames[Key.f10] = "F10";
191     keyNames[Key.f11] = "F11";
192     keyNames[Key.f12] = "F12";
193     keyNames[Key.f13] = "F13";
194     keyNames[Key.f14] = "F14";
195     keyNames[Key.f15] = "F15";
196     keyNames[Key.f16] = "F16";
197     keyNames[Key.f17] = "F17";
198     keyNames[Key.f18] = "F18";
199     keyNames[Key.f19] = "F19";
200     keyNames[Key.f20] = "F20";
201     keyNames[Key.f21] = "F21";
202     keyNames[Key.f22] = "F22";
203     keyNames[Key.f23] = "F23";
204     keyNames[Key.f24] = "F24";
205     keyNames[Key.f25] = "F25";
206     keyNames[Key.f26] = "F26";
207     keyNames[Key.f27] = "F27";
208     keyNames[Key.f28] = "F28";
209     keyNames[Key.f29] = "F29";
210     keyNames[Key.f30] = "F30";
211     keyNames[Key.f31] = "F31";
212     keyNames[Key.f32] = "F32";
213     keyNames[Key.f33] = "F33";
214     keyNames[Key.f34] = "F34";
215     keyNames[Key.f35] = "F35";
216     keyNames[Key.f36] = "F36";
217     keyNames[Key.f37] = "F37";
218     keyNames[Key.f38] = "F38";
219     keyNames[Key.f39] = "F39";
220     keyNames[Key.f40] = "F40";
221     keyNames[Key.f41] = "F41";
222     keyNames[Key.f42] = "F42";
223     keyNames[Key.f43] = "F43";
224     keyNames[Key.f44] = "F44";
225     keyNames[Key.f45] = "F45";
226     keyNames[Key.f46] = "F46";
227     keyNames[Key.f47] = "F47";
228     keyNames[Key.f48] = "F48";
229     keyNames[Key.f49] = "F49";
230     keyNames[Key.f50] = "F50";
231     keyNames[Key.f51] = "F51";
232     keyNames[Key.f52] = "F52";
233     keyNames[Key.f53] = "F53";
234     keyNames[Key.f54] = "F54";
235     keyNames[Key.f55] = "F55";
236     keyNames[Key.f56] = "F56";
237     keyNames[Key.f57] = "F57";
238     keyNames[Key.f58] = "F58";
239     keyNames[Key.f59] = "F59";
240     keyNames[Key.f60] = "F60";
241     keyNames[Key.f61] = "F61";
242     keyNames[Key.f62] = "F62";
243     keyNames[Key.f63] = "F63";
244     keyNames[Key.f64] = "F64";
245     keyNames[Key.ctrlA] = "Ctrl-A";
246     keyNames[Key.ctrlB] = "Ctrl-B";
247     keyNames[Key.ctrlC] = "Ctrl-C";
248     keyNames[Key.ctrlD] = "Ctrl-D";
249     keyNames[Key.ctrlE] = "Ctrl-E";
250     keyNames[Key.ctrlF] = "Ctrl-F";
251     keyNames[Key.ctrlG] = "Ctrl-G";
252     keyNames[Key.ctrlJ] = "Ctrl-J";
253     keyNames[Key.ctrlK] = "Ctrl-K";
254     keyNames[Key.ctrlL] = "Ctrl-L";
255     keyNames[Key.ctrlN] = "Ctrl-N";
256     keyNames[Key.ctrlO] = "Ctrl-O";
257     keyNames[Key.ctrlP] = "Ctrl-P";
258     keyNames[Key.ctrlQ] = "Ctrl-Q";
259     keyNames[Key.ctrlR] = "Ctrl-R";
260     keyNames[Key.ctrlS] = "Ctrl-S";
261     keyNames[Key.ctrlT] = "Ctrl-T";
262     keyNames[Key.ctrlU] = "Ctrl-U";
263     keyNames[Key.ctrlV] = "Ctrl-V";
264     keyNames[Key.ctrlW] = "Ctrl-W";
265     keyNames[Key.ctrlX] = "Ctrl-X";
266     keyNames[Key.ctrlY] = "Ctrl-Y";
267     keyNames[Key.ctrlZ] = "Ctrl-Z";
268     keyNames[Key.ctrlSpace] = "Ctrl-Space";
269     keyNames[Key.ctrlUnderscore] = "Ctrl-_";
270     keyNames[Key.ctrlRightSq] = "Ctrl-]";
271     keyNames[Key.ctrlBackslash] = "Ctrl-\\";
272     keyNames[Key.ctrlCarat] = "Ctrl-^";
273 }
274 
275 /** 
276  * Modifiers are special keys that when combined with other keys
277  * change their meaning.
278  */
279 enum Modifiers
280 {
281     none = 0,
282     shift = 1 << 0,
283     ctrl = 1 << 1,
284     alt = 1 << 2,
285     meta = 1 << 3,
286 }
287 
288 /**
289  * This represents a single pressed key, possibly with modifiers.
290  */
291 struct KeyEvent
292 {
293     Key key; /// Key pressed.
294     dchar ch; /// Set if key == rune.
295     Modifiers mod; /// Any modifiers pressed together.
296 
297     dstring toString() const pure
298     {
299         dstring s = "";
300         if (mod & Modifiers.meta)
301         {
302             s ~= "Meta-";
303         }
304         if (mod & Modifiers.alt)
305         {
306             s ~= "Alt-";
307         }
308         dstring kn = "";
309         if (key in keyNames)
310         {
311             kn = keyNames[key];
312         }
313         else if (key == Key.rune)
314         {
315             kn = [ch];
316         }
317         else
318         {
319 
320             kn = format("Key[%02X]"d, key);
321         }
322         if ((mod & Modifiers.ctrl) && !startsWith(kn, "Ctrl-"))
323         {
324             s ~= "Ctrl-";
325         }
326 
327         return s ~ kn;
328     }
329 }