keyboard.rs

   1use collections::HashMap;
   2use std::ffi::{CStr, c_void};
   3
   4use objc::{msg_send, runtime::Object, sel, sel_impl};
   5
   6use crate::{KeybindingKeystroke, Keystroke, PlatformKeyboardLayout, PlatformKeyboardMapper};
   7
   8use super::{
   9    TISCopyCurrentKeyboardLayoutInputSource, TISGetInputSourceProperty, kTISPropertyInputSourceID,
  10    kTISPropertyLocalizedName,
  11};
  12
  13pub(crate) struct MacKeyboardLayout {
  14    id: String,
  15    name: String,
  16}
  17
  18pub(crate) struct MacKeyboardMapper {
  19    key_equivalents: Option<HashMap<char, char>>,
  20}
  21
  22impl PlatformKeyboardLayout for MacKeyboardLayout {
  23    fn id(&self) -> &str {
  24        &self.id
  25    }
  26
  27    fn name(&self) -> &str {
  28        &self.name
  29    }
  30}
  31
  32impl PlatformKeyboardMapper for MacKeyboardMapper {
  33    fn map_key_equivalent(
  34        &self,
  35        mut keystroke: Keystroke,
  36        use_key_equivalents: bool,
  37    ) -> KeybindingKeystroke {
  38        if use_key_equivalents && let Some(key_equivalents) = &self.key_equivalents {
  39            if keystroke.key.chars().count() == 1
  40                && let Some(key) = key_equivalents.get(&keystroke.key.chars().next().unwrap())
  41            {
  42                keystroke.key = key.to_string();
  43            }
  44        }
  45        KeybindingKeystroke::from_keystroke(keystroke)
  46    }
  47
  48    fn get_key_equivalents(&self) -> Option<&HashMap<char, char>> {
  49        self.key_equivalents.as_ref()
  50    }
  51}
  52
  53impl MacKeyboardLayout {
  54    pub(crate) fn new() -> Self {
  55        unsafe {
  56            let current_keyboard = TISCopyCurrentKeyboardLayoutInputSource();
  57
  58            let id: *mut Object = TISGetInputSourceProperty(
  59                current_keyboard,
  60                kTISPropertyInputSourceID as *const c_void,
  61            );
  62            let id: *const std::os::raw::c_char = msg_send![id, UTF8String];
  63            let id = CStr::from_ptr(id).to_str().unwrap().to_string();
  64
  65            let name: *mut Object = TISGetInputSourceProperty(
  66                current_keyboard,
  67                kTISPropertyLocalizedName as *const c_void,
  68            );
  69            let name: *const std::os::raw::c_char = msg_send![name, UTF8String];
  70            let name = CStr::from_ptr(name).to_str().unwrap().to_string();
  71
  72            Self { id, name }
  73        }
  74    }
  75}
  76
  77impl MacKeyboardMapper {
  78    pub(crate) fn new(layout_id: &str) -> Self {
  79        let key_equivalents = get_key_equivalents(layout_id);
  80
  81        Self { key_equivalents }
  82    }
  83}
  84
  85// On some keyboards (e.g. German QWERTZ) it is not possible to type the full ASCII range
  86// without using option. This means that some of our built in keyboard shortcuts do not work
  87// for those users.
  88//
  89// The way macOS solves this problem is to move shortcuts around so that they are all reachable,
  90// even if the mnemonic changes. https://developer.apple.com/documentation/swiftui/keyboardshortcut/localization-swift.struct
  91//
  92// For example, cmd-> is the "switch window" shortcut because the > key is right above tab.
  93// To ensure this doesn't cause problems for shortcuts defined for a QWERTY layout, apple moves
  94// any shortcuts defined as cmd-> to cmd-:. Coincidentally this s also the same keyboard position
  95// as cmd-> on a QWERTY layout.
  96//
  97// Another example is cmd-[ and cmd-], as they cannot be typed without option, those keys are remapped to cmd-ö
  98// and cmd-ä. These shortcuts are not in the same position as a QWERTY keyboard, because on a QWERTZ keyboard
  99// the + key is in the way; and shortcuts bound to cmd-+ are still typed as cmd-+ on either keyboard (though the
 100// specific key moves)
 101//
 102// As far as I can tell, there's no way to query the mappings Apple uses except by rendering a menu with every
 103// possible key combination, and inspecting the UI to see what it rendered. So that's what we did...
 104//
 105// These mappings were generated by running https://github.com/ConradIrwin/keyboard-inspector, tidying up the
 106// output to remove languages with no mappings and other oddities, and converting it to a less verbose representation with:
 107//  jq -s 'map(to_entries | map({key: .key, value: [(.value | to_entries | map(.key) | join("")), (.value | to_entries | map(.value) | join(""))]}) | from_entries) | add'
 108// From there I used multi-cursor to produce this match statement.
 109fn get_key_equivalents(layout_id: &str) -> Option<HashMap<char, char>> {
 110    let mappings: &[(char, char)] = match layout_id {
 111        "com.apple.keylayout.ABC-AZERTY" => &[
 112            ('!', '1'),
 113            ('"', '%'),
 114            ('#', '3'),
 115            ('$', '4'),
 116            ('%', '5'),
 117            ('&', '7'),
 118            ('(', '9'),
 119            (')', '0'),
 120            ('*', '8'),
 121            ('.', ';'),
 122            ('/', ':'),
 123            ('0', 'à'),
 124            ('1', '&'),
 125            ('2', 'é'),
 126            ('3', '"'),
 127            ('4', '\''),
 128            ('5', '('),
 129            ('6', '§'),
 130            ('7', 'è'),
 131            ('8', '!'),
 132            ('9', 'ç'),
 133            (':', '°'),
 134            (';', ')'),
 135            ('<', '.'),
 136            ('>', '/'),
 137            ('@', '2'),
 138            ('[', '^'),
 139            ('\'', 'ù'),
 140            ('\\', '`'),
 141            (']', '$'),
 142            ('^', '6'),
 143            ('`', '<'),
 144            ('{', '¨'),
 145            ('|', '£'),
 146            ('}', '*'),
 147            ('~', '>'),
 148        ],
 149        "com.apple.keylayout.ABC-QWERTZ" => &[
 150            ('"', '`'),
 151            ('#', '§'),
 152            ('&', '/'),
 153            ('(', ')'),
 154            (')', '='),
 155            ('*', '('),
 156            ('/', 'ß'),
 157            (':', 'Ü'),
 158            (';', 'ü'),
 159            ('<', ';'),
 160            ('=', '*'),
 161            ('>', ':'),
 162            ('@', '"'),
 163            ('[', 'ö'),
 164            ('\'', '´'),
 165            ('\\', '#'),
 166            (']', 'ä'),
 167            ('^', '&'),
 168            ('`', '<'),
 169            ('{', 'Ö'),
 170            ('|', '\''),
 171            ('}', 'Ä'),
 172            ('~', '>'),
 173        ],
 174        "com.apple.keylayout.Albanian" => &[
 175            ('"', '\''),
 176            (':', 'Ç'),
 177            (';', 'ç'),
 178            ('<', ';'),
 179            ('>', ':'),
 180            ('@', '"'),
 181            ('\'', '@'),
 182            ('\\', 'ë'),
 183            ('`', '<'),
 184            ('|', 'Ë'),
 185            ('~', '>'),
 186        ],
 187        "com.apple.keylayout.Austrian" => &[
 188            ('"', '`'),
 189            ('#', '§'),
 190            ('&', '/'),
 191            ('(', ')'),
 192            (')', '='),
 193            ('*', '('),
 194            ('/', 'ß'),
 195            (':', 'Ü'),
 196            (';', 'ü'),
 197            ('<', ';'),
 198            ('=', '*'),
 199            ('>', ':'),
 200            ('@', '"'),
 201            ('[', 'ö'),
 202            ('\'', '´'),
 203            ('\\', '#'),
 204            (']', 'ä'),
 205            ('^', '&'),
 206            ('`', '<'),
 207            ('{', 'Ö'),
 208            ('|', '\''),
 209            ('}', 'Ä'),
 210            ('~', '>'),
 211        ],
 212        "com.apple.keylayout.Azeri" => &[
 213            ('"', 'Ə'),
 214            (',', 'ç'),
 215            ('.', 'ş'),
 216            ('/', '.'),
 217            (':', 'I'),
 218            (';', 'ı'),
 219            ('<', 'Ç'),
 220            ('>', 'Ş'),
 221            ('?', ','),
 222            ('W', 'Ü'),
 223            ('[', 'ö'),
 224            ('\'', 'ə'),
 225            (']', 'ğ'),
 226            ('w', 'ü'),
 227            ('{', 'Ö'),
 228            ('|', '/'),
 229            ('}', 'Ğ'),
 230        ],
 231        "com.apple.keylayout.Belgian" => &[
 232            ('!', '1'),
 233            ('"', '%'),
 234            ('#', '3'),
 235            ('$', '4'),
 236            ('%', '5'),
 237            ('&', '7'),
 238            ('(', '9'),
 239            (')', '0'),
 240            ('*', '8'),
 241            ('.', ';'),
 242            ('/', ':'),
 243            ('0', 'à'),
 244            ('1', '&'),
 245            ('2', 'é'),
 246            ('3', '"'),
 247            ('4', '\''),
 248            ('5', '('),
 249            ('6', '§'),
 250            ('7', 'è'),
 251            ('8', '!'),
 252            ('9', 'ç'),
 253            (':', '°'),
 254            (';', ')'),
 255            ('<', '.'),
 256            ('>', '/'),
 257            ('@', '2'),
 258            ('[', '^'),
 259            ('\'', 'ù'),
 260            ('\\', '`'),
 261            (']', '$'),
 262            ('^', '6'),
 263            ('`', '<'),
 264            ('{', '¨'),
 265            ('|', '£'),
 266            ('}', '*'),
 267            ('~', '>'),
 268        ],
 269        "com.apple.keylayout.Brazilian-ABNT2" => &[
 270            ('"', '`'),
 271            ('/', 'ç'),
 272            ('?', 'Ç'),
 273            ('\'', '´'),
 274            ('\\', '~'),
 275            ('^', '¨'),
 276            ('`', '\''),
 277            ('|', '^'),
 278            ('~', '"'),
 279        ],
 280        "com.apple.keylayout.Brazilian-Pro" => &[('^', 'ˆ'), ('~', '˜')],
 281        "com.apple.keylayout.British" => &[('#', '£')],
 282        "com.apple.keylayout.Canadian-CSA" => &[
 283            ('"', 'È'),
 284            ('/', 'é'),
 285            ('<', '\''),
 286            ('>', '"'),
 287            ('?', 'É'),
 288            ('[', '^'),
 289            ('\'', 'è'),
 290            ('\\', 'à'),
 291            (']', 'ç'),
 292            ('`', 'ù'),
 293            ('{', '¨'),
 294            ('|', 'À'),
 295            ('}', 'Ç'),
 296            ('~', 'Ù'),
 297        ],
 298        "com.apple.keylayout.Croatian" => &[
 299            ('"', 'Ć'),
 300            ('&', '\''),
 301            ('(', ')'),
 302            (')', '='),
 303            ('*', '('),
 304            (':', 'Č'),
 305            (';', 'č'),
 306            ('<', ';'),
 307            ('=', '*'),
 308            ('>', ':'),
 309            ('@', '"'),
 310            ('[', 'š'),
 311            ('\'', 'ć'),
 312            ('\\', 'ž'),
 313            (']', 'đ'),
 314            ('^', '&'),
 315            ('`', '<'),
 316            ('{', 'Š'),
 317            ('|', 'Ž'),
 318            ('}', 'Đ'),
 319            ('~', '>'),
 320        ],
 321        "com.apple.keylayout.Croatian-PC" => &[
 322            ('"', 'Ć'),
 323            ('&', '/'),
 324            ('(', ')'),
 325            (')', '='),
 326            ('*', '('),
 327            ('/', '\''),
 328            (':', 'Č'),
 329            (';', 'č'),
 330            ('<', ';'),
 331            ('=', '*'),
 332            ('>', ':'),
 333            ('@', '"'),
 334            ('[', 'š'),
 335            ('\'', 'ć'),
 336            ('\\', 'ž'),
 337            (']', 'đ'),
 338            ('^', '&'),
 339            ('`', '<'),
 340            ('{', 'Š'),
 341            ('|', 'Ž'),
 342            ('}', 'Đ'),
 343            ('~', '>'),
 344        ],
 345        "com.apple.keylayout.Czech" => &[
 346            ('!', '1'),
 347            ('"', '!'),
 348            ('#', '3'),
 349            ('$', '4'),
 350            ('%', '5'),
 351            ('&', '7'),
 352            ('(', '9'),
 353            (')', '0'),
 354            ('*', '8'),
 355            ('+', '%'),
 356            ('/', '\''),
 357            ('0', 'é'),
 358            ('1', '+'),
 359            ('2', 'ě'),
 360            ('3', 'š'),
 361            ('4', 'č'),
 362            ('5', 'ř'),
 363            ('6', 'ž'),
 364            ('7', 'ý'),
 365            ('8', 'á'),
 366            ('9', 'í'),
 367            (':', '"'),
 368            (';', 'ů'),
 369            ('<', '?'),
 370            ('>', ':'),
 371            ('?', 'ˇ'),
 372            ('@', '2'),
 373            ('[', 'ú'),
 374            ('\'', '§'),
 375            (']', ')'),
 376            ('^', '6'),
 377            ('`', '¨'),
 378            ('{', 'Ú'),
 379            ('}', '('),
 380            ('~', '`'),
 381        ],
 382        "com.apple.keylayout.Czech-QWERTY" => &[
 383            ('!', '1'),
 384            ('"', '!'),
 385            ('#', '3'),
 386            ('$', '4'),
 387            ('%', '5'),
 388            ('&', '7'),
 389            ('(', '9'),
 390            (')', '0'),
 391            ('*', '8'),
 392            ('+', '%'),
 393            ('/', '\''),
 394            ('0', 'é'),
 395            ('1', '+'),
 396            ('2', 'ě'),
 397            ('3', 'š'),
 398            ('4', 'č'),
 399            ('5', 'ř'),
 400            ('6', 'ž'),
 401            ('7', 'ý'),
 402            ('8', 'á'),
 403            ('9', 'í'),
 404            (':', '"'),
 405            (';', 'ů'),
 406            ('<', '?'),
 407            ('>', ':'),
 408            ('?', 'ˇ'),
 409            ('@', '2'),
 410            ('[', 'ú'),
 411            ('\'', '§'),
 412            (']', ')'),
 413            ('^', '6'),
 414            ('`', '¨'),
 415            ('{', 'Ú'),
 416            ('}', '('),
 417            ('~', '`'),
 418        ],
 419        "com.apple.keylayout.Danish" => &[
 420            ('"', '^'),
 421            ('$', '€'),
 422            ('&', '/'),
 423            ('(', ')'),
 424            (')', '='),
 425            ('*', '('),
 426            ('/', '´'),
 427            (':', 'Å'),
 428            (';', 'å'),
 429            ('<', ';'),
 430            ('=', '`'),
 431            ('>', ':'),
 432            ('@', '"'),
 433            ('[', 'æ'),
 434            ('\'', '¨'),
 435            ('\\', '\''),
 436            (']', 'ø'),
 437            ('^', '&'),
 438            ('`', '<'),
 439            ('{', 'Æ'),
 440            ('|', '*'),
 441            ('}', 'Ø'),
 442            ('~', '>'),
 443        ],
 444        "com.apple.keylayout.Faroese" => &[
 445            ('"', 'Ø'),
 446            ('$', '€'),
 447            ('&', '/'),
 448            ('(', ')'),
 449            (')', '='),
 450            ('*', '('),
 451            ('/', '´'),
 452            (':', 'Æ'),
 453            (';', 'æ'),
 454            ('<', ';'),
 455            ('=', '`'),
 456            ('>', ':'),
 457            ('@', '"'),
 458            ('[', 'å'),
 459            ('\'', 'ø'),
 460            ('\\', '\''),
 461            (']', 'ð'),
 462            ('^', '&'),
 463            ('`', '<'),
 464            ('{', 'Å'),
 465            ('|', '*'),
 466            ('}', 'Ð'),
 467            ('~', '>'),
 468        ],
 469        "com.apple.keylayout.Finnish" => &[
 470            ('"', '^'),
 471            ('$', '€'),
 472            ('&', '/'),
 473            ('(', ')'),
 474            (')', '='),
 475            ('*', '('),
 476            ('/', '´'),
 477            (':', 'Å'),
 478            (';', 'å'),
 479            ('<', ';'),
 480            ('=', '`'),
 481            ('>', ':'),
 482            ('@', '"'),
 483            ('[', 'ö'),
 484            ('\'', '¨'),
 485            ('\\', '\''),
 486            (']', 'ä'),
 487            ('^', '&'),
 488            ('`', '<'),
 489            ('{', 'Ö'),
 490            ('|', '*'),
 491            ('}', 'Ä'),
 492            ('~', '>'),
 493        ],
 494        "com.apple.keylayout.FinnishExtended" => &[
 495            ('"', 'ˆ'),
 496            ('$', '€'),
 497            ('&', '/'),
 498            ('(', ')'),
 499            (')', '='),
 500            ('*', '('),
 501            ('/', '´'),
 502            (':', 'Å'),
 503            (';', 'å'),
 504            ('<', ';'),
 505            ('=', '`'),
 506            ('>', ':'),
 507            ('@', '"'),
 508            ('[', 'ö'),
 509            ('\'', '¨'),
 510            ('\\', '\''),
 511            (']', 'ä'),
 512            ('^', '&'),
 513            ('`', '<'),
 514            ('{', 'Ö'),
 515            ('|', '*'),
 516            ('}', 'Ä'),
 517            ('~', '>'),
 518        ],
 519        "com.apple.keylayout.FinnishSami-PC" => &[
 520            ('"', 'ˆ'),
 521            ('&', '/'),
 522            ('(', ')'),
 523            (')', '='),
 524            ('*', '('),
 525            ('/', '´'),
 526            (':', 'Å'),
 527            (';', 'å'),
 528            ('<', ';'),
 529            ('=', '`'),
 530            ('>', ':'),
 531            ('@', '"'),
 532            ('[', 'ö'),
 533            ('\'', '¨'),
 534            ('\\', '@'),
 535            (']', 'ä'),
 536            ('^', '&'),
 537            ('`', '<'),
 538            ('{', 'Ö'),
 539            ('|', '*'),
 540            ('}', 'Ä'),
 541            ('~', '>'),
 542        ],
 543        "com.apple.keylayout.French" => &[
 544            ('!', '1'),
 545            ('"', '%'),
 546            ('#', '3'),
 547            ('$', '4'),
 548            ('%', '5'),
 549            ('&', '7'),
 550            ('(', '9'),
 551            (')', '0'),
 552            ('*', '8'),
 553            ('.', ';'),
 554            ('/', ':'),
 555            ('0', 'à'),
 556            ('1', '&'),
 557            ('2', 'é'),
 558            ('3', '"'),
 559            ('4', '\''),
 560            ('5', '('),
 561            ('6', '§'),
 562            ('7', 'è'),
 563            ('8', '!'),
 564            ('9', 'ç'),
 565            (':', '°'),
 566            (';', ')'),
 567            ('<', '.'),
 568            ('>', '/'),
 569            ('@', '2'),
 570            ('[', '^'),
 571            ('\'', 'ù'),
 572            ('\\', '`'),
 573            (']', '$'),
 574            ('^', '6'),
 575            ('`', '<'),
 576            ('{', '¨'),
 577            ('|', '£'),
 578            ('}', '*'),
 579            ('~', '>'),
 580        ],
 581        "com.apple.keylayout.French-PC" => &[
 582            ('!', '1'),
 583            ('"', '%'),
 584            ('#', '3'),
 585            ('$', '4'),
 586            ('%', '5'),
 587            ('&', '7'),
 588            ('(', '9'),
 589            (')', '0'),
 590            ('*', '8'),
 591            ('-', ')'),
 592            ('.', ';'),
 593            ('/', ':'),
 594            ('0', 'à'),
 595            ('1', '&'),
 596            ('2', 'é'),
 597            ('3', '"'),
 598            ('4', '\''),
 599            ('5', '('),
 600            ('6', '-'),
 601            ('7', 'è'),
 602            ('8', '_'),
 603            ('9', 'ç'),
 604            (':', '§'),
 605            (';', '!'),
 606            ('<', '.'),
 607            ('>', '/'),
 608            ('@', '2'),
 609            ('[', '^'),
 610            ('\'', 'ù'),
 611            ('\\', '*'),
 612            (']', '$'),
 613            ('^', '6'),
 614            ('_', '°'),
 615            ('`', '<'),
 616            ('{', '¨'),
 617            ('|', 'μ'),
 618            ('}', '£'),
 619            ('~', '>'),
 620        ],
 621        "com.apple.keylayout.French-numerical" => &[
 622            ('!', '1'),
 623            ('"', '%'),
 624            ('#', '3'),
 625            ('$', '4'),
 626            ('%', '5'),
 627            ('&', '7'),
 628            ('(', '9'),
 629            (')', '0'),
 630            ('*', '8'),
 631            ('.', ';'),
 632            ('/', ':'),
 633            ('0', 'à'),
 634            ('1', '&'),
 635            ('2', 'é'),
 636            ('3', '"'),
 637            ('4', '\''),
 638            ('5', '('),
 639            ('6', '§'),
 640            ('7', 'è'),
 641            ('8', '!'),
 642            ('9', 'ç'),
 643            (':', '°'),
 644            (';', ')'),
 645            ('<', '.'),
 646            ('>', '/'),
 647            ('@', '2'),
 648            ('[', '^'),
 649            ('\'', 'ù'),
 650            ('\\', '`'),
 651            (']', '$'),
 652            ('^', '6'),
 653            ('`', '<'),
 654            ('{', '¨'),
 655            ('|', '£'),
 656            ('}', '*'),
 657            ('~', '>'),
 658        ],
 659        "com.apple.keylayout.German" => &[
 660            ('"', '`'),
 661            ('#', '§'),
 662            ('&', '/'),
 663            ('(', ')'),
 664            (')', '='),
 665            ('*', '('),
 666            ('/', 'ß'),
 667            (':', 'Ü'),
 668            (';', 'ü'),
 669            ('<', ';'),
 670            ('=', '*'),
 671            ('>', ':'),
 672            ('@', '"'),
 673            ('[', 'ö'),
 674            ('\'', '´'),
 675            ('\\', '#'),
 676            (']', 'ä'),
 677            ('^', '&'),
 678            ('`', '<'),
 679            ('{', 'Ö'),
 680            ('|', '\''),
 681            ('}', 'Ä'),
 682            ('~', '>'),
 683        ],
 684        "com.apple.keylayout.German-DIN-2137" => &[
 685            ('"', '`'),
 686            ('#', '§'),
 687            ('&', '/'),
 688            ('(', ')'),
 689            (')', '='),
 690            ('*', '('),
 691            ('/', 'ß'),
 692            (':', 'Ü'),
 693            (';', 'ü'),
 694            ('<', ';'),
 695            ('=', '*'),
 696            ('>', ':'),
 697            ('@', '"'),
 698            ('[', 'ö'),
 699            ('\'', '´'),
 700            ('\\', '#'),
 701            (']', 'ä'),
 702            ('^', '&'),
 703            ('`', '<'),
 704            ('{', 'Ö'),
 705            ('|', '\''),
 706            ('}', 'Ä'),
 707            ('~', '>'),
 708        ],
 709        "com.apple.keylayout.Hawaiian" => &[('\'', 'ʻ')],
 710        "com.apple.keylayout.Hungarian" => &[
 711            ('!', '\''),
 712            ('"', 'Á'),
 713            ('#', '+'),
 714            ('$', '!'),
 715            ('&', '='),
 716            ('(', ')'),
 717            (')', 'Ö'),
 718            ('*', '('),
 719            ('+', 'Ó'),
 720            ('/', 'ü'),
 721            ('0', 'ö'),
 722            (':', 'É'),
 723            (';', 'é'),
 724            ('<', 'Ü'),
 725            ('=', 'ó'),
 726            ('>', ':'),
 727            ('@', '"'),
 728            ('[', 'ő'),
 729            ('\'', 'á'),
 730            ('\\', 'ű'),
 731            (']', 'ú'),
 732            ('^', '/'),
 733            ('`', 'í'),
 734            ('{', 'Ő'),
 735            ('|', 'Ű'),
 736            ('}', 'Ú'),
 737            ('~', 'Í'),
 738        ],
 739        "com.apple.keylayout.Hungarian-QWERTY" => &[
 740            ('!', '\''),
 741            ('"', 'Á'),
 742            ('#', '+'),
 743            ('$', '!'),
 744            ('&', '='),
 745            ('(', ')'),
 746            (')', 'Ö'),
 747            ('*', '('),
 748            ('+', 'Ó'),
 749            ('/', 'ü'),
 750            ('0', 'ö'),
 751            (':', 'É'),
 752            (';', 'é'),
 753            ('<', 'Ü'),
 754            ('=', 'ó'),
 755            ('>', ':'),
 756            ('@', '"'),
 757            ('[', 'ő'),
 758            ('\'', 'á'),
 759            ('\\', 'ű'),
 760            (']', 'ú'),
 761            ('^', '/'),
 762            ('`', 'í'),
 763            ('{', 'Ő'),
 764            ('|', 'Ű'),
 765            ('}', 'Ú'),
 766            ('~', 'Í'),
 767        ],
 768        "com.apple.keylayout.Icelandic" => &[
 769            ('"', 'Ö'),
 770            ('&', '/'),
 771            ('(', ')'),
 772            (')', '='),
 773            ('*', '('),
 774            ('/', '\''),
 775            (':', 'Ð'),
 776            (';', 'ð'),
 777            ('<', ';'),
 778            ('=', '*'),
 779            ('>', ':'),
 780            ('@', '"'),
 781            ('[', 'æ'),
 782            ('\'', 'ö'),
 783            ('\\', 'þ'),
 784            (']', '´'),
 785            ('^', '&'),
 786            ('`', '<'),
 787            ('{', 'Æ'),
 788            ('|', 'Þ'),
 789            ('}', '´'),
 790            ('~', '>'),
 791        ],
 792        "com.apple.keylayout.Irish" => &[('#', '£')],
 793        "com.apple.keylayout.IrishExtended" => &[('#', '£')],
 794        "com.apple.keylayout.Italian" => &[
 795            ('!', '1'),
 796            ('"', '%'),
 797            ('#', '3'),
 798            ('$', '4'),
 799            ('%', '5'),
 800            ('&', '7'),
 801            ('(', '9'),
 802            (')', '0'),
 803            ('*', '8'),
 804            (',', ';'),
 805            ('.', ':'),
 806            ('/', ','),
 807            ('0', 'é'),
 808            ('1', '&'),
 809            ('2', '"'),
 810            ('3', '\''),
 811            ('4', '('),
 812            ('5', 'ç'),
 813            ('6', 'è'),
 814            ('7', ')'),
 815            ('8', '£'),
 816            ('9', 'à'),
 817            (':', '!'),
 818            (';', 'ò'),
 819            ('<', '.'),
 820            ('>', '/'),
 821            ('@', '2'),
 822            ('[', 'ì'),
 823            ('\'', 'ù'),
 824            ('\\', '§'),
 825            (']', '$'),
 826            ('^', '6'),
 827            ('`', '<'),
 828            ('{', '^'),
 829            ('|', '°'),
 830            ('}', '*'),
 831            ('~', '>'),
 832        ],
 833        "com.apple.keylayout.Italian-Pro" => &[
 834            ('"', '^'),
 835            ('#', '£'),
 836            ('&', '/'),
 837            ('(', ')'),
 838            (')', '='),
 839            ('*', '('),
 840            ('/', '\''),
 841            (':', 'é'),
 842            (';', 'è'),
 843            ('<', ';'),
 844            ('=', '*'),
 845            ('>', ':'),
 846            ('@', '"'),
 847            ('[', 'ò'),
 848            ('\'', 'ì'),
 849            ('\\', 'ù'),
 850            (']', 'à'),
 851            ('^', '&'),
 852            ('`', '<'),
 853            ('{', 'ç'),
 854            ('|', '§'),
 855            ('}', '°'),
 856            ('~', '>'),
 857        ],
 858        "com.apple.keylayout.LatinAmerican" => &[
 859            ('"', '¨'),
 860            ('&', '/'),
 861            ('(', ')'),
 862            (')', '='),
 863            ('*', '('),
 864            ('/', '\''),
 865            (':', 'Ñ'),
 866            (';', 'ñ'),
 867            ('<', ';'),
 868            ('=', '*'),
 869            ('>', ':'),
 870            ('@', '"'),
 871            ('[', '{'),
 872            ('\'', '´'),
 873            ('\\', '¿'),
 874            (']', '}'),
 875            ('^', '&'),
 876            ('`', '<'),
 877            ('{', '['),
 878            ('|', '¡'),
 879            ('}', ']'),
 880            ('~', '>'),
 881        ],
 882        "com.apple.keylayout.Lithuanian" => &[
 883            ('!', 'Ą'),
 884            ('#', 'Ę'),
 885            ('$', 'Ė'),
 886            ('%', 'Į'),
 887            ('&', 'Ų'),
 888            ('*', 'Ū'),
 889            ('+', 'Ž'),
 890            ('1', 'ą'),
 891            ('2', 'č'),
 892            ('3', 'ę'),
 893            ('4', 'ė'),
 894            ('5', 'į'),
 895            ('6', 'š'),
 896            ('7', 'ų'),
 897            ('8', 'ū'),
 898            ('=', 'ž'),
 899            ('@', 'Č'),
 900            ('^', 'Š'),
 901        ],
 902        "com.apple.keylayout.Maltese" => &[
 903            ('#', '£'),
 904            ('[', 'ġ'),
 905            (']', 'ħ'),
 906            ('`', 'ż'),
 907            ('{', 'Ġ'),
 908            ('}', 'Ħ'),
 909            ('~', 'Ż'),
 910        ],
 911        "com.apple.keylayout.NorthernSami" => &[
 912            ('"', 'Ŋ'),
 913            ('&', '/'),
 914            ('(', ')'),
 915            (')', '='),
 916            ('*', '('),
 917            ('/', '´'),
 918            (':', 'Å'),
 919            (';', 'å'),
 920            ('<', ';'),
 921            ('=', '`'),
 922            ('>', ':'),
 923            ('@', '"'),
 924            ('Q', 'Á'),
 925            ('W', 'Š'),
 926            ('X', 'Č'),
 927            ('[', 'ø'),
 928            ('\'', 'ŋ'),
 929            ('\\', 'đ'),
 930            (']', 'æ'),
 931            ('^', '&'),
 932            ('`', 'ž'),
 933            ('q', 'á'),
 934            ('w', 'š'),
 935            ('x', 'č'),
 936            ('{', 'Ø'),
 937            ('|', 'Đ'),
 938            ('}', 'Æ'),
 939            ('~', 'Ž'),
 940        ],
 941        "com.apple.keylayout.Norwegian" => &[
 942            ('"', '^'),
 943            ('&', '/'),
 944            ('(', ')'),
 945            (')', '='),
 946            ('*', '('),
 947            ('/', '´'),
 948            (':', 'Å'),
 949            (';', 'å'),
 950            ('<', ';'),
 951            ('=', '`'),
 952            ('>', ':'),
 953            ('@', '"'),
 954            ('[', 'ø'),
 955            ('\'', '¨'),
 956            ('\\', '@'),
 957            (']', 'æ'),
 958            ('^', '&'),
 959            ('`', '<'),
 960            ('{', 'Ø'),
 961            ('|', '*'),
 962            ('}', 'Æ'),
 963            ('~', '>'),
 964        ],
 965        "com.apple.keylayout.NorwegianExtended" => &[
 966            ('"', 'ˆ'),
 967            ('&', '/'),
 968            ('(', ')'),
 969            (')', '='),
 970            ('*', '('),
 971            ('/', '´'),
 972            (':', 'Å'),
 973            (';', 'å'),
 974            ('<', ';'),
 975            ('=', '`'),
 976            ('>', ':'),
 977            ('@', '"'),
 978            ('[', 'ø'),
 979            ('\\', '@'),
 980            (']', 'æ'),
 981            ('`', '<'),
 982            ('}', 'Æ'),
 983            ('~', '>'),
 984        ],
 985        "com.apple.keylayout.NorwegianSami-PC" => &[
 986            ('"', 'ˆ'),
 987            ('&', '/'),
 988            ('(', ')'),
 989            (')', '='),
 990            ('*', '('),
 991            ('/', '´'),
 992            (':', 'Å'),
 993            (';', 'å'),
 994            ('<', ';'),
 995            ('=', '`'),
 996            ('>', ':'),
 997            ('@', '"'),
 998            ('[', 'ø'),
 999            ('\'', '¨'),
1000            ('\\', '@'),
1001            (']', 'æ'),
1002            ('^', '&'),
1003            ('`', '<'),
1004            ('{', 'Ø'),
1005            ('|', '*'),
1006            ('}', 'Æ'),
1007            ('~', '>'),
1008        ],
1009        "com.apple.keylayout.Polish" => &[
1010            ('!', '§'),
1011            ('"', 'ę'),
1012            ('#', '!'),
1013            ('$', '?'),
1014            ('%', '+'),
1015            ('&', ':'),
1016            ('(', '/'),
1017            (')', '"'),
1018            ('*', '_'),
1019            ('+', ']'),
1020            (',', '.'),
1021            ('.', ','),
1022            ('/', 'ż'),
1023            (':', 'Ł'),
1024            (';', 'ł'),
1025            ('<', 'ś'),
1026            ('=', '['),
1027            ('>', 'ń'),
1028            ('?', 'Ż'),
1029            ('@', '%'),
1030            ('[', 'ó'),
1031            ('\'', 'ą'),
1032            ('\\', ';'),
1033            (']', '('),
1034            ('^', '='),
1035            ('_', 'ć'),
1036            ('`', '<'),
1037            ('{', 'ź'),
1038            ('|', '$'),
1039            ('}', ')'),
1040            ('~', '>'),
1041        ],
1042        "com.apple.keylayout.Portuguese" => &[
1043            ('"', '`'),
1044            ('&', '/'),
1045            ('(', ')'),
1046            (')', '='),
1047            ('*', '('),
1048            ('/', '\''),
1049            (':', 'ª'),
1050            (';', 'º'),
1051            ('<', ';'),
1052            ('=', '*'),
1053            ('>', ':'),
1054            ('@', '"'),
1055            ('[', 'ç'),
1056            ('\'', '´'),
1057            (']', '~'),
1058            ('^', '&'),
1059            ('`', '<'),
1060            ('{', 'Ç'),
1061            ('}', '^'),
1062            ('~', '>'),
1063        ],
1064        "com.apple.keylayout.Sami-PC" => &[
1065            ('"', 'Ŋ'),
1066            ('&', '/'),
1067            ('(', ')'),
1068            (')', '='),
1069            ('*', '('),
1070            ('/', '´'),
1071            (':', 'Å'),
1072            (';', 'å'),
1073            ('<', ';'),
1074            ('=', '`'),
1075            ('>', ':'),
1076            ('@', '"'),
1077            ('Q', 'Á'),
1078            ('W', 'Š'),
1079            ('X', 'Č'),
1080            ('[', 'ø'),
1081            ('\'', 'ŋ'),
1082            ('\\', 'đ'),
1083            (']', 'æ'),
1084            ('^', '&'),
1085            ('`', 'ž'),
1086            ('q', 'á'),
1087            ('w', 'š'),
1088            ('x', 'č'),
1089            ('{', 'Ø'),
1090            ('|', 'Đ'),
1091            ('}', 'Æ'),
1092            ('~', 'Ž'),
1093        ],
1094        "com.apple.keylayout.Serbian-Latin" => &[
1095            ('"', 'Ć'),
1096            ('&', '\''),
1097            ('(', ')'),
1098            (')', '='),
1099            ('*', '('),
1100            (':', 'Č'),
1101            (';', 'č'),
1102            ('<', ';'),
1103            ('=', '*'),
1104            ('>', ':'),
1105            ('@', '"'),
1106            ('[', 'š'),
1107            ('\'', 'ć'),
1108            ('\\', 'ž'),
1109            (']', 'đ'),
1110            ('^', '&'),
1111            ('`', '<'),
1112            ('{', 'Š'),
1113            ('|', 'Ž'),
1114            ('}', 'Đ'),
1115            ('~', '>'),
1116        ],
1117        "com.apple.keylayout.Slovak" => &[
1118            ('!', '1'),
1119            ('"', '!'),
1120            ('#', '3'),
1121            ('$', '4'),
1122            ('%', '5'),
1123            ('&', '7'),
1124            ('(', '9'),
1125            (')', '0'),
1126            ('*', '8'),
1127            ('+', '%'),
1128            ('/', '\''),
1129            ('0', 'é'),
1130            ('1', '+'),
1131            ('2', 'ľ'),
1132            ('3', 'š'),
1133            ('4', 'č'),
1134            ('5', 'ť'),
1135            ('6', 'ž'),
1136            ('7', 'ý'),
1137            ('8', 'á'),
1138            ('9', 'í'),
1139            (':', '"'),
1140            (';', 'ô'),
1141            ('<', '?'),
1142            ('>', ':'),
1143            ('?', 'ˇ'),
1144            ('@', '2'),
1145            ('[', 'ú'),
1146            ('\'', '§'),
1147            (']', 'ä'),
1148            ('^', '6'),
1149            ('`', 'ň'),
1150            ('{', 'Ú'),
1151            ('}', 'Ä'),
1152            ('~', 'Ň'),
1153        ],
1154        "com.apple.keylayout.Slovak-QWERTY" => &[
1155            ('!', '1'),
1156            ('"', '!'),
1157            ('#', '3'),
1158            ('$', '4'),
1159            ('%', '5'),
1160            ('&', '7'),
1161            ('(', '9'),
1162            (')', '0'),
1163            ('*', '8'),
1164            ('+', '%'),
1165            ('/', '\''),
1166            ('0', 'é'),
1167            ('1', '+'),
1168            ('2', 'ľ'),
1169            ('3', 'š'),
1170            ('4', 'č'),
1171            ('5', 'ť'),
1172            ('6', 'ž'),
1173            ('7', 'ý'),
1174            ('8', 'á'),
1175            ('9', 'í'),
1176            (':', '"'),
1177            (';', 'ô'),
1178            ('<', '?'),
1179            ('>', ':'),
1180            ('?', 'ˇ'),
1181            ('@', '2'),
1182            ('[', 'ú'),
1183            ('\'', '§'),
1184            (']', 'ä'),
1185            ('^', '6'),
1186            ('`', 'ň'),
1187            ('{', 'Ú'),
1188            ('}', 'Ä'),
1189            ('~', 'Ň'),
1190        ],
1191        "com.apple.keylayout.Slovenian" => &[
1192            ('"', 'Ć'),
1193            ('&', '\''),
1194            ('(', ')'),
1195            (')', '='),
1196            ('*', '('),
1197            (':', 'Č'),
1198            (';', 'č'),
1199            ('<', ';'),
1200            ('=', '*'),
1201            ('>', ':'),
1202            ('@', '"'),
1203            ('[', 'š'),
1204            ('\'', 'ć'),
1205            ('\\', 'ž'),
1206            (']', 'đ'),
1207            ('^', '&'),
1208            ('`', '<'),
1209            ('{', 'Š'),
1210            ('|', 'Ž'),
1211            ('}', 'Đ'),
1212            ('~', '>'),
1213        ],
1214        "com.apple.keylayout.Spanish" => &[
1215            ('!', '¡'),
1216            ('"', '¨'),
1217            ('.', 'ç'),
1218            ('/', '.'),
1219            (':', 'º'),
1220            (';', '´'),
1221            ('<', '¿'),
1222            ('>', 'Ç'),
1223            ('@', '!'),
1224            ('[', 'ñ'),
1225            ('\'', '`'),
1226            ('\\', '\''),
1227            (']', ';'),
1228            ('^', '/'),
1229            ('`', '<'),
1230            ('{', 'Ñ'),
1231            ('|', '"'),
1232            ('}', ':'),
1233            ('~', '>'),
1234        ],
1235        "com.apple.keylayout.Spanish-ISO" => &[
1236            ('"', '¨'),
1237            ('#', '·'),
1238            ('&', '/'),
1239            ('(', ')'),
1240            (')', '='),
1241            ('*', '('),
1242            ('.', 'ç'),
1243            ('/', '.'),
1244            (':', 'º'),
1245            (';', '´'),
1246            ('<', '¿'),
1247            ('>', 'Ç'),
1248            ('@', '"'),
1249            ('[', 'ñ'),
1250            ('\'', '`'),
1251            ('\\', '\''),
1252            (']', ';'),
1253            ('^', '&'),
1254            ('`', '<'),
1255            ('{', 'Ñ'),
1256            ('|', '"'),
1257            ('}', '`'),
1258            ('~', '>'),
1259        ],
1260        "com.apple.keylayout.Swedish" => &[
1261            ('"', '^'),
1262            ('$', '€'),
1263            ('&', '/'),
1264            ('(', ')'),
1265            (')', '='),
1266            ('*', '('),
1267            ('/', '´'),
1268            (':', 'Å'),
1269            (';', 'å'),
1270            ('<', ';'),
1271            ('=', '`'),
1272            ('>', ':'),
1273            ('@', '"'),
1274            ('[', 'ö'),
1275            ('\'', '¨'),
1276            ('\\', '\''),
1277            (']', 'ä'),
1278            ('^', '&'),
1279            ('`', '<'),
1280            ('{', 'Ö'),
1281            ('|', '*'),
1282            ('}', 'Ä'),
1283            ('~', '>'),
1284        ],
1285        "com.apple.keylayout.Swedish-Pro" => &[
1286            ('"', '^'),
1287            ('$', '€'),
1288            ('&', '/'),
1289            ('(', ')'),
1290            (')', '='),
1291            ('*', '('),
1292            ('/', '´'),
1293            (':', 'Å'),
1294            (';', 'å'),
1295            ('<', ';'),
1296            ('=', '`'),
1297            ('>', ':'),
1298            ('@', '"'),
1299            ('[', 'ö'),
1300            ('\'', '¨'),
1301            ('\\', '\''),
1302            (']', 'ä'),
1303            ('^', '&'),
1304            ('`', '<'),
1305            ('{', 'Ö'),
1306            ('|', '*'),
1307            ('}', 'Ä'),
1308            ('~', '>'),
1309        ],
1310        "com.apple.keylayout.SwedishSami-PC" => &[
1311            ('"', 'ˆ'),
1312            ('&', '/'),
1313            ('(', ')'),
1314            (')', '='),
1315            ('*', '('),
1316            ('/', '´'),
1317            (':', 'Å'),
1318            (';', 'å'),
1319            ('<', ';'),
1320            ('=', '`'),
1321            ('>', ':'),
1322            ('@', '"'),
1323            ('[', 'ö'),
1324            ('\'', '¨'),
1325            ('\\', '@'),
1326            (']', 'ä'),
1327            ('^', '&'),
1328            ('`', '<'),
1329            ('{', 'Ö'),
1330            ('|', '*'),
1331            ('}', 'Ä'),
1332            ('~', '>'),
1333        ],
1334        "com.apple.keylayout.SwissFrench" => &[
1335            ('!', '+'),
1336            ('"', '`'),
1337            ('#', '*'),
1338            ('$', 'ç'),
1339            ('&', '/'),
1340            ('(', ')'),
1341            (')', '='),
1342            ('*', '('),
1343            ('+', '!'),
1344            ('/', '\''),
1345            (':', 'ü'),
1346            (';', 'è'),
1347            ('<', ';'),
1348            ('=', '¨'),
1349            ('>', ':'),
1350            ('@', '"'),
1351            ('[', 'é'),
1352            ('\'', '^'),
1353            ('\\', '$'),
1354            (']', 'à'),
1355            ('^', '&'),
1356            ('`', '<'),
1357            ('{', 'ö'),
1358            ('|', '£'),
1359            ('}', 'ä'),
1360            ('~', '>'),
1361        ],
1362        "com.apple.keylayout.SwissGerman" => &[
1363            ('!', '+'),
1364            ('"', '`'),
1365            ('#', '*'),
1366            ('$', 'ç'),
1367            ('&', '/'),
1368            ('(', ')'),
1369            (')', '='),
1370            ('*', '('),
1371            ('+', '!'),
1372            ('/', '\''),
1373            (':', 'è'),
1374            (';', 'ü'),
1375            ('<', ';'),
1376            ('=', '¨'),
1377            ('>', ':'),
1378            ('@', '"'),
1379            ('[', 'ö'),
1380            ('\'', '^'),
1381            ('\\', '$'),
1382            (']', 'ä'),
1383            ('^', '&'),
1384            ('`', '<'),
1385            ('{', 'é'),
1386            ('|', '£'),
1387            ('}', 'à'),
1388            ('~', '>'),
1389        ],
1390        "com.apple.keylayout.Turkish" => &[
1391            ('"', '-'),
1392            ('#', '"'),
1393            ('$', '\''),
1394            ('%', '('),
1395            ('&', ')'),
1396            ('(', '%'),
1397            (')', ':'),
1398            ('*', '_'),
1399            (',', 'ö'),
1400            ('-', 'ş'),
1401            ('.', 'ç'),
1402            ('/', '.'),
1403            (':', '$'),
1404            ('<', 'Ö'),
1405            ('>', 'Ç'),
1406            ('@', '*'),
1407            ('[', 'ğ'),
1408            ('\'', ','),
1409            ('\\', 'ü'),
1410            (']', 'ı'),
1411            ('^', '/'),
1412            ('_', 'Ş'),
1413            ('`', '<'),
1414            ('{', 'Ğ'),
1415            ('|', 'Ü'),
1416            ('}', 'I'),
1417            ('~', '>'),
1418        ],
1419        "com.apple.keylayout.Turkish-QWERTY-PC" => &[
1420            ('"', 'I'),
1421            ('#', '^'),
1422            ('$', '+'),
1423            ('&', '/'),
1424            ('(', ')'),
1425            (')', '='),
1426            ('*', '('),
1427            ('+', ':'),
1428            (',', 'ö'),
1429            ('.', 'ç'),
1430            ('/', '*'),
1431            (':', 'Ş'),
1432            (';', 'ş'),
1433            ('<', 'Ö'),
1434            ('=', '.'),
1435            ('>', 'Ç'),
1436            ('@', '\''),
1437            ('[', 'ğ'),
1438            ('\'', 'ı'),
1439            ('\\', ','),
1440            (']', 'ü'),
1441            ('^', '&'),
1442            ('`', '<'),
1443            ('{', 'Ğ'),
1444            ('|', ';'),
1445            ('}', 'Ü'),
1446            ('~', '>'),
1447        ],
1448        "com.apple.keylayout.Turkish-Standard" => &[
1449            ('"', 'Ş'),
1450            ('#', '^'),
1451            ('&', '\''),
1452            ('(', ')'),
1453            (')', '='),
1454            ('*', '('),
1455            (',', '.'),
1456            ('.', ','),
1457            (':', 'Ç'),
1458            (';', 'ç'),
1459            ('<', ':'),
1460            ('=', '*'),
1461            ('>', ';'),
1462            ('@', '"'),
1463            ('[', 'ğ'),
1464            ('\'', 'ş'),
1465            ('\\', 'ü'),
1466            (']', 'ı'),
1467            ('^', '&'),
1468            ('`', 'ö'),
1469            ('{', 'Ğ'),
1470            ('|', 'Ü'),
1471            ('}', 'I'),
1472            ('~', 'Ö'),
1473        ],
1474        "com.apple.keylayout.Turkmen" => &[
1475            ('C', 'Ç'),
1476            ('Q', 'Ä'),
1477            ('V', 'Ý'),
1478            ('X', 'Ü'),
1479            ('[', 'ň'),
1480            ('\\', 'ş'),
1481            (']', 'ö'),
1482            ('^', '№'),
1483            ('`', 'ž'),
1484            ('c', 'ç'),
1485            ('q', 'ä'),
1486            ('v', 'ý'),
1487            ('x', 'ü'),
1488            ('{', 'Ň'),
1489            ('|', 'Ş'),
1490            ('}', 'Ö'),
1491            ('~', 'Ž'),
1492        ],
1493        "com.apple.keylayout.USInternational-PC" => &[('^', 'ˆ'), ('~', '˜')],
1494        "com.apple.keylayout.Welsh" => &[('#', '£')],
1495
1496        _ => return None,
1497    };
1498
1499    Some(HashMap::from_iter(mappings.iter().cloned()))
1500}