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 26 static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY 27 | B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY; 28 29 30 static void 31 print_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 66 Keymap::Keymap() 67 : 68 fModificationMessage(NULL) 69 { 70 } 71 72 73 Keymap::~Keymap() 74 { 75 delete fModificationMessage; 76 } 77 78 79 void 80 Keymap::SetTarget(BMessenger target, BMessage* modificationMessage) 81 { 82 delete fModificationMessage; 83 84 fTarget = target; 85 fModificationMessage = modificationMessage; 86 } 87 88 89 void 90 Keymap::SetName(const char* name) 91 { 92 strlcpy(fName, name, sizeof(fName)); 93 } 94 95 96 void 97 Keymap::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 123 status_t 124 Keymap::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 150 status_t 151 Keymap::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 file.WriteAttr("keymap:name", B_STRING_TYPE, 0, fName, strlen(fName)); 189 // Failing would be non-fatal 190 } 191 192 return status; 193 } 194 195 196 status_t 197 Keymap::SetModifier(uint32 keyCode, uint32 modifier) 198 { 199 const uint32 kSingleModifierKeys = B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY 200 | B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY | B_LEFT_CONTROL_KEY 201 | B_RIGHT_CONTROL_KEY | B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY; 202 203 if ((modifier & kSingleModifierKeys) != 0) 204 modifier &= kSingleModifierKeys; 205 else if ((modifier & kModifierKeys) != 0) 206 modifier &= kModifierKeys; 207 208 if (modifier == B_CAPS_LOCK) 209 fKeys.caps_key = keyCode; 210 else if (modifier == B_NUM_LOCK) 211 fKeys.num_key = keyCode; 212 else if (modifier == B_SCROLL_LOCK) 213 fKeys.scroll_key = keyCode; 214 else if (modifier == B_LEFT_SHIFT_KEY) 215 fKeys.left_shift_key = keyCode; 216 else if (modifier == B_RIGHT_SHIFT_KEY) 217 fKeys.right_shift_key = keyCode; 218 else if (modifier == B_LEFT_COMMAND_KEY) 219 fKeys.left_command_key = keyCode; 220 else if (modifier == B_RIGHT_COMMAND_KEY) 221 fKeys.right_command_key = keyCode; 222 else if (modifier == B_LEFT_CONTROL_KEY) 223 fKeys.left_control_key = keyCode; 224 else if (modifier == B_RIGHT_CONTROL_KEY) 225 fKeys.right_control_key = keyCode; 226 else if (modifier == B_LEFT_OPTION_KEY) 227 fKeys.left_option_key = keyCode; 228 else if (modifier == B_RIGHT_OPTION_KEY) 229 fKeys.right_option_key = keyCode; 230 else if (modifier == B_MENU_KEY) 231 fKeys.menu_key = keyCode; 232 else 233 return B_BAD_VALUE; 234 235 if (fModificationMessage != NULL) 236 fTarget.SendMessage(fModificationMessage); 237 238 return B_OK; 239 } 240 241 242 //! Enables/disables the "deadness" of the given keycode/modifier combo. 243 void 244 Keymap::SetDeadKeyEnabled(uint32 keyCode, uint32 modifiers, bool enabled) 245 { 246 uint32 tableMask = 0; 247 int32 offset = Offset(keyCode, modifiers, &tableMask); 248 uint8 deadKeyIndex = DeadKeyIndex(offset); 249 if (deadKeyIndex > 0) { 250 uint32* deadTables[] = { 251 &fKeys.acute_tables, 252 &fKeys.grave_tables, 253 &fKeys.circumflex_tables, 254 &fKeys.dieresis_tables, 255 &fKeys.tilde_tables 256 }; 257 258 if (enabled) 259 (*deadTables[deadKeyIndex - 1]) |= tableMask; 260 else 261 (*deadTables[deadKeyIndex - 1]) &= ~tableMask; 262 263 if (fModificationMessage != NULL) 264 fTarget.SendMessage(fModificationMessage); 265 } 266 } 267 268 269 /*! Returns the trigger character string that is currently set for the dead 270 key with the given index (which is 1..5). 271 */ 272 void 273 Keymap::GetDeadKeyTrigger(dead_key_index deadKeyIndex, BString& outTrigger) 274 { 275 outTrigger = ""; 276 if (deadKeyIndex < 1 || deadKeyIndex > 5) 277 return; 278 279 int32 deadOffsets[] = { 280 fKeys.acute_dead_key[1], 281 fKeys.grave_dead_key[1], 282 fKeys.circumflex_dead_key[1], 283 fKeys.dieresis_dead_key[1], 284 fKeys.tilde_dead_key[1] 285 }; 286 287 int32 offset = deadOffsets[deadKeyIndex - 1]; 288 if (offset < 0 || offset >= (int32)fCharsSize) 289 return; 290 291 uint32 deadNumBytes = fChars[offset]; 292 if (!deadNumBytes) 293 return; 294 295 outTrigger.SetTo(&fChars[offset + 1], deadNumBytes); 296 } 297 298 299 /*! Sets the trigger character string that shall be used for the dead key 300 with the given index (which is 1..5). 301 */ 302 void 303 Keymap::SetDeadKeyTrigger(dead_key_index deadKeyIndex, const BString& trigger) 304 { 305 if (deadKeyIndex < 1 || deadKeyIndex > 5) 306 return; 307 308 int32 deadOffsets[] = { 309 fKeys.acute_dead_key[1], 310 fKeys.grave_dead_key[1], 311 fKeys.circumflex_dead_key[1], 312 fKeys.dieresis_dead_key[1], 313 fKeys.tilde_dead_key[1] 314 }; 315 316 int32 offset = deadOffsets[deadKeyIndex - 1]; 317 if (offset < 0 || offset >= (int32)fCharsSize) 318 return; 319 320 if (_SetChars(offset, trigger.String(), trigger.Length())) { 321 // reset modifier table such that new dead key is enabled wherever 322 // it is available 323 uint32* deadTables[] = { 324 &fKeys.acute_tables, 325 &fKeys.grave_tables, 326 &fKeys.circumflex_tables, 327 &fKeys.dieresis_tables, 328 &fKeys.tilde_tables 329 }; 330 *deadTables[deadKeyIndex - 1] 331 = B_NORMAL_TABLE | B_SHIFT_TABLE | B_CONTROL_TABLE | B_OPTION_TABLE 332 | B_OPTION_SHIFT_TABLE | B_CAPS_TABLE | B_CAPS_SHIFT_TABLE 333 | B_OPTION_CAPS_TABLE | B_OPTION_CAPS_SHIFT_TABLE; 334 335 if (fModificationMessage != NULL) 336 fTarget.SendMessage(fModificationMessage); 337 } 338 } 339 340 341 status_t 342 Keymap::RestoreSystemDefault() 343 { 344 BPath path; 345 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 346 if (status != B_OK) 347 return status; 348 349 path.Append("Key_map"); 350 351 BEntry entry(path.Path()); 352 entry.Remove(); 353 354 return Use(); 355 } 356 357 358 //! We make our input server use the map in /boot/home/config/settings/Keymap 359 status_t 360 Keymap::Use() 361 { 362 status_t result = _restore_key_map_(); 363 if (result == B_OK) 364 set_keyboard_locks(modifiers()); 365 return result; 366 } 367 368 369 void 370 Keymap::SetKey(uint32 keyCode, uint32 modifiers, int8 deadKey, 371 const char* bytes, int32 numBytes) 372 { 373 int32 offset = Offset(keyCode, modifiers); 374 if (offset < 0) 375 return; 376 377 if (numBytes == -1) 378 numBytes = strlen(bytes); 379 if (numBytes > 6) 380 return; 381 382 if (_SetChars(offset, bytes, numBytes)) { 383 if (fModificationMessage != NULL) 384 fTarget.SendMessage(fModificationMessage); 385 } 386 } 387 388 389 Keymap& 390 Keymap::operator=(const Keymap& other) 391 { 392 if (this == &other) 393 return *this; 394 395 delete[] fChars; 396 delete fModificationMessage; 397 398 fChars = new(std::nothrow) char[other.fCharsSize]; 399 if (fChars != NULL) { 400 memcpy(fChars, other.fChars, other.fCharsSize); 401 fCharsSize = other.fCharsSize; 402 } else 403 fCharsSize = 0; 404 405 memcpy(&fKeys, &other.fKeys, sizeof(key_map)); 406 strlcpy(fName, other.fName, sizeof(fName)); 407 408 fTarget = other.fTarget; 409 410 if (other.fModificationMessage != NULL) 411 fModificationMessage = new BMessage(*other.fModificationMessage); 412 413 return *this; 414 } 415 416 417 bool 418 Keymap::_SetChars(int32 offset, const char* bytes, int32 numBytes) 419 { 420 int32 oldNumBytes = fChars[offset]; 421 422 if (oldNumBytes == numBytes 423 && !memcmp(&fChars[offset + 1], bytes, numBytes)) { 424 // nothing to do 425 return false; 426 } 427 428 int32 diff = numBytes - oldNumBytes; 429 if (diff != 0) { 430 fCharsSize += diff; 431 432 if (diff > 0) { 433 // make space for the new data 434 char* chars = new(std::nothrow) char[fCharsSize]; 435 if (chars != NULL) { 436 memcpy(chars, fChars, offset + oldNumBytes + 1); 437 memcpy(&chars[offset + 1 + numBytes], 438 &fChars[offset + 1 + oldNumBytes], 439 fCharsSize - 2 - offset - diff); 440 delete[] fChars; 441 fChars = chars; 442 } else 443 return false; 444 } else if (diff < 0) { 445 // shrink table 446 memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes], 447 fCharsSize - offset - 2 - diff); 448 } 449 450 // update offsets 451 int32* data = fKeys.control_map; 452 int32 size = sizeof(fKeys.control_map) / 4 * 9 453 + sizeof(fKeys.acute_dead_key) / 4 * 5; 454 for (int32 i = 0; i < size; i++) { 455 if (data[i] > offset) 456 data[i] += diff; 457 } 458 } 459 460 memcpy(&fChars[offset + 1], bytes, numBytes); 461 fChars[offset] = numBytes; 462 463 return true; 464 } 465