xref: /haiku/src/bin/keymap/Keymap.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
1 /*
2  *	Copyright (c) 2004-2006, Haiku, Inc.
3  *
4  *  This software is part of the Haiku distribution and is covered
5  *  by the Haiku license.
6  *
7  *  Author: Jérôme Duval
8  */
9 
10 
11 #include "Keymap.h"
12 
13 #include <ByteOrder.h>
14 #include <File.h>
15 #include <FindDirectory.h>
16 #include <Path.h>
17 #include <String.h>
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <regex.h>
22 
23 
24 #define CHARS_TABLE_MAXSIZE  10000
25 
26 
27 void
28 dump_map(FILE* file, const char* name, int32* map)
29 {
30 	fprintf(file, "\t%s:{\n", name);
31 
32 	for (uint32 i = 0; i < 16; i++) {
33 		fprintf(file, "\t\t");
34 		for (uint32 j = 0; j < 8; j++) {
35 			fprintf(file, "0x%04lx,%s", map[i * 8 + j], j < 7 ? " " : "");
36 		}
37 		fprintf(file, "\n");
38 	}
39 	fprintf(file, "\t},\n");
40 }
41 
42 
43 void
44 dump_keys(FILE* file, const char* name, int32* keys)
45 {
46 	fprintf(file, "\t%s:{\n", name);
47 
48 	for (uint32 i = 0; i < 4; i++) {
49 		fprintf(file, "\t\t");
50 		for (uint32 j = 0; j < 8; j++) {
51 			fprintf(file, "0x%04lx,%s", keys[i * 8 + j], j < 7 ? " " : "");
52 		}
53 		fprintf(file, "\n");
54 	}
55 	fprintf(file, "\t},\n");
56 }
57 
58 
59 //	#pragma mark -
60 
61 
62 Keymap::Keymap()
63 	:
64 	fChars(NULL),
65 	fCharsSize(0)
66 {
67 	memset(&fKeys, 0, sizeof(fKeys));
68 }
69 
70 
71 Keymap::~Keymap()
72 {
73 	delete[] fChars;
74 }
75 
76 
77 void
78 Keymap::GetKey(char *chars, int32 offset, char* string)
79 {
80 	int size = chars[offset++];
81 	char str[32];
82 	memset(str, 0, 32);
83 	memset(string, 0, 32);
84 
85 	switch (size) {
86 		case 0:
87 			// Not mapped
88 			sprintf(str, "''");
89 			break;
90 
91 		case 1:
92 			// 1-byte UTF-8/ASCII character
93 			if ((uint8)chars[offset] < 0x20
94 				|| (uint8)chars[offset] > 0x7e)
95 				sprintf(str, "0x%02x", (uint8)chars[offset]);
96 			else
97 				sprintf(str, "'%s%c'",
98 					(chars[offset] == '\\' || chars[offset] == '\'') ? "\\" : "", chars[offset]);
99 			break;
100 
101 		default:
102 			// n-byte UTF-8/ASCII character
103 			sprintf(str, "0x");
104 			for (int i = 0; i < size; i++) {
105 				sprintf(str + 2*(i+1), "%02x", (uint8)chars[offset+i]);
106 			}
107 			break;
108 	}
109 
110 	strncpy(string, str, strlen(str) < 12 ? strlen(str) : 12);
111 		// TODO: Huh?
112 }
113 
114 
115 void
116 Keymap::Dump()
117 {
118 	printf("#!/bin/keymap -l\n"
119 		"#\n"
120 		"#\tRaw key numbering for 101 keyboard...\n"
121 		"#                                                                                        [sys]       [brk]\n"
122 		"#                                                                                         0x7e        0x7f\n"
123 		"# [esc]       [ f1] [ f2] [ f3] [ f4] [ f5] [ f6] [ f7] [ f8] [ f9] [f10] [f11] [f12]    [prn] [scr] [pau]\n"
124 		"#  0x01        0x02  0x03  0x04  0x05  0x06  0x07  0x08  0x09  0x0a  0x0b  0x0c  0x0d     0x0e  0x0f  0x10     K E Y P A D   K E Y S\n"
125 		"#\n"
126 		"# [ ` ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ] [ - ] [ = ] [bck]    [ins] [hme] [pup]    [num] [ / ] [ * ] [ - ]\n"
127 		"#  0x11  0x12  0x13  0x14  0x15  0x16  0x17  0x18  0x19  0x1a  0x1b  0x1c  0x1d  0x1e     0x1f  0x20  0x21     0x22  0x23  0x24  0x25\n"
128 		"#\n"
129 		"# [tab] [ q ] [ w ] [ e ] [ r ] [ t ] [ y ] [ u ] [ i ] [ o ] [ p ] [ [ ] [ ] ] [ \\ ]    [del] [end] [pdn]    [ 7 ] [ 8 ] [ 9 ] [ + ]\n"
130 		"#  0x26  0x27  0x28  0x29  0x2a  0x2b  0x2c  0x2d  0x2e  0x2f  0x30  0x31  0x32  0x33     0x34  0x35  0x36     0x37  0x38  0x39  0x3a\n"
131 		"#\n"
132 		"# [cap] [ a ] [ s ] [ d ] [ f ] [ g ] [ h ] [ j ] [ k ] [ l ] [ ; ] [ ' ] [  enter  ]                         [ 4 ] [ 5 ] [ 6 ]\n"
133 		"#  0x3b  0x3c  0x3d  0x3e  0x3f  0x40  0x41  0x42  0x43  0x44  0x45  0x46     0x47                             0x48  0x49  0x4a\n"
134 		"#\n"
135 		"# [shift]     [ z ] [ x ] [ c ] [ v ] [ b ] [ n ] [ m ] [ , ] [ . ] [ / ]     [shift]          [ up]          [ 1 ] [ 2 ] [ 3 ] [ent]\n"
136 		"#   0x4b       0x4c  0x4d  0x4e  0x4f  0x50  0x51  0x52  0x53  0x54  0x55       0x56            0x57           0x58  0x59  0x5a  0x5b\n"
137 		"#\n"
138 		"# [ctr]             [cmd]             [  space  ]             [cmd]             [ctr]    [lft] [dwn] [rgt]    [ 0 ] [ . ]\n"
139 		"#  0x5c              0x5d                 0x5e                 0x5f              0x60     0x61  0x62  0x63     0x64  0x65\n"
140 		"#\n"
141 		"#\tNOTE: On a Microsoft Natural Keyboard:\n"
142 		"#\t\t\tleft option  = 0x66\n"
143 		"#\t\t\tright option = 0x67\n"
144 		"#\t\t\tmenu key     = 0x68\n"
145 		"#\tNOTE: On an Apple Extended Keyboard:\n"
146 		"#\t\t\tleft option  = 0x66\n"
147 		"#\t\t\tright option = 0x67\n"
148 		"#\t\t\tkeypad '='   = 0x6a\n"
149 		"#\t\t\tpower key    = 0x6b\n");
150 
151 	printf("Version = %ld\n", fKeys.version);
152 	printf("CapsLock = 0x%02lx\n", fKeys.caps_key);
153 	printf("ScrollLock = 0x%02lx\n", fKeys.scroll_key);
154 	printf("NumLock = 0x%02lx\n", fKeys.num_key);
155 	printf("LShift = 0x%02lx\n", fKeys.left_shift_key);
156 	printf("RShift = 0x%02lx\n", fKeys.right_shift_key);
157 	printf("LCommand = 0x%02lx\n", fKeys.left_command_key);
158 	printf("RCommand = 0x%02lx\n", fKeys.right_command_key);
159 	printf("LControl = 0x%02lx\n", fKeys.left_control_key);
160 	printf("RControl = 0x%02lx\n", fKeys.right_control_key);
161 	printf("LOption = 0x%02lx\n", fKeys.left_option_key);
162 	printf("ROption = 0x%02lx\n", fKeys.right_option_key);
163 	printf("Menu = 0x%02lx\n", fKeys.menu_key);
164 	printf("#\n"
165 		"# Lock settings\n"
166 		"# To set NumLock, do the following:\n"
167 		"#   LockSettings = NumLock\n"
168 		"#\n"
169 		"# To set everything, do the following:\n"
170 		"#   LockSettings = CapsLock NumLock ScrollLock\n"
171 		"#\n");
172 	printf("LockSettings = ");
173 	if (fKeys.lock_settings & B_CAPS_LOCK)
174 		printf("CapsLock ");
175 	if (fKeys.lock_settings & B_NUM_LOCK)
176 		printf("NumLock ");
177 	if (fKeys.lock_settings & B_SCROLL_LOCK)
178 		printf("ScrollLock ");
179 	printf("\n");
180 	printf("# Legend:\n"
181 		"#   n = Normal\n"
182 		"#   s = Shift\n"
183 		"#   c = Control\n"
184 		"#   C = CapsLock\n"
185 		"#   o = Option\n"
186 		"# Key      n        s        c        o        os       C        Cs       Co       Cos     \n");
187 
188 	for (int idx = 0; idx < 128; idx++) {
189 		char normalKey[32];
190 		char shiftKey[32];
191 		char controlKey[32];
192 		char optionKey[32];
193 		char optionShiftKey[32];
194 		char capsKey[32];
195 		char capsShiftKey[32];
196 		char optionCapsKey[32];
197 		char optionCapsShiftKey[32];
198 
199 		GetKey(fChars, fKeys.normal_map[idx], normalKey);
200 		GetKey(fChars, fKeys.shift_map[idx], shiftKey);
201 		GetKey(fChars, fKeys.control_map[idx], controlKey);
202 		GetKey(fChars, fKeys.option_map[idx], optionKey);
203 		GetKey(fChars, fKeys.option_shift_map[idx], optionShiftKey);
204 		GetKey(fChars, fKeys.caps_map[idx], capsKey);
205 		GetKey(fChars, fKeys.caps_shift_map[idx], capsShiftKey);
206 		GetKey(fChars, fKeys.option_caps_map[idx], optionCapsKey);
207 		GetKey(fChars, fKeys.option_caps_shift_map[idx], optionCapsShiftKey);
208 
209 		printf("Key 0x%02x = %-9s%-9s%-9s%-9s%-9s%-9s%-9s%-9s%-9s\n", idx, normalKey, shiftKey, controlKey,
210 			optionKey, optionShiftKey, capsKey, capsShiftKey, optionCapsKey, optionCapsShiftKey);
211 	}
212 
213 	int32* deadOffsets[] = {
214 		fKeys.acute_dead_key,
215 		fKeys.grave_dead_key,
216 		fKeys.circumflex_dead_key,
217 		fKeys.dieresis_dead_key,
218 		fKeys.tilde_dead_key
219 	};
220 
221 	char labels[][12] = {
222 		"Acute",
223 		"Grave",
224 		"Circumflex",
225 		"Diaeresis",
226 		"Tilde"
227 	};
228 
229 	uint32 deadTables[] = {
230 		fKeys.acute_tables,
231 		fKeys.grave_tables,
232 		fKeys.circumflex_tables,
233 		fKeys.dieresis_tables,
234 		fKeys.tilde_tables
235 	};
236 
237 	for (int i = 0; i<5; i++) {
238 		for (int idx = 0; idx < 32; idx++ ) {
239 			char deadKey[32];
240 			char secondKey[32];
241 			GetKey(fChars, deadOffsets[i][idx++], deadKey);
242 			GetKey(fChars, deadOffsets[i][idx], secondKey);
243 			printf("%s %-9s = %-9s\n", labels[i], deadKey, secondKey);
244 		}
245 
246 		printf("%sTab = ", labels[i]);
247 
248 		if (deadTables[i] & B_NORMAL_TABLE)
249 			printf("Normal ");
250 		if (deadTables[i] & B_SHIFT_TABLE)
251 			printf("Shift ");
252 		if (deadTables[i] & B_CONTROL_TABLE)
253 			printf("Control ");
254 		if (deadTables[i] & B_OPTION_TABLE)
255 			printf("Option ");
256 		if (deadTables[i] & B_OPTION_SHIFT_TABLE)
257 			printf("Option-Shift ");
258 		if (deadTables[i] & B_CAPS_TABLE)
259 			printf("CapsLock ");
260 		if (deadTables[i] & B_CAPS_SHIFT_TABLE)
261 			printf("CapsLock-Shift ");
262 		if (deadTables[i] & B_OPTION_CAPS_TABLE)
263 			printf("CapsLock-Option ");
264 		if (deadTables[i] & B_OPTION_CAPS_SHIFT_TABLE)
265 			printf("CapsLock-Option-Shift ");
266 		printf("\n");
267 	}
268 }
269 
270 
271 status_t
272 Keymap::LoadCurrent()
273 {
274 #ifdef __BEOS__
275 	key_map *keys = NULL;
276 	get_key_map(&keys, &fChars);
277 	if (!keys)
278 		return B_ERROR;
279 
280 	memcpy(&fKeys, keys, sizeof(fKeys));
281 	free(keys);
282 	return B_OK;
283 
284 #else	// ! __BEOS__
285 	fprintf(stderr, "Unsupported operation on this platform!\n");
286 	exit(1);
287 #endif	// ! __BEOS__
288 }
289 
290 
291 /*!
292 	Load a map from a file.
293 
294 	file format in big endian:
295 		struct key_map
296 		uint32 size of following charset
297 		charset (offsets go into this with size of character followed by character)
298 */
299 status_t
300 Keymap::Load(entry_ref &ref)
301 {
302 	status_t err;
303 
304 	BFile file(&ref, B_READ_ONLY);
305 	if ((err = file.InitCheck()) != B_OK)
306 		return err;
307 
308 	if (file.Read(&fKeys, sizeof(fKeys)) < (ssize_t)sizeof(fKeys))
309 		return B_BAD_VALUE;
310 
311 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++) {
312 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
313 	}
314 
315 	if (fKeys.version != 3)
316 		return KEYMAP_ERROR_UNKNOWN_VERSION;
317 
318 	if (file.Read(&fCharsSize, sizeof(uint32)) < (ssize_t)sizeof(uint32))
319 		return B_BAD_VALUE;
320 
321 	fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
322 	if (!fChars)
323 		delete[] fChars;
324 	fChars = new char[fCharsSize];
325 	if ((unsigned)file.Read(fChars, fCharsSize) != fCharsSize)
326 		return B_BAD_VALUE;
327 
328 	return B_OK;
329 }
330 
331 
332 void
333 Keymap::ComputeChars(const char *buffer, struct re_registers &regs, int i, int &offset)
334 {
335 	char *current = &fChars[offset + 1];
336 	char hexChars[12];
337 	uint32 length = 0;
338 	if (strncmp(buffer + regs.start[i], "''", regs.end[i] - regs.start[i]) == 0)
339 		length = 0;
340 	else if (sscanf(buffer + regs.start[i], "'%s'", current) > 0) {
341 		if (current[0] == '\\')
342 			current[0] = current[1];
343 		else if (current[0] == '\'')
344 			current[0] = ' ';
345 		length = 1;
346 	} else if (sscanf(buffer + regs.start[i], "0x%s", hexChars) > 0) {
347 		length = strlen(hexChars) / 2;
348 		for (uint32 j = 0; j < length; j++)
349 			sscanf(hexChars + 2*j, "%02hhx", current + j);
350 	}
351 	fChars[offset] = length;
352 	offset += length + 1;
353 }
354 
355 
356 void
357 Keymap::ComputeTables(const char *buffer, struct re_registers &regs, uint32 &table)
358 {
359 	for (int32 i=1; i<=9; i++) {
360 		if (regs.end[i] - regs.start[i] <= 0)
361 			break;
362 		if (strncmp(buffer + regs.start[i], "Normal", regs.end[i] - regs.start[i]) == 0)
363 			table |= B_NORMAL_TABLE;
364 		else if (strncmp(buffer + regs.start[i], "Shift", regs.end[i] - regs.start[i]) == 0)
365 			table |= B_SHIFT_TABLE;
366 		else if (strncmp(buffer + regs.start[i], "Control", regs.end[i] - regs.start[i]) == 0)
367 			table |= B_CONTROL_TABLE;
368 		else if (strncmp(buffer + regs.start[i], "Option", regs.end[i] - regs.start[i]) == 0)
369 			table |= B_OPTION_TABLE;
370 		else if (strncmp(buffer + regs.start[i], "Option-Shift", regs.end[i] - regs.start[i]) == 0)
371 			table |= B_OPTION_SHIFT_TABLE;
372 		else if (strncmp(buffer + regs.start[i], "CapsLock", regs.end[i] - regs.start[i]) == 0)
373 			table |= B_CAPS_TABLE;
374 		else if (strncmp(buffer + regs.start[i], "CapsLock-Shift", regs.end[i] - regs.start[i]) == 0)
375 			table |= B_CAPS_SHIFT_TABLE;
376 		else if (strncmp(buffer + regs.start[i], "CapsLock-Option", regs.end[i] - regs.start[i]) == 0)
377 			table |= B_OPTION_CAPS_TABLE;
378 		else if (strncmp(buffer + regs.start[i], "CapsLock-Option-Shift", regs.end[i] - regs.start[i]) == 0)
379 			table |= B_OPTION_CAPS_SHIFT_TABLE;
380 	}
381 }
382 
383 
384 status_t
385 Keymap::LoadSourceFromRef(entry_ref &ref)
386 {
387 	status_t err;
388 
389 	BFile file(&ref, B_READ_ONLY);
390 	if ((err = file.InitCheck()) != B_OK)
391 		return err;
392 
393 	int fd = file.Dup();
394 	FILE* f = fdopen(fd, "r");
395 	if (f != NULL) {
396 		err = LoadSource(f);
397 		fclose(f);
398 	} else
399 		err = B_FILE_ERROR;
400 
401 	return err;
402 }
403 
404 
405 // i couldn't put patterns and pattern bufs on the stack without segfaulting
406 // regexp patterns
407 const char versionPattern[] = "Version[[:space:]]+=[[:space:]]+\\([[:digit:]]+\\)";
408 const char capslockPattern[] = "CapsLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
409 const char scrolllockPattern[] = "ScrollLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
410 const char numlockPattern[] = "NumLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
411 const char lshiftPattern[] = "LShift[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
412 const char rshiftPattern[] = "RShift[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
413 const char lcommandPattern[] = "LCommand[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
414 const char rcommandPattern[] = "RCommand[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
415 const char lcontrolPattern[] = "LControl[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
416 const char rcontrolPattern[] = "RControl[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
417 const char loptionPattern[] = "LOption[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
418 const char roptionPattern[] = "ROption[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
419 const char menuPattern[] = "Menu[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)";
420 const char locksettingsPattern[] = "LockSettings[[:space:]]+=[[:space:]]+\\([[:alnum:]]*\\)"
421 						"[[:space:]]*\\([[:alnum:]]*\\)"
422 						"[[:space:]]*\\([[:alnum:]]*\\)[[:space:]]*";
423 const char keyPattern[] = "Key[[:space:]]+\\([[:alnum:]]+\\)[[:space:]]+="
424 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
425 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
426 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
427 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
428 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
429 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
430 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
431 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
432 						"[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)"
433 						"[[:space:]]+";
434 
435 
436 const char acutePattern[] = "Acute[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
437 const char gravePattern[] = "Grave[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
438 const char circumflexPattern[] = "Circumflex[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
439 const char diaeresisPattern[] = "Diaeresis[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
440 const char tildePattern[] = "Tilde[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+";
441 const char acutetabPattern[] = "AcuteTab[[:space:]]+="
442 						"[[:space:]]+\\([[:alnum:]-]*\\)"
443 						"[[:space:]]*\\([[:alnum:]-]*\\)"
444 						"[[:space:]]*\\([[:alnum:]-]*\\)"
445 						"[[:space:]]*\\([[:alnum:]-]*\\)"
446 						"[[:space:]]*\\([[:alnum:]-]*\\)"
447 						"[[:space:]]*\\([[:alnum:]-]*\\)"
448 						"[[:space:]]*\\([[:alnum:]-]*\\)"
449 						"[[:space:]]*\\([[:alnum:]-]*\\)"
450 						"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
451 const char gravetabPattern[] = "GraveTab[[:space:]]+="
452 						"[[:space:]]+\\([[:alnum:]-]*\\)"
453 						"[[:space:]]*\\([[:alnum:]-]*\\)"
454 						"[[:space:]]*\\([[:alnum:]-]*\\)"
455 						"[[:space:]]*\\([[:alnum:]-]*\\)"
456 						"[[:space:]]*\\([[:alnum:]-]*\\)"
457 						"[[:space:]]*\\([[:alnum:]-]*\\)"
458 						"[[:space:]]*\\([[:alnum:]-]*\\)"
459 						"[[:space:]]*\\([[:alnum:]-]*\\)"
460 						"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
461 const char circumflextabPattern[] = "CircumflexTab[[:space:]]+="
462 						"[[:space:]]+\\([[:alnum:]-]*\\)"
463 						"[[:space:]]*\\([[:alnum:]-]*\\)"
464 						"[[:space:]]*\\([[:alnum:]-]*\\)"
465 						"[[:space:]]*\\([[:alnum:]-]*\\)"
466 						"[[:space:]]*\\([[:alnum:]-]*\\)"
467 						"[[:space:]]*\\([[:alnum:]-]*\\)"
468 						"[[:space:]]*\\([[:alnum:]-]*\\)"
469 						"[[:space:]]*\\([[:alnum:]-]*\\)"
470 						"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
471 const char diaeresistabPattern[] = "DiaeresisTab[[:space:]]+="
472 						"[[:space:]]+\\([[:alnum:]-]*\\)"
473 						"[[:space:]]*\\([[:alnum:]-]*\\)"
474 						"[[:space:]]*\\([[:alnum:]-]*\\)"
475 						"[[:space:]]*\\([[:alnum:]-]*\\)"
476 						"[[:space:]]*\\([[:alnum:]-]*\\)"
477 						"[[:space:]]*\\([[:alnum:]-]*\\)"
478 						"[[:space:]]*\\([[:alnum:]-]*\\)"
479 						"[[:space:]]*\\([[:alnum:]-]*\\)"
480 						"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
481 const char tildetabPattern[] = "TildeTab[[:space:]]+="
482 						"[[:space:]]+\\([[:alnum:]-]*\\)"
483 						"[[:space:]]*\\([[:alnum:]-]*\\)"
484 						"[[:space:]]*\\([[:alnum:]-]*\\)"
485 						"[[:space:]]*\\([[:alnum:]-]*\\)"
486 						"[[:space:]]*\\([[:alnum:]-]*\\)"
487 						"[[:space:]]*\\([[:alnum:]-]*\\)"
488 						"[[:space:]]*\\([[:alnum:]-]*\\)"
489 						"[[:space:]]*\\([[:alnum:]-]*\\)"
490 						"[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ;
491 
492 
493 // re_pattern_buffer buffers
494 struct re_pattern_buffer versionBuf;
495 struct re_pattern_buffer capslockBuf;
496 struct re_pattern_buffer scrolllockBuf;
497 struct re_pattern_buffer numlockBuf;
498 struct re_pattern_buffer lshiftBuf;
499 struct re_pattern_buffer rshiftBuf;
500 struct re_pattern_buffer lcommandBuf;
501 struct re_pattern_buffer rcommandBuf;
502 struct re_pattern_buffer lcontrolBuf;
503 struct re_pattern_buffer rcontrolBuf;
504 struct re_pattern_buffer loptionBuf;
505 struct re_pattern_buffer roptionBuf;
506 struct re_pattern_buffer menuBuf;
507 struct re_pattern_buffer locksettingsBuf;
508 struct re_pattern_buffer keyBuf;
509 struct re_pattern_buffer acuteBuf;
510 struct re_pattern_buffer graveBuf;
511 struct re_pattern_buffer circumflexBuf;
512 struct re_pattern_buffer diaeresisBuf;
513 struct re_pattern_buffer tildeBuf;
514 struct re_pattern_buffer acutetabBuf;
515 struct re_pattern_buffer gravetabBuf;
516 struct re_pattern_buffer circumflextabBuf;
517 struct re_pattern_buffer diaeresistabBuf;
518 struct re_pattern_buffer tildetabBuf;
519 
520 status_t
521 Keymap::LoadSource(FILE * f)
522 {
523 	reg_syntax_t syntax = RE_CHAR_CLASSES;
524 	re_set_syntax(syntax);
525 
526 	const char* error;
527 	error = re_compile_pattern(versionPattern, strlen(versionPattern), &versionBuf);
528 	if (error)
529 		fprintf(stderr, error);
530 	error = re_compile_pattern(capslockPattern, strlen(capslockPattern), &capslockBuf);
531 	if (error)
532 		fprintf(stderr, error);
533 	error = re_compile_pattern(scrolllockPattern, strlen(scrolllockPattern), &scrolllockBuf);
534 	if (error)
535 		fprintf(stderr, error);
536 	error = re_compile_pattern(numlockPattern, strlen(numlockPattern), &numlockBuf);
537 	if (error)
538 		fprintf(stderr, error);
539 	error = re_compile_pattern(lshiftPattern, strlen(lshiftPattern), &lshiftBuf);
540 	if (error)
541 		fprintf(stderr, error);
542 	error = re_compile_pattern(rshiftPattern, strlen(rshiftPattern), &rshiftBuf);
543 	if (error)
544 		fprintf(stderr, error);
545 	error = re_compile_pattern(lcommandPattern, strlen(lcommandPattern), &lcommandBuf);
546 	if (error)
547 		fprintf(stderr, error);
548 	error = re_compile_pattern(rcommandPattern, strlen(rcommandPattern), &rcommandBuf);
549 	if (error)
550 		fprintf(stderr, error);
551 	error = re_compile_pattern(lcontrolPattern, strlen(lcontrolPattern), &lcontrolBuf);
552 	if (error)
553 		fprintf(stderr, error);
554 	error = re_compile_pattern(rcontrolPattern, strlen(rcontrolPattern), &rcontrolBuf);
555 	if (error)
556 		fprintf(stderr, error);
557 	error = re_compile_pattern(loptionPattern, strlen(loptionPattern), &loptionBuf);
558 	if (error)
559 		fprintf(stderr, error);
560 	error = re_compile_pattern(roptionPattern, strlen(roptionPattern), &roptionBuf);
561 	if (error)
562 		fprintf(stderr, error);
563 	error = re_compile_pattern(menuPattern, strlen(menuPattern), &menuBuf);
564 	if (error)
565 		fprintf(stderr, error);
566 	error = re_compile_pattern(locksettingsPattern, strlen(locksettingsPattern), &locksettingsBuf);
567 	if (error)
568 		fprintf(stderr, error);
569 	error = re_compile_pattern(keyPattern, strlen(keyPattern), &keyBuf);
570 	if (error)
571 		fprintf(stderr, error);
572 	error = re_compile_pattern(acutePattern, strlen(acutePattern), &acuteBuf);
573 	if (error)
574 		fprintf(stderr, error);
575 	error = re_compile_pattern(gravePattern, strlen(gravePattern), &graveBuf);
576 	if (error)
577 		fprintf(stderr, error);
578 	error = re_compile_pattern(circumflexPattern, strlen(circumflexPattern), &circumflexBuf);
579 	if (error)
580 		fprintf(stderr, error);
581 	error = re_compile_pattern(diaeresisPattern, strlen(diaeresisPattern), &diaeresisBuf);
582 	if (error)
583 		fprintf(stderr, error);
584 	error = re_compile_pattern(tildePattern, strlen(tildePattern), &tildeBuf);
585 	if (error)
586 		fprintf(stderr, error);
587 	error = re_compile_pattern(acutetabPattern, strlen(acutetabPattern), &acutetabBuf);
588 	if (error)
589 		fprintf(stderr, error);
590 	error = re_compile_pattern(gravetabPattern, strlen(gravetabPattern), &gravetabBuf);
591 	if (error)
592 		fprintf(stderr, error);
593 	error = re_compile_pattern(circumflextabPattern, strlen(circumflextabPattern), &circumflextabBuf);
594 	if (error)
595 		fprintf(stderr, error);
596 	error = re_compile_pattern(diaeresistabPattern, strlen(diaeresistabPattern), &diaeresistabBuf);
597 	if (error)
598 		fprintf(stderr, error);
599 	error = re_compile_pattern(tildetabPattern, strlen(tildetabPattern), &tildetabBuf);
600 	if (error)
601 		fprintf(stderr, error);
602 
603 	char buffer[1024];
604 
605 	delete[] fChars;
606 	fChars = new char[CHARS_TABLE_MAXSIZE];
607 	fCharsSize = CHARS_TABLE_MAXSIZE;
608 	int offset = 0;
609 	int acuteOffset = 0;
610 	int graveOffset = 0;
611 	int circumflexOffset = 0;
612 	int diaeresisOffset = 0;
613 	int tildeOffset = 0;
614 
615 	int32 *maps[] = {
616 		fKeys.normal_map,
617 		fKeys.shift_map,
618 		fKeys.control_map,
619 		fKeys.option_map,
620 		fKeys.option_shift_map,
621 		fKeys.caps_map,
622 		fKeys.caps_shift_map,
623 		fKeys.option_caps_map,
624 		fKeys.option_caps_shift_map
625 	};
626 
627 	while (fgets(buffer, 1024-1, f) != NULL) {
628 		if (buffer[0] == '#' || buffer[0] == '\n')
629 			continue;
630 
631 		struct re_registers regs;
632 		if (re_search(&versionBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
633 			sscanf(buffer + regs.start[1], "%ld", &fKeys.version);
634 		} else if (re_search(&capslockBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
635 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.caps_key);
636 		} else if (re_search(&scrolllockBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
637 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.scroll_key);
638 		} else if (re_search(&numlockBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
639 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.num_key);
640 		} else if (re_search(&lshiftBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
641 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.left_shift_key);
642 		} else if (re_search(&rshiftBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
643 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.right_shift_key);
644 		} else if (re_search(&lcommandBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
645 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.left_command_key);
646 		} else if (re_search(&rcommandBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
647 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.right_command_key);
648 		} else if (re_search(&lcontrolBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
649 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.left_control_key);
650 		} else if (re_search(&rcontrolBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
651 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.right_control_key);
652 		} else if (re_search(&loptionBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
653 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.left_option_key);
654 		} else if (re_search(&roptionBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
655 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.right_option_key);
656 		} else if (re_search(&menuBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
657 			sscanf(buffer + regs.start[1], "0x%lx", &fKeys.menu_key);
658 		} else if (re_search(&locksettingsBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
659 			fKeys.lock_settings = 0;
660 			for (int32 i = 1; i <= 3; i++) {
661 				if (regs.end[i] - regs.start[i] <= 0)
662 					break;
663 
664 				if (strncmp(buffer + regs.start[i], "CapsLock", regs.end[i] - regs.start[i]) == 0)
665 					fKeys.lock_settings |= B_CAPS_LOCK;
666 				else if (strncmp(buffer + regs.start[i], "NumLock", regs.end[i] - regs.start[i]) == 0)
667 					fKeys.lock_settings |= B_NUM_LOCK;
668 				else if (strncmp(buffer + regs.start[i], "ScrollLock", regs.end[i] - regs.start[i]) == 0)
669 					fKeys.lock_settings |= B_SCROLL_LOCK;
670 			}
671 		} else if (re_search(&keyBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
672 			uint32 keyCode;
673 			if (sscanf(buffer + regs.start[1], "0x%lx", &keyCode) > 0) {
674 				for (int i = 2; i <= 10; i++) {
675 					maps[i - 2][keyCode] = offset;
676 					ComputeChars(buffer, regs, i, offset);
677 				}
678 			}
679 		} else if (re_search(&acuteBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
680 			for (int i = 1; i <= 2; i++) {
681 				fKeys.acute_dead_key[acuteOffset++] = offset;
682 				ComputeChars(buffer, regs, i, offset);
683 			}
684 		} else if (re_search(&graveBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
685 			for (int i = 1; i <= 2; i++) {
686 				fKeys.grave_dead_key[graveOffset++] = offset;
687 				ComputeChars(buffer, regs, i, offset);
688 			}
689 		} else if (re_search(&circumflexBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
690 			for (int i = 1; i <= 2; i++) {
691 				fKeys.circumflex_dead_key[circumflexOffset++] = offset;
692 				ComputeChars(buffer, regs, i, offset);
693 			}
694 		} else if (re_search(&diaeresisBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
695 			for (int i = 1; i <= 2; i++) {
696 				fKeys.dieresis_dead_key[diaeresisOffset++] = offset;
697 				ComputeChars(buffer, regs, i, offset);
698 			}
699 		} else if (re_search(&tildeBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
700 			for (int i = 1; i <= 2; i++) {
701 				fKeys.tilde_dead_key[tildeOffset++] = offset;
702 				ComputeChars(buffer, regs, i, offset);
703 			}
704 		} else if (re_search(&acutetabBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
705 			ComputeTables(buffer, regs, fKeys.acute_tables);
706 		} else if (re_search(&gravetabBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
707 			ComputeTables(buffer, regs, fKeys.grave_tables);
708 		} else if (re_search(&circumflextabBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
709 			ComputeTables(buffer, regs, fKeys.circumflex_tables);
710 		} else if (re_search(&diaeresistabBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
711 			ComputeTables(buffer, regs, fKeys.dieresis_tables);
712 		} else if (re_search(&tildetabBuf, buffer, strlen(buffer), 0, strlen(buffer), &regs) >= 0) {
713 			ComputeTables(buffer, regs, fKeys.tilde_tables);
714 		}
715 	}
716 
717 	fCharsSize = offset;
718 
719 	if (fKeys.version != 3)
720 		return KEYMAP_ERROR_UNKNOWN_VERSION;
721 
722 	return B_OK;
723 }
724 
725 
726 //! we save a map to a file
727 status_t
728 Keymap::Save(entry_ref &ref)
729 {
730 	status_t err;
731 
732 	BFile file(&ref, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE );
733 	if ((err = file.InitCheck()) != B_OK)
734 		return err;
735 
736 	// convert to big endian
737 	for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) {
738 		((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]);
739 	}
740 
741 	ssize_t bytesWritten = file.Write(&fKeys, sizeof(fKeys));
742 
743 	// convert endian back
744 	for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) {
745 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
746 	}
747 
748 	if (bytesWritten < (ssize_t)sizeof(fKeys))
749 		return B_ERROR;
750 	if (bytesWritten < B_OK)
751 		return bytesWritten;
752 
753 	uint32 charSize = B_HOST_TO_BENDIAN_INT32(fCharsSize);
754 
755 	bytesWritten = file.Write(&charSize, sizeof(uint32));
756 	if (bytesWritten < (ssize_t)sizeof(uint32))
757 		return B_ERROR;
758 	if (bytesWritten < B_OK)
759 		return bytesWritten;
760 
761 	bytesWritten = file.Write(fChars, fCharsSize);
762 	if (bytesWritten < (ssize_t)fCharsSize)
763 		return B_ERROR;
764 	if (bytesWritten < B_OK)
765 		return bytesWritten;
766 
767 	return B_OK;
768 }
769 
770 
771 /*!
772 	Save a keymap as C source file - this is used to get the default keymap
773 	into the input_server, for example.
774 */
775 void
776 Keymap::SaveAsHeader(entry_ref &ref)
777 {
778 	BPath path;
779 	status_t err = path.SetTo(&ref);
780 	if (err < B_OK)
781 		return;
782 
783 	FILE* file = fopen(path.Path(), "w");
784 
785 	fprintf(file, "/*\n"
786 		" * Haiku Keymap\n"
787 		" * This file is generated automatically. Don't edit!\n"
788 		" */\n\n");
789 	fprintf(file, "#include <InterfaceDefs.h>\n\n");
790 	fprintf(file, "const key_map kSystemKeymap = {\n");
791 	fprintf(file, "\tversion:%ld,\n", fKeys.version);
792 	fprintf(file, "\tcaps_key:0x%lx,\n", fKeys.caps_key);
793 	fprintf(file, "\tscroll_key:0x%lx,\n", fKeys.scroll_key);
794 	fprintf(file, "\tnum_key:0x%lx,\n", fKeys.num_key);
795 	fprintf(file, "\tleft_shift_key:0x%lx,\n", fKeys.left_shift_key);
796 	fprintf(file, "\tright_shift_key:0x%lx,\n", fKeys.right_shift_key);
797 	fprintf(file, "\tleft_command_key:0x%lx,\n", fKeys.left_command_key);
798 	fprintf(file, "\tright_command_key:0x%lx,\n", fKeys.right_command_key);
799 	fprintf(file, "\tleft_control_key:0x%lx,\n", fKeys.left_control_key);
800 	fprintf(file, "\tright_control_key:0x%lx,\n", fKeys.right_control_key);
801 	fprintf(file, "\tleft_option_key:0x%lx,\n", fKeys.left_option_key);
802 	fprintf(file, "\tright_option_key:0x%lx,\n", fKeys.right_option_key);
803 	fprintf(file, "\tmenu_key:0x%lx,\n", fKeys.menu_key);
804 	fprintf(file, "\tlock_settings:0x%lx,\n", fKeys.lock_settings);
805 
806 	dump_map(file, "control_map", fKeys.control_map);
807 	dump_map(file, "option_caps_shift_map", fKeys.option_caps_shift_map);
808 	dump_map(file, "option_caps_map", fKeys.option_caps_map);
809 	dump_map(file, "option_shift_map", fKeys.option_shift_map);
810 	dump_map(file, "option_map", fKeys.option_map);
811 	dump_map(file, "caps_shift_map", fKeys.caps_shift_map);
812 	dump_map(file, "caps_map", fKeys.caps_map);
813 	dump_map(file, "shift_map", fKeys.shift_map);
814 	dump_map(file, "normal_map", fKeys.normal_map);
815 
816 	dump_keys(file, "acute_dead_key", fKeys.acute_dead_key);
817 	dump_keys(file, "grave_dead_key", fKeys.grave_dead_key);
818 
819 	dump_keys(file, "circumflex_dead_key", fKeys.circumflex_dead_key);
820 	dump_keys(file, "dieresis_dead_key", fKeys.dieresis_dead_key);
821 	dump_keys(file, "tilde_dead_key", fKeys.tilde_dead_key);
822 
823 	fprintf(file, "\tacute_tables:0x%lx,\n", fKeys.acute_tables);
824 	fprintf(file, "\tgrave_tables:0x%lx,\n", fKeys.grave_tables);
825 	fprintf(file, "\tcircumflex_tables:0x%lx,\n", fKeys.circumflex_tables);
826 	fprintf(file, "\tdieresis_tables:0x%lx,\n", fKeys.dieresis_tables);
827 	fprintf(file, "\ttilde_tables:0x%lx,\n", fKeys.tilde_tables);
828 
829 	fprintf(file, "};\n\n");
830 
831 	fprintf(file, "const char kSystemKeyChars[] = {\n");
832 	for (uint32 i = 0; i < fCharsSize; i++) {
833 		if (i % 10 == 0) {
834 			if (i > 0)
835 				fprintf(file, "\n");
836 			fprintf(file, "\t");
837 		} else
838 			fprintf(file, " ");
839 
840 		fprintf(file, "0x%02x,", (uint8)fChars[i]);
841 	}
842 	fprintf(file, "\n};\n\n");
843 
844 	fprintf(file, "const uint32 kSystemKeyCharsSize = %ld;\n", fCharsSize);
845 	fclose(file);
846 }
847 
848 
849 /*!
850 	We need to know if a key is a modifier key to choose
851 	a valid key when several are pressed together
852 */
853 bool
854 Keymap::IsModifierKey(uint32 keyCode)
855 {
856 	return keyCode == fKeys.caps_key
857 		|| keyCode == fKeys.num_key
858 		|| keyCode == fKeys.left_shift_key
859 		|| keyCode == fKeys.right_shift_key
860 		|| keyCode == fKeys.left_command_key
861 		|| keyCode == fKeys.right_command_key
862 		|| keyCode == fKeys.left_control_key
863 		|| keyCode == fKeys.right_control_key
864 		|| keyCode == fKeys.left_option_key
865 		|| keyCode == fKeys.right_option_key
866 		|| keyCode == fKeys.menu_key;
867 }
868 
869 
870 //! Tell if a key is a dead key, needed for draw a dead key
871 uint8
872 Keymap::IsDeadKey(uint32 keyCode, uint32 modifiers)
873 {
874 	int32 offset;
875 	uint32 tableMask = 0;
876 
877 	switch (modifiers & 0xcf) {
878 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; tableMask = B_SHIFT_TABLE; break;
879 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; tableMask = B_CAPS_TABLE; break;
880 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; tableMask = B_CAPS_SHIFT_TABLE; break;
881 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; tableMask = B_OPTION_TABLE; break;
882 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; tableMask = B_OPTION_SHIFT_TABLE; break;
883 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; tableMask = B_OPTION_CAPS_TABLE; break;
884 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; tableMask = B_OPTION_CAPS_SHIFT_TABLE; break;
885 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; tableMask = B_CONTROL_TABLE; break;
886 		default: offset = fKeys.normal_map[keyCode]; tableMask = B_NORMAL_TABLE; break;
887 	}
888 
889 	if (offset <= 0)
890 		return 0;
891 	uint32 numBytes = fChars[offset];
892 
893 	if (!numBytes)
894 		return 0;
895 
896 	char chars[4];
897 	strncpy(chars, &(fChars[offset+1]), numBytes );
898 	chars[numBytes] = 0;
899 
900 	int32 deadOffsets[] = {
901 		fKeys.acute_dead_key[1],
902 		fKeys.grave_dead_key[1],
903 		fKeys.circumflex_dead_key[1],
904 		fKeys.dieresis_dead_key[1],
905 		fKeys.tilde_dead_key[1]
906 	};
907 
908 	uint32 deadTables[] = {
909 		fKeys.acute_tables,
910 		fKeys.grave_tables,
911 		fKeys.circumflex_tables,
912 		fKeys.dieresis_tables,
913 		fKeys.tilde_tables
914 	};
915 
916 	for (int32 i = 0; i < 5; i++) {
917 		if ((deadTables[i] & tableMask) == 0)
918 			continue;
919 
920 		if (offset == deadOffsets[i])
921 			return i+1;
922 
923 		uint32 deadNumBytes = fChars[deadOffsets[i]];
924 
925 		if (!deadNumBytes)
926 			continue;
927 
928 		if (strncmp(chars, &(fChars[deadOffsets[i]+1]), deadNumBytes ) == 0)
929 			return i+1;
930 	}
931 	return 0;
932 }
933 
934 
935 //! Tell if a key is a dead second key, needed for draw a dead second key
936 bool
937 Keymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey)
938 {
939 	if (!activeDeadKey)
940 		return false;
941 
942 	int32 offset;
943 
944 	switch (modifiers & 0xcf) {
945 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; break;
946 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; break;
947 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; break;
948 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; break;
949 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; break;
950 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; break;
951 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; break;
952 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; break;
953 		default: offset = fKeys.normal_map[keyCode]; break;
954 	}
955 
956 	uint32 numBytes = fChars[offset];
957 
958 	if (!numBytes)
959 		return false;
960 
961 	int32* deadOffsets[] = {
962 		fKeys.acute_dead_key,
963 		fKeys.grave_dead_key,
964 		fKeys.circumflex_dead_key,
965 		fKeys.dieresis_dead_key,
966 		fKeys.tilde_dead_key
967 	};
968 
969 	int32 *deadOffset = deadOffsets[activeDeadKey - 1];
970 
971 	for (int32 i = 0; i < 32; i++) {
972 		if (offset == deadOffset[i])
973 			return true;
974 
975 		uint32 deadNumBytes = fChars[deadOffset[i]];
976 
977 		if (!deadNumBytes)
978 			continue;
979 
980 		if (strncmp(&(fChars[offset+1]), &(fChars[deadOffset[i]+1]), deadNumBytes) == 0)
981 			return true;
982 
983 		i++;
984 	}
985 	return false;
986 }
987 
988 
989 //! Get the char for a key given modifiers and active dead key
990 void
991 Keymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey,
992 	char** chars, int32* numBytes)
993 {
994 	int32 offset;
995 
996 	*numBytes = 0;
997 	*chars = NULL;
998 
999 	// here we take NUMLOCK into account
1000 	if (modifiers & B_NUM_LOCK) {
1001 		switch (keyCode) {
1002 			case 0x37:
1003 			case 0x38:
1004 			case 0x39:
1005 			case 0x48:
1006 			case 0x49:
1007 			case 0x4a:
1008 			case 0x58:
1009 			case 0x59:
1010 			case 0x5a:
1011 			case 0x64:
1012 			case 0x65:
1013 				modifiers ^= B_SHIFT_KEY;
1014 		}
1015 	}
1016 
1017 	// here we choose the right map given the modifiers
1018 	switch (modifiers & 0xcf) {
1019 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; break;
1020 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; break;
1021 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; break;
1022 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; break;
1023 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; break;
1024 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; break;
1025 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; break;
1026 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; break;
1027 		default: offset = fKeys.normal_map[keyCode]; break;
1028 	}
1029 
1030 	// here we get the char size
1031 	*numBytes = fChars[offset];
1032 
1033 	if (!*numBytes)
1034 		return;
1035 
1036 	// here we take an potential active dead key
1037 	int32 *dead_key;
1038 	switch (activeDeadKey) {
1039 		case 1: dead_key = fKeys.acute_dead_key; break;
1040 		case 2: dead_key = fKeys.grave_dead_key; break;
1041 		case 3: dead_key = fKeys.circumflex_dead_key; break;
1042 		case 4: dead_key = fKeys.dieresis_dead_key; break;
1043 		case 5: dead_key = fKeys.tilde_dead_key; break;
1044 
1045 		default:
1046 			// if not dead, we copy and return the char
1047 			char *str = *chars = new char[*numBytes + 1];
1048 			strncpy(str, &(fChars[offset+1]), *numBytes );
1049 			str[*numBytes] = 0;
1050 			return;
1051 	}
1052 
1053 	// if dead key, we search for our current offset char in the dead key offset table
1054 	// string comparison is needed
1055 	for (int32 i = 0; i < 32; i++) {
1056 		if (strncmp(&(fChars[offset+1]), &(fChars[dead_key[i]+1]), *numBytes ) == 0) {
1057 			*numBytes = fChars[dead_key[i+1]];
1058 
1059 			switch (*numBytes) {
1060 				case 0:
1061 					// Not mapped
1062 					*chars = NULL;
1063 					break;
1064 
1065 				default:
1066 					// 1-, 2-, 3-, or 4-byte UTF-8 character
1067 					char *str = *chars = new char[*numBytes + 1];
1068 					strncpy(str, &fChars[dead_key[i+1]+1], *numBytes );
1069 					str[*numBytes] = 0;
1070 					break;
1071 			}
1072 			return;
1073 		}
1074 		i++;
1075 	}
1076 
1077 	// if not found we return the current char mapped
1078 	*chars = new char[*numBytes + 1];
1079 	strncpy(*chars, &(fChars[offset+1]), *numBytes );
1080 	(*chars)[*numBytes] = 0;
1081 }
1082 
1083 
1084 status_t _restore_key_map_();
1085 
1086 
1087 void
1088 Keymap::RestoreSystemDefault()
1089 {
1090 #ifdef __BEOS__
1091 	// work-around to get rid of this stupid find_directory_r() on Zeta
1092 #	ifdef find_directory
1093 #		undef find_directory
1094 #	endif
1095 	BPath path;
1096 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
1097 		return;
1098 
1099 	path.Append("Key_map");
1100 
1101 	BEntry ref(path.Path());
1102 	ref.Remove();
1103 
1104 	_restore_key_map_();
1105 #else	// ! __BEOS__
1106 	fprintf(stderr, "Unsupported operation on this platform!\n");
1107 	exit(1);
1108 #endif	// ! __BEOS__
1109 }
1110 
1111 
1112 void
1113 Keymap::SaveAsCurrent()
1114 {
1115 #ifdef __BEOS__
1116 	BPath path;
1117 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
1118 		return;
1119 
1120 	path.Append("Key_map");
1121 
1122 	entry_ref ref;
1123 	get_ref_for_path(path.Path(), &ref);
1124 
1125 	status_t err;
1126 	if ((err = Save(ref)) != B_OK) {
1127 		printf("error when saving : %s", strerror(err));
1128 		return;
1129 	}
1130 	Use();
1131 
1132 #else	// ! __BEOS__
1133 	fprintf(stderr, "Unsupported operation on this platform!\n");
1134 	exit(1);
1135 #endif	// ! __BEOS__
1136 }
1137 
1138 
1139 //! We make our input server use the map in /boot/home/config/settings/Keymap
1140 status_t
1141 Keymap::Use()
1142 {
1143 #ifdef __BEOS__
1144 	return _restore_key_map_();
1145 
1146 #else	// ! __BEOS__
1147 	fprintf(stderr, "Unsupported operation on this platform!\n");
1148 	exit(1);
1149 #endif	// ! __BEOS__
1150 }
1151