1 /**
2  * Key module for dcell containing definitiosn for various key strokes.
3  *
4  * Copyright: Copyright 2025 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     // NB: the low keys are empty because they are used for control
25     // encodings.  But we translate them to graph with a payload char.
26     // and ctrl modifier.
27 
28     // start of defined keys, numbered high to avoid conflicts
29     graph = 128, // special indicating that key is a grapheme
30     up,
31     down,
32     right,
33     left,
34     upLeft,
35     upRight,
36     downLeft,
37     downRight,
38     center,
39     pgUp,
40     pgDn,
41     home,
42     end,
43     insert,
44     del2, // secondary delete button, apart from DEL
45     help,
46     exit,
47     clear,
48     cancel,
49     print,
50     pause,
51     backtab,
52     f1,
53     f2,
54     f3,
55     f4,
56     f5,
57     f6,
58     f7,
59     f8,
60     f9,
61     f10,
62     f11,
63     f12,
64     f13,
65     f14,
66     f15,
67     f16,
68     f17,
69     f18,
70     f19,
71     f20,
72     f21,
73     f22,
74     f23,
75     f24,
76     f25,
77     f26,
78     f27,
79     f28,
80     f29,
81     f30,
82     f31,
83     f32,
84     f33,
85     f34,
86     f35,
87     f36,
88     f37,
89     f38,
90     f39,
91     f40,
92     f41,
93     f42,
94     f43,
95     f44,
96     f45,
97     f46,
98     f47,
99     f48,
100     f49,
101     f50,
102     f51,
103     f52,
104     f53,
105     f54,
106     f55,
107     f56,
108     f57,
109     f58,
110     f59,
111     f60,
112     f61,
113     f62,
114     f63,
115     f64,
116 
117     // convenience aliases
118     backspace = 0x08,
119     tab = 0x09,
120     esc = 0x1B,
121     enter = 0x0A,
122     del = 0x7F, // Note del2 has a different value
123 }
124 
125 static immutable dstring[Key] keyNames;
126 shared static this()
127 {
128     keyNames[Key.enter] = "Enter";
129     keyNames[Key.backspace] = "Backspace";
130     keyNames[Key.tab] = "Tab";
131     keyNames[Key.backtab] = "Backtab";
132     keyNames[Key.esc] = "Esc";
133     keyNames[Key.del2] = "Delete2";
134     keyNames[Key.del] = "Delete";
135     keyNames[Key.insert] = "Insert";
136     keyNames[Key.up] = "Up";
137     keyNames[Key.down] = "Down";
138     keyNames[Key.left] = "Left";
139     keyNames[Key.right] = "Right";
140     keyNames[Key.home] = "Home";
141     keyNames[Key.end] = "End";
142     keyNames[Key.upLeft] = "UpLeft";
143     keyNames[Key.upRight] = "UpRight";
144     keyNames[Key.downLeft] = "DownLeft";
145     keyNames[Key.downRight] = "DownRight";
146     keyNames[Key.center] = "Center";
147     keyNames[Key.pgDn] = "PgDn";
148     keyNames[Key.pgUp] = "PgUp";
149     keyNames[Key.clear] = "Clear";
150     keyNames[Key.exit] = "Exit";
151     keyNames[Key.cancel] = "Cancel";
152     keyNames[Key.pause] = "Pause";
153     keyNames[Key.print] = "Print";
154     keyNames[Key.f1] = "F1";
155     keyNames[Key.f2] = "F2";
156     keyNames[Key.f3] = "F3";
157     keyNames[Key.f4] = "F4";
158     keyNames[Key.f5] = "F5";
159     keyNames[Key.f6] = "F6";
160     keyNames[Key.f7] = "F7";
161     keyNames[Key.f8] = "F8";
162     keyNames[Key.f9] = "F9";
163     keyNames[Key.f10] = "F10";
164     keyNames[Key.f11] = "F11";
165     keyNames[Key.f12] = "F12";
166     keyNames[Key.f13] = "F13";
167     keyNames[Key.f14] = "F14";
168     keyNames[Key.f15] = "F15";
169     keyNames[Key.f16] = "F16";
170     keyNames[Key.f17] = "F17";
171     keyNames[Key.f18] = "F18";
172     keyNames[Key.f19] = "F19";
173     keyNames[Key.f20] = "F20";
174     keyNames[Key.f21] = "F21";
175     keyNames[Key.f22] = "F22";
176     keyNames[Key.f23] = "F23";
177     keyNames[Key.f24] = "F24";
178     keyNames[Key.f25] = "F25";
179     keyNames[Key.f26] = "F26";
180     keyNames[Key.f27] = "F27";
181     keyNames[Key.f28] = "F28";
182     keyNames[Key.f29] = "F29";
183     keyNames[Key.f30] = "F30";
184     keyNames[Key.f31] = "F31";
185     keyNames[Key.f32] = "F32";
186     keyNames[Key.f33] = "F33";
187     keyNames[Key.f34] = "F34";
188     keyNames[Key.f35] = "F35";
189     keyNames[Key.f36] = "F36";
190     keyNames[Key.f37] = "F37";
191     keyNames[Key.f38] = "F38";
192     keyNames[Key.f39] = "F39";
193     keyNames[Key.f40] = "F40";
194     keyNames[Key.f41] = "F41";
195     keyNames[Key.f42] = "F42";
196     keyNames[Key.f43] = "F43";
197     keyNames[Key.f44] = "F44";
198     keyNames[Key.f45] = "F45";
199     keyNames[Key.f46] = "F46";
200     keyNames[Key.f47] = "F47";
201     keyNames[Key.f48] = "F48";
202     keyNames[Key.f49] = "F49";
203     keyNames[Key.f50] = "F50";
204     keyNames[Key.f51] = "F51";
205     keyNames[Key.f52] = "F52";
206     keyNames[Key.f53] = "F53";
207     keyNames[Key.f54] = "F54";
208     keyNames[Key.f55] = "F55";
209     keyNames[Key.f56] = "F56";
210     keyNames[Key.f57] = "F57";
211     keyNames[Key.f58] = "F58";
212     keyNames[Key.f59] = "F59";
213     keyNames[Key.f60] = "F60";
214     keyNames[Key.f61] = "F61";
215     keyNames[Key.f62] = "F62";
216     keyNames[Key.f63] = "F63";
217     keyNames[Key.f64] = "F64";
218 }
219 
220 /**
221  * Modifiers are special keys that when combined with other keys
222  * change their meaning.
223  */
224 enum Modifiers
225 {
226     none = 0,
227     shift = 1 << 0,
228     ctrl = 1 << 1,
229     alt = 1 << 2,
230     meta = 1 << 3,
231     hyper = 1 << 4,
232 }
233 
234 /**
235  * KeyEvent represents a single pressed key, possibly with modifiers.
236  */
237 struct KeyEvent
238 {
239     Key key; /// Key pressed.
240     dchar ch; /// Set if key == rune.
241     Modifiers mod; /// Any modifiers pressed together.
242 
243     dstring toString() const pure
244     {
245         dstring s = "";
246         if (mod & Modifiers.ctrl)
247         {
248             s ~= "Ctrl-";
249         }
250         if (mod & Modifiers.shift)
251         {
252             s ~= "Shift-";
253         }
254         if (mod & Modifiers.meta)
255         {
256             s ~= "Meta-";
257         }
258         if (mod & Modifiers.alt)
259         {
260             s ~= "Alt-";
261         }
262         if (mod & Modifiers.hyper)
263         {
264             s ~= "Hyper-";
265         }
266         dstring kn = "";
267         if (key in keyNames)
268         {
269             kn = keyNames[key];
270         }
271         else if (key == Key.graph)
272         {
273             if (mod == Modifiers.none)
274             {
275                 kn = [ch];
276             }
277             else if (ch == ' ')
278             {
279                 kn = "Space";
280             }
281             else
282             {
283                 kn = [ch.toUpper];
284             }
285         }
286         else
287         {
288             kn = format("Key[%02X]"d, key);
289         }
290 
291         return s ~ kn;
292     }
293 }