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