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