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