// Fill out your copyright notice in the Description page of Project Settings. #include "KeyboardLayout.h" #if PLATFORM_WINDOWS // WindowsHWrapper et pas un nu : il pose lui-meme les gardes // Pre/PostWindowsApi qui neutralisent les macros et les typedefs de l'API Win32 // entrant en collision avec ceux du moteur (INT, FLOAT, TEXT...). #include "Windows/WindowsHWrapper.h" #endif namespace KeyboardLayout { #if PLATFORM_WINDOWS /** * Position physique de chaque touche de reference, en scancode PS/2 set 1. * * Ces codes decrivent l'EMPLACEMENT sur le clavier, pas le caractere grave * dessus : c'est ce qui rend la table independante de la disposition. Ils sont * figes depuis le clavier IBM de 1984 et ne bougeront plus. * * Table ecrite a la main plutot que deduite d'un HKL americain charge a la * volee : LoadKeyboardLayout ajoute la disposition a celles du systeme, et un * jeu n'a pas a toucher a la configuration clavier de la machine. * * Construite au PREMIER APPEL et pas en variable globale : les constantes de * EKeys sont initialisees par le moteur au demarrage, et une table globale * pourrait naitre avant elles. */ static const TMap& GetReferenceScanCodes() { static const TMap ScanCodes = [] { TMap Map; Map.Reserve(48); // Rangee des chiffres Map.Add(EKeys::Tilde.GetFName(), 0x29); Map.Add(EKeys::One.GetFName(), 0x02); Map.Add(EKeys::Two.GetFName(), 0x03); Map.Add(EKeys::Three.GetFName(), 0x04); Map.Add(EKeys::Four.GetFName(), 0x05); Map.Add(EKeys::Five.GetFName(), 0x06); Map.Add(EKeys::Six.GetFName(), 0x07); Map.Add(EKeys::Seven.GetFName(), 0x08); Map.Add(EKeys::Eight.GetFName(), 0x09); Map.Add(EKeys::Nine.GetFName(), 0x0A); Map.Add(EKeys::Zero.GetFName(), 0x0B); Map.Add(EKeys::Hyphen.GetFName(), 0x0C); Map.Add(EKeys::Equals.GetFName(), 0x0D); // Rangee du haut Map.Add(EKeys::Q.GetFName(), 0x10); Map.Add(EKeys::W.GetFName(), 0x11); Map.Add(EKeys::E.GetFName(), 0x12); Map.Add(EKeys::R.GetFName(), 0x13); Map.Add(EKeys::T.GetFName(), 0x14); Map.Add(EKeys::Y.GetFName(), 0x15); Map.Add(EKeys::U.GetFName(), 0x16); Map.Add(EKeys::I.GetFName(), 0x17); Map.Add(EKeys::O.GetFName(), 0x18); Map.Add(EKeys::P.GetFName(), 0x19); Map.Add(EKeys::LeftBracket.GetFName(), 0x1A); Map.Add(EKeys::RightBracket.GetFName(), 0x1B); Map.Add(EKeys::Backslash.GetFName(), 0x2B); // Rangee de repos Map.Add(EKeys::A.GetFName(), 0x1E); Map.Add(EKeys::S.GetFName(), 0x1F); Map.Add(EKeys::D.GetFName(), 0x20); Map.Add(EKeys::F.GetFName(), 0x21); Map.Add(EKeys::G.GetFName(), 0x22); Map.Add(EKeys::H.GetFName(), 0x23); Map.Add(EKeys::J.GetFName(), 0x24); Map.Add(EKeys::K.GetFName(), 0x25); Map.Add(EKeys::L.GetFName(), 0x26); Map.Add(EKeys::Semicolon.GetFName(), 0x27); Map.Add(EKeys::Apostrophe.GetFName(), 0x28); // Rangee du bas Map.Add(EKeys::Z.GetFName(), 0x2C); Map.Add(EKeys::X.GetFName(), 0x2D); Map.Add(EKeys::C.GetFName(), 0x2E); Map.Add(EKeys::V.GetFName(), 0x2F); Map.Add(EKeys::B.GetFName(), 0x30); Map.Add(EKeys::N.GetFName(), 0x31); Map.Add(EKeys::M.GetFName(), 0x32); Map.Add(EKeys::Comma.GetFName(), 0x33); Map.Add(EKeys::Period.GetFName(), 0x34); Map.Add(EKeys::Slash.GetFName(), 0x35); return Map; }(); return ScanCodes; } #endif // PLATFORM_WINDOWS FKey RemapKeyToActiveLayout(const FKey& ReferenceKey) { #if PLATFORM_WINDOWS const uint32* ScanCode = GetReferenceScanCodes().Find(ReferenceKey.GetFName()); if (!ScanCode) { // Touche insensible a la disposition : espace, modificateur, manette. return ReferenceKey; } // GetKeyboardLayout(0) = la disposition du thread appelant, donc celle que // le joueur a reellement active -- et pas celle par defaut du systeme, qui // differe des qu'il en a plusieurs et bascule au clavier. const HKL ActiveLayout = ::GetKeyboardLayout(0); const UINT VirtualKey = ::MapVirtualKeyExW(*ScanCode, MAPVK_VSC_TO_VK_EX, ActiveLayout); if (VirtualKey == 0) { // La disposition n'a rien a cette position. Rare, mais possible sur les // dispositions non latines : on garde la touche d'origine. return ReferenceKey; } // On REPRODUIT ici, a l'identique, le couple que le moteur recevra au // runtime : FWindowsApplication passe le virtual key ET le caractere non // modifie rendu par MAPVK_VK_TO_CHAR. Deduire le CharCode du virtual key // serait faux des que la touche ne porte pas le caractere de son code -- // et c'est precisement le cas de la rangee des chiffres en AZERTY, ou // Windows garde VK_1 pour une touche qui produit "&". Le moteur y repond // EKeys::Ampersand ; simuler EKeys::One nous ferait conclure qu'il n'y a // rien a convertir, et les dix touches de la barre resteraient muettes. // // Le bit de poids fort signale une touche morte (l'accent circonflexe d'un // AZERTY, par exemple) : on le masque, seul le caractere nous interesse. const UINT CharCode = ::MapVirtualKeyExW(VirtualKey, MAPVK_VK_TO_CHAR, ActiveLayout) & 0x7FFFFFFF; const FKey MappedKey = FInputKeyManager::Get().GetKeyFromCodes(VirtualKey, CharCode); // Une touche introuvable dans les deux tables est SYNTHETISEE par le moteur, // et elle sort marquee non bindable -- l'assigner donnerait une action // definitivement muette. C'est le seul cas ou l'on prefere une touche fausse // a une touche morte. if (!MappedKey.IsValid() || !MappedKey.IsBindableToActions()) { return ReferenceKey; } return MappedKey; #else return ReferenceKey; #endif } FString GetActiveLayoutId() { #if PLATFORM_WINDOWS // 16 et pas KL_NAMELENGTH : l'API en ecrit 9 au plus, et un buffer en dur // evite de dependre d'une macro que WIN32_LEAN_AND_MEAN pourrait retirer. WCHAR LayoutName[16] = {}; if (::GetKeyboardLayoutNameW(LayoutName)) { return FString(LayoutName); } return TEXT("inconnue"); #else return TEXT("non gere sur cette plateforme"); #endif } }