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