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