Files
Unreal_EmberWild/Source/Survival_projet/Private/KeyboardLayout.cpp
T
2026-08-06 13:27:21 +02:00

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
}
}