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