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