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