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