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