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