xref: /haiku/src/preferences/keymap/Keymap.cpp (revision 6c4a44e36ba846c54467103f884d65dfa13e7fcb)
1 /*
2  * Copyright 2004-2011 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  *		Axel Dörfler, axeld@pinc-software.de.
9  */
10 
11 
12 #include "Keymap.h"
13 
14 #include <new>
15 #include <stdio.h>
16 #include <string.h>
17 
18 #include <ByteOrder.h>
19 #include <File.h>
20 
21 #include <input_globals.h>
22 
23 
24 static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY
25 	| B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY;
26 
27 
28 static void
29 print_key(char *chars, int32 offset)
30 {
31 	int size = chars[offset++];
32 
33 	switch (size) {
34 		case 0:
35 			// Not mapped
36 			fputs("N/A", stdout);
37 			break;
38 
39 		case 1:
40 			// single-byte UTF-8/ASCII character
41 			fputc(chars[offset], stdout);
42 			break;
43 
44 		default:
45 		{
46 			// 2-, 3-, or 4-byte UTF-8 character
47 			char *str = new char[size + 1];
48 			strncpy(str, &chars[offset], size);
49 			str[size] = 0;
50 			fputs(str, stdout);
51 			delete[] str;
52 			break;
53 		}
54 	}
55 
56 	fputs("\t", stdout);
57 }
58 
59 
60 //	#pragma mark -
61 
62 
63 Keymap::Keymap()
64 	:
65 	fModificationMessage(NULL)
66 {
67 }
68 
69 
70 Keymap::~Keymap()
71 {
72 	delete fModificationMessage;
73 }
74 
75 
76 void
77 Keymap::SetTarget(BMessenger target, BMessage* modificationMessage)
78 {
79 	delete fModificationMessage;
80 
81 	fTarget = target;
82 	fModificationMessage = modificationMessage;
83 }
84 
85 
86 void
87 Keymap::SetName(const char* name)
88 {
89 	strlcpy(fName, name, sizeof(fName));
90 }
91 
92 
93 void
94 Keymap::DumpKeymap()
95 {
96 	if (fKeys.version != 3)
97 		return;
98 
99 	// Print a chart of the normal, shift, control, option, option+shift,
100 	// Caps, Caps+shift, Caps+option, and Caps+option+shift keys.
101 	puts("Key #\tn\ts\tc\to\tos\tC\tCs\tCo\tCos\n");
102 
103 	for (uint8 i = 0; i < 128; i++) {
104 		printf(" 0x%02x\t", i);
105 		print_key(fChars, fKeys.normal_map[i]);
106 		print_key(fChars, fKeys.shift_map[i]);
107 		print_key(fChars, fKeys.control_map[i]);
108 		print_key(fChars, fKeys.option_map[i]);
109 		print_key(fChars, fKeys.option_shift_map[i]);
110 		print_key(fChars, fKeys.caps_map[i]);
111 		print_key(fChars, fKeys.caps_shift_map[i]);
112 		print_key(fChars, fKeys.option_caps_map[i]);
113 		print_key(fChars, fKeys.option_caps_shift_map[i]);
114 		fputs("\n", stdout);
115 	}
116 }
117 
118 
119 //!	Load a map from a file
120 status_t
121 Keymap::Load(const entry_ref& ref)
122 {
123 	BEntry entry;
124 	status_t status = entry.SetTo(&ref, true);
125 	if (status != B_OK)
126 		return status;
127 
128 	BFile file(&entry, B_READ_ONLY);
129 	status = SetTo(file);
130 	if (status != B_OK)
131 		return status;
132 
133 	// fetch name from attribute and fall back to filename
134 
135 	ssize_t bytesRead = file.ReadAttr("keymap:name", B_STRING_TYPE, 0, fName,
136 		sizeof(fName));
137 	if (bytesRead > 0)
138 		fName[bytesRead] = '\0';
139 	else
140 		strlcpy(fName, ref.name, sizeof(fName));
141 
142 	return B_OK;
143 }
144 
145 
146 //!	We save a map to a file
147 status_t
148 Keymap::Save(const entry_ref& ref)
149 {
150 	BFile file;
151 	status_t status = file.SetTo(&ref,
152 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
153 	if (status != B_OK) {
154 		printf("error %s\n", strerror(status));
155 		return status;
156 	}
157 
158 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
159 		((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]);
160 
161 	ssize_t bytesWritten = file.Write(&fKeys, sizeof(fKeys));
162 	if (bytesWritten < (ssize_t)sizeof(fKeys))
163 		status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
164 
165 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
166 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
167 
168 	if (status == B_OK) {
169 		fCharsSize = B_HOST_TO_BENDIAN_INT32(fCharsSize);
170 
171 		bytesWritten = file.Write(&fCharsSize, sizeof(uint32));
172 		if (bytesWritten < (ssize_t)sizeof(uint32))
173 			status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
174 
175 		fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
176 	}
177 
178 	if (status == B_OK) {
179 		bytesWritten = file.Write(fChars, fCharsSize);
180 		if (bytesWritten < (ssize_t)fCharsSize)
181 			status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
182 	}
183 
184 	if (status == B_OK) {
185 		file.WriteAttr("keymap:name", B_STRING_TYPE, 0, fName, strlen(fName));
186 			// Failing would be non-fatal
187 	}
188 
189 	return status;
190 }
191 
192 
193 status_t
194 Keymap::SetModifier(uint32 keyCode, uint32 modifier)
195 {
196 	const uint32 kSingleModifierKeys = B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY
197 		| B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY | B_LEFT_CONTROL_KEY
198 		| B_RIGHT_CONTROL_KEY | B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY;
199 
200 	if ((modifier & kSingleModifierKeys) != 0)
201 		modifier &= kSingleModifierKeys;
202 	else if ((modifier & kModifierKeys) != 0)
203 		modifier &= kModifierKeys;
204 
205 	if (modifier == B_CAPS_LOCK)
206 		fKeys.caps_key = keyCode;
207 	else if (modifier == B_NUM_LOCK)
208 		fKeys.num_key = keyCode;
209 	else if (modifier == B_SCROLL_LOCK)
210 		fKeys.scroll_key = keyCode;
211 	else if (modifier == B_LEFT_SHIFT_KEY)
212 		fKeys.left_shift_key = keyCode;
213 	else if (modifier == B_RIGHT_SHIFT_KEY)
214 		fKeys.right_shift_key = keyCode;
215 	else if (modifier == B_LEFT_COMMAND_KEY)
216 		fKeys.left_command_key = keyCode;
217 	else if (modifier == B_RIGHT_COMMAND_KEY)
218 		fKeys.right_command_key = keyCode;
219 	else if (modifier == B_LEFT_CONTROL_KEY)
220 		fKeys.left_control_key = keyCode;
221 	else if (modifier == B_RIGHT_CONTROL_KEY)
222 		fKeys.right_control_key = keyCode;
223 	else if (modifier == B_LEFT_OPTION_KEY)
224 		fKeys.left_option_key = keyCode;
225 	else if (modifier == B_RIGHT_OPTION_KEY)
226 		fKeys.right_option_key = keyCode;
227 	else if (modifier == B_MENU_KEY)
228 		fKeys.menu_key = keyCode;
229 	else
230 		return B_BAD_VALUE;
231 
232 	if (fModificationMessage != NULL)
233 		fTarget.SendMessage(fModificationMessage);
234 
235 	return B_OK;
236 }
237 
238 
239 //! Enables/disables the "deadness" of the given keycode/modifier combo.
240 void
241 Keymap::SetDeadKeyEnabled(uint32 keyCode, uint32 modifiers, bool enabled)
242 {
243 	uint32 tableMask = 0;
244 	int32 offset = Offset(keyCode, modifiers, &tableMask);
245 	uint8 deadKeyIndex = DeadKeyIndex(offset);
246 	if (deadKeyIndex > 0) {
247 		uint32* deadTables[] = {
248 			&fKeys.acute_tables,
249 			&fKeys.grave_tables,
250 			&fKeys.circumflex_tables,
251 			&fKeys.dieresis_tables,
252 			&fKeys.tilde_tables
253 		};
254 
255 		if (enabled)
256 			(*deadTables[deadKeyIndex - 1]) |= tableMask;
257 		else
258 			(*deadTables[deadKeyIndex - 1]) &= ~tableMask;
259 
260 		if (fModificationMessage != NULL)
261 			fTarget.SendMessage(fModificationMessage);
262 	}
263 }
264 
265 
266 /*! Returns the trigger character string that is currently set for the dead
267 	key with the given index (which is 1..5).
268 */
269 void
270 Keymap::GetDeadKeyTrigger(dead_key_index deadKeyIndex, BString& outTrigger)
271 {
272 	outTrigger = "";
273 	if (deadKeyIndex < 1 || deadKeyIndex > 5)
274 		return;
275 
276 	int32 deadOffsets[] = {
277 		fKeys.acute_dead_key[1],
278 		fKeys.grave_dead_key[1],
279 		fKeys.circumflex_dead_key[1],
280 		fKeys.dieresis_dead_key[1],
281 		fKeys.tilde_dead_key[1]
282 	};
283 
284 	int32 offset = deadOffsets[deadKeyIndex - 1];
285 	if (offset < 0 || offset >= (int32)fCharsSize)
286 		return;
287 
288 	uint32 deadNumBytes = fChars[offset];
289 	if (!deadNumBytes)
290 		return;
291 
292 	outTrigger.SetTo(&fChars[offset + 1], deadNumBytes);
293 }
294 
295 
296 /*! Sets the trigger character string that shall be used for the dead key
297 	with the given index (which is 1..5).
298 */
299 void
300 Keymap::SetDeadKeyTrigger(dead_key_index deadKeyIndex, const BString& trigger)
301 {
302 	if (deadKeyIndex < 1 || deadKeyIndex > 5)
303 		return;
304 
305 	int32 deadOffsets[] = {
306 		fKeys.acute_dead_key[1],
307 		fKeys.grave_dead_key[1],
308 		fKeys.circumflex_dead_key[1],
309 		fKeys.dieresis_dead_key[1],
310 		fKeys.tilde_dead_key[1]
311 	};
312 
313 	int32 offset = deadOffsets[deadKeyIndex - 1];
314 	if (offset < 0 || offset >= (int32)fCharsSize)
315 		return;
316 
317 	if (_SetChars(offset, trigger.String(), trigger.Length())) {
318 		// reset modifier table such that new dead key is enabled wherever
319 		// it is available
320 		uint32* deadTables[] = {
321 			&fKeys.acute_tables,
322 			&fKeys.grave_tables,
323 			&fKeys.circumflex_tables,
324 			&fKeys.dieresis_tables,
325 			&fKeys.tilde_tables
326 		};
327 		*deadTables[deadKeyIndex - 1]
328 			= B_NORMAL_TABLE | B_SHIFT_TABLE | B_CONTROL_TABLE | B_OPTION_TABLE
329 				| B_OPTION_SHIFT_TABLE | B_CAPS_TABLE | B_CAPS_SHIFT_TABLE
330 				| B_OPTION_CAPS_TABLE | B_OPTION_CAPS_SHIFT_TABLE;
331 
332 		if (fModificationMessage != NULL)
333 			fTarget.SendMessage(fModificationMessage);
334 	}
335 }
336 
337 
338 //! We make our input server use the map in /boot/home/config/settings/Keymap
339 status_t
340 Keymap::Use()
341 {
342 	status_t result = _restore_key_map_();
343 	if (result == B_OK)
344 		set_keyboard_locks(modifiers());
345 	return result;
346 }
347 
348 
349 void
350 Keymap::SetKey(uint32 keyCode, uint32 modifiers, int8 deadKey,
351 	const char* bytes, int32 numBytes)
352 {
353 	int32 offset = Offset(keyCode, modifiers);
354 	if (offset < 0)
355 		return;
356 
357 	if (numBytes == -1)
358 		numBytes = strlen(bytes);
359 	if (numBytes > 6)
360 		return;
361 
362 	if (_SetChars(offset, bytes, numBytes)) {
363 		if (fModificationMessage != NULL)
364 			fTarget.SendMessage(fModificationMessage);
365 	}
366 }
367 
368 
369 Keymap&
370 Keymap::operator=(const Keymap& other)
371 {
372 	if (this == &other)
373 		return *this;
374 
375 	delete[] fChars;
376 	delete fModificationMessage;
377 
378 	fChars = new(std::nothrow) char[other.fCharsSize];
379 	if (fChars != NULL) {
380 		memcpy(fChars, other.fChars, other.fCharsSize);
381 		fCharsSize = other.fCharsSize;
382 	} else
383 		fCharsSize = 0;
384 
385 	memcpy(&fKeys, &other.fKeys, sizeof(key_map));
386 	strlcpy(fName, other.fName, sizeof(fName));
387 
388 	fTarget = other.fTarget;
389 
390 	if (other.fModificationMessage != NULL)
391 		fModificationMessage = new BMessage(*other.fModificationMessage);
392 
393 	return *this;
394 }
395 
396 
397 bool
398 Keymap::_SetChars(int32 offset, const char* bytes, int32 numBytes)
399 {
400 	int32 oldNumBytes = fChars[offset];
401 
402 	if (oldNumBytes == numBytes
403 		&& !memcmp(&fChars[offset + 1], bytes, numBytes)) {
404 		// nothing to do
405 		return false;
406 	}
407 
408 	int32 diff = numBytes - oldNumBytes;
409 	if (diff != 0) {
410 		fCharsSize += diff;
411 
412 		if (diff > 0) {
413 			// make space for the new data
414 			char* chars = new(std::nothrow) char[fCharsSize];
415 			if (chars != NULL) {
416 				memcpy(chars, fChars, offset + oldNumBytes + 1);
417 				memcpy(&chars[offset + 1 + numBytes],
418 					&fChars[offset + 1 + oldNumBytes],
419 					fCharsSize - 2 - offset - diff);
420 				delete[] fChars;
421 				fChars = chars;
422 			} else
423 				return false;
424 		} else if (diff < 0) {
425 			// shrink table
426 			memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes],
427 				fCharsSize - offset - 2 - diff);
428 		}
429 
430 		// update offsets
431 		int32* data = fKeys.control_map;
432 		int32 size = sizeof(fKeys.control_map) / 4 * 9
433 			+ sizeof(fKeys.acute_dead_key) / 4 * 5;
434 		for (int32 i = 0; i < size; i++) {
435 			if (data[i] > offset)
436 				data[i] += diff;
437 		}
438 	}
439 
440 	memcpy(&fChars[offset + 1], bytes, numBytes);
441 	fChars[offset] = numBytes;
442 
443 	return true;
444 }
445