172 lines
5.9 KiB
C++
172 lines
5.9 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "KeyboardLayout.h"
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// WindowsHWrapper et pas un <Windows.h> 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<FName, uint32>& GetReferenceScanCodes()
|
|
{
|
|
static const TMap<FName, uint32> ScanCodes = []
|
|
{
|
|
TMap<FName, uint32> 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
|
|
}
|
|
}
|