xref: /haiku/src/add-ons/input_server/devices/keyboard/Keymap.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
1 /*
2  * Copyright 2004-2006, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval
7  */
8 
9 
10 #include "Keymap.h"
11 
12 #include <ByteOrder.h>
13 #include <File.h>
14 
15 #include <new>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 
20 
21 static void
22 print_key(char *chars, int32 offset)
23 {
24 	int size = chars[offset++];
25 
26 	switch (size) {
27 		case 0:
28 			// Not mapped
29 			printf("N/A");
30 			break;
31 
32 		case 1:
33 			// 1-byte UTF-8/ASCII character
34 			printf("%c", chars[offset]);
35 			break;
36 
37 		default:
38 		{
39 			// 2-, 3-, or 4-byte UTF-8 character
40 			char *str = new (std::nothrow) char[size + 1];
41 			if (str == NULL)
42 				break;
43 
44 			strncpy(str, &(chars[offset]), size);
45 			str[size] = 0;
46 			printf("%s", str);
47 			delete [] str;
48 			break;
49 		}
50 	}
51 
52 	printf("\t");
53 }
54 
55 
56 //	#pragma mark -
57 
58 
59 void
60 Keymap::DumpKeymap()
61 {
62 	// Print a chart of the normal, shift, option, and option+shift
63 	// keys.
64 	printf("Key #\tNormal\tShift\tCaps\tC+S\tOption\tO+S\tO+C\tO+C+S\tControl\n");
65 
66 	for (int i = 0; i < 128; i++) {
67 		printf(" 0x%x\t", i);
68 		print_key(fChars, fKeys.normal_map[i]);
69 		print_key(fChars, fKeys.shift_map[i]);
70 		print_key(fChars, fKeys.caps_map[i]);
71 		print_key(fChars, fKeys.caps_shift_map[i]);
72 		print_key(fChars, fKeys.option_map[i]);
73 		print_key(fChars, fKeys.option_shift_map[i]);
74 		print_key(fChars, fKeys.option_caps_map[i]);
75 		print_key(fChars, fKeys.option_caps_shift_map[i]);
76 		print_key(fChars, fKeys.control_map[i]);
77 		printf("\n");
78 	}
79 }
80 
81 
82 Keymap::Keymap()
83 	:
84 	fChars(NULL)
85 {
86 	key_map *keys;
87 	get_key_map(&keys, &fChars);
88 
89 	if (keys) {
90 		memcpy(&fKeys, keys, sizeof(key_map));
91 		free(keys);
92 	}
93 }
94 
95 
96 Keymap::~Keymap()
97 {
98 	free(fChars);
99 }
100 
101 
102 /* we need to know if a key is a modifier key to choose
103 	a valid key when several are pressed together
104 */
105 bool
106 Keymap::IsModifierKey(uint32 keyCode)
107 {
108 	return keyCode == fKeys.caps_key
109 		|| keyCode == fKeys.num_key
110 		|| keyCode == fKeys.scroll_key
111 		|| keyCode == fKeys.left_shift_key
112 		|| keyCode == fKeys.right_shift_key
113 		|| keyCode == fKeys.left_command_key
114 		|| keyCode == fKeys.right_command_key
115 		|| keyCode == fKeys.left_control_key
116 		|| keyCode == fKeys.right_control_key
117 		|| keyCode == fKeys.left_option_key
118 		|| keyCode == fKeys.right_option_key
119 		|| keyCode == fKeys.menu_key;
120 }
121 
122 
123 //! We need to know a modifier for a key
124 uint32
125 Keymap::Modifier(uint32 keyCode)
126 {
127 	if (keyCode == fKeys.caps_key)
128 		return B_CAPS_LOCK;
129 	if (keyCode == fKeys.num_key)
130 		return B_NUM_LOCK;
131 	if (keyCode == fKeys.scroll_key)
132 		return B_SCROLL_LOCK;
133 	if (keyCode == fKeys.left_shift_key)
134 		return B_LEFT_SHIFT_KEY | B_SHIFT_KEY;
135 	if (keyCode == fKeys.right_shift_key)
136 		return B_RIGHT_SHIFT_KEY | B_SHIFT_KEY;
137 	if (keyCode == fKeys.left_command_key)
138 		return B_LEFT_COMMAND_KEY | B_COMMAND_KEY;
139 	if (keyCode == fKeys.right_command_key)
140 		return B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
141 	if (keyCode == fKeys.left_control_key)
142 		return B_LEFT_CONTROL_KEY | B_CONTROL_KEY;
143 	if (keyCode == fKeys.right_control_key)
144 		return B_RIGHT_CONTROL_KEY | B_CONTROL_KEY;
145 	if (keyCode == fKeys.left_option_key)
146 		return B_LEFT_OPTION_KEY | B_OPTION_KEY;
147 	if (keyCode == fKeys.right_option_key)
148 		return B_RIGHT_OPTION_KEY | B_OPTION_KEY;
149 	if (keyCode == fKeys.menu_key)
150 		return B_MENU_KEY;
151 
152 	return 0;
153 }
154 
155 
156 //! Tell if a key is a dead key, needed for draw a dead key
157 uint8
158 Keymap::IsDeadKey(uint32 keyCode, uint32 modifiers)
159 {
160 	if (keyCode >= 256)
161 		return 0;
162 
163 	int32 offset;
164 	uint32 tableMask = 0;
165 
166 	switch (modifiers & 0xcf) {
167 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; tableMask = B_SHIFT_TABLE; break;
168 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; tableMask = B_CAPS_TABLE; break;
169 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; tableMask = B_CAPS_SHIFT_TABLE; break;
170 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; tableMask = B_OPTION_TABLE; break;
171 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; tableMask = B_OPTION_SHIFT_TABLE; break;
172 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; tableMask = B_OPTION_CAPS_TABLE; break;
173 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; tableMask = B_OPTION_CAPS_SHIFT_TABLE; break;
174 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; tableMask = B_CONTROL_TABLE; break;
175 		default: offset = fKeys.normal_map[keyCode]; tableMask = B_NORMAL_TABLE; break;
176 	}
177 
178 	if (offset <= 0)
179 		return 0;
180 
181 	uint32 numBytes = fChars[offset];
182 	if (!numBytes)
183 		return 0;
184 
185 	char chars[4];
186 	strncpy(chars, &(fChars[offset+1]), numBytes );
187 	chars[numBytes] = 0;
188 
189 	int32 deadOffsets[] = {
190 		fKeys.acute_dead_key[1],
191 		fKeys.grave_dead_key[1],
192 		fKeys.circumflex_dead_key[1],
193 		fKeys.dieresis_dead_key[1],
194 		fKeys.tilde_dead_key[1]
195 	};
196 
197 	uint32 deadTables[] = {
198 		fKeys.acute_tables,
199 		fKeys.grave_tables,
200 		fKeys.circumflex_tables,
201 		fKeys.dieresis_tables,
202 		fKeys.tilde_tables
203 	};
204 
205 	for (int32 i = 0; i < 5; i++) {
206 		if ((deadTables[i] & tableMask) == 0)
207 			continue;
208 
209 		if (offset == deadOffsets[i])
210 			return i + 1;
211 
212 		uint32 deadNumBytes = fChars[deadOffsets[i]];
213 		if (!deadNumBytes)
214 			continue;
215 
216 		if (!strncmp(chars, &(fChars[deadOffsets[i] + 1]), deadNumBytes))
217 			return i + 1;
218 	}
219 	return 0;
220 }
221 
222 
223 //! Tell if a key is a dead second key, needed for draw a dead second key
224 bool
225 Keymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey)
226 {
227 	if (!activeDeadKey || keyCode >= 256)
228 		return false;
229 
230 	int32 offset;
231 
232 	switch (modifiers & 0xcf) {
233 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; break;
234 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; break;
235 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; break;
236 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; break;
237 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; break;
238 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; break;
239 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; break;
240 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; break;
241 		default: offset = fKeys.normal_map[keyCode]; break;
242 	}
243 
244 	if (offset <= 0)
245 		return false;
246 
247 	uint32 numBytes = fChars[offset];
248 	if (!numBytes)
249 		return false;
250 
251 	int32* deadOffsets[] = {
252 		fKeys.acute_dead_key,
253 		fKeys.grave_dead_key,
254 		fKeys.circumflex_dead_key,
255 		fKeys.dieresis_dead_key,
256 		fKeys.tilde_dead_key
257 	};
258 
259 	int32 *deadOffset = deadOffsets[activeDeadKey-1];
260 
261 	for (int32 i=0; i<32; i++) {
262 		if (offset == deadOffset[i])
263 			return true;
264 
265 		uint32 deadNumBytes = fChars[deadOffset[i]];
266 		if (!deadNumBytes)
267 			continue;
268 
269 		if (!strncmp(&(fChars[offset+1]), &(fChars[deadOffset[i]+1]), deadNumBytes))
270 			return true;
271 
272 		i++;
273 	}
274 
275 	return false;
276 }
277 
278 
279 //! Get the char for a key given modifiers and active dead key
280 void
281 Keymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey,
282 	char** chars, int32* numBytes)
283 {
284 	*numBytes = 0;
285 	*chars = NULL;
286 
287 	if (keyCode >= 256)
288 		return;
289 
290 	// here we take NUMLOCK into account
291 	if (modifiers & B_NUM_LOCK) {
292 		switch (keyCode) {
293 			case 0x37:
294 			case 0x38:
295 			case 0x39:
296 			case 0x48:
297 			case 0x49:
298 			case 0x4a:
299 			case 0x58:
300 			case 0x59:
301 			case 0x5a:
302 			case 0x64:
303 			case 0x65:
304 				modifiers ^= B_SHIFT_KEY;
305 				break;
306 		}
307 	}
308 
309 	int32 offset;
310 
311 	// here we choose the right map given the modifiers
312 	switch (modifiers & 0xcf) {
313 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; break;
314 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; break;
315 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; break;
316 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; break;
317 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; break;
318 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; break;
319 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; break;
320 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; break;
321 
322 		default:
323 			offset = fKeys.normal_map[keyCode];
324 			break;
325 	}
326 
327 	if (offset <= 0)
328 		return;
329 
330 	// here we get the char size
331 	*numBytes = fChars[offset];
332 	if (!*numBytes)
333 		return;
334 
335 	// here we take an potential active dead key
336 	int32 *deadKey;
337 	switch (activeDeadKey) {
338 		case 1: deadKey = fKeys.acute_dead_key; break;
339 		case 2: deadKey = fKeys.grave_dead_key; break;
340 		case 3: deadKey = fKeys.circumflex_dead_key; break;
341 		case 4: deadKey = fKeys.dieresis_dead_key; break;
342 		case 5: deadKey = fKeys.tilde_dead_key; break;
343 		default:
344 		{
345 			// if not dead, we copy and return the char
346 			char *str = *chars = new (std::nothrow) char[*numBytes + 1];
347 			if (str == NULL) {
348 				*numBytes = 0;
349 				return;
350 			}
351 			strncpy(str, &(fChars[offset + 1]), *numBytes);
352 			str[*numBytes] = 0;
353 			return;
354 		}
355 	}
356 
357 	// if dead key, we search for our current offset char in the dead key offset table
358 	// string comparison is needed
359 	for (int32 i = 0; i < 32; i++) {
360 		if (strncmp(&(fChars[offset + 1]), &(fChars[deadKey[i] + 1]), *numBytes) == 0) {
361 			*numBytes = fChars[deadKey[i + 1]];
362 
363 			switch (*numBytes) {
364 				case 0:
365 					// Not mapped
366 					*chars = NULL;
367 					break;
368 				default:
369 				{
370 					// 1-, 2-, 3-, or 4-byte UTF-8 character
371 					char *str = *chars = new (std::nothrow) char[*numBytes + 1];
372 					if (str == NULL) {
373 						*numBytes = 0;
374 						return;
375 					}
376 					strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes);
377 					str[*numBytes] = 0;
378 					break;
379 				}
380 			}
381 			return;
382 		}
383 		i++;
384 	}
385 
386 	// if not found we return the current char mapped
387 	*chars = new (std::nothrow) char[*numBytes + 1];
388 	if (*chars == NULL) {
389 		*numBytes = 0;
390 		return;
391 	}
392 
393 	strncpy(*chars, &(fChars[offset+1]), *numBytes );
394 	(*chars)[*numBytes] = 0;
395 }
396 
397 
398 status_t
399 Keymap::LoadCurrent()
400 {
401 	key_map *keys = NULL;
402 	free(fChars);
403 	fChars = NULL;
404 
405 	get_key_map(&keys, &fChars);
406 	if (!keys) {
407 		fprintf(stderr, "error while getting current keymap!\n");
408 		return B_ERROR;
409 	}
410 	memcpy(&fKeys, keys, sizeof(fKeys));
411 	free(keys);
412 	return B_OK;
413 }
414 
415