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 const BString name(fName); 189 file.WriteAttrString("keymap:name", &name); 190 // Failing would be non-fatal 191 } 192 193 return status; 194 } 195 196 197 status_t 198 Keymap::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. 244 void 245 Keymap::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 */ 273 void 274 Keymap::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 */ 303 void 304 Keymap::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 342 status_t 343 Keymap::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 360 status_t 361 Keymap::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 370 void 371 Keymap::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 390 Keymap& 391 Keymap::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 418 bool 419 Keymap::_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