Merge c5a02153c5
into f54d209bc8
This commit is contained in:
commit
9a135a20bb
@ -151,6 +151,16 @@ enable_hotbar_mouse_wheel (Hotbar: Enable mouse wheel for selection) bool true
|
||||
# Requires: keyboard_mouse
|
||||
invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
|
||||
|
||||
# SDL only: Save key controls as scancodes (position on the keyboard) instead of keycodes (the
|
||||
# symbol marked on the key). This allows using the same physical keys with multiple
|
||||
# keyboard layouts, but the new scancode-based settings can not be parsed by older clients.
|
||||
# Note that this only affects how keybindings are saved in the settings. In particular,
|
||||
# key controls are always matched based on scancodes regardless of this setting if SDL is used.
|
||||
# Key controls are matched based on keycodes for non-SDL devices.
|
||||
#
|
||||
# Requires: !touch_controls
|
||||
save_keys_as_scancodes (Save keybindings as scancodes) bool true
|
||||
|
||||
[*Touchscreen]
|
||||
|
||||
# Enables the touchscreen controls, allowing you to play the game with a touchscreen.
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "IrrCompileConfig.h"
|
||||
#include "position2d.h"
|
||||
#include "SColor.h" // video::ECOLOR_FORMAT
|
||||
#include <variant>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -342,6 +343,20 @@ public:
|
||||
{
|
||||
return video::isDriverSupported(driver);
|
||||
}
|
||||
|
||||
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) || USE_SDL2
|
||||
//! Get the scancode of the corresponding keycode.
|
||||
virtual std::variant<u32, EKEY_CODE> getScancodeFromKey(const Keycode &key) const {
|
||||
if (auto pv = std::get_if<EKEY_CODE>(&key))
|
||||
return *pv;
|
||||
return (u32)std::get<wchar_t>(key);
|
||||
}
|
||||
|
||||
//! Get the keycode of the corresponding scancode.
|
||||
virtual Keycode getKeyFromScancode(const u32 scancode) const {
|
||||
return Keycode(KEY_UNKNOWN, (wchar_t)scancode);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
|
@ -3,6 +3,7 @@
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#pragma once
|
||||
#include <variant>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -182,4 +183,29 @@ enum EKEY_CODE
|
||||
KEY_KEY_CODES_COUNT = 0x100 // this is not a key, but the amount of keycodes there are.
|
||||
};
|
||||
|
||||
class Keycode : public std::variant<EKEY_CODE, wchar_t> {
|
||||
using super = std::variant<EKEY_CODE, wchar_t>;
|
||||
public:
|
||||
Keycode() : Keycode(KEY_KEY_CODES_COUNT, L'\0') {}
|
||||
|
||||
Keycode(EKEY_CODE code, wchar_t ch)
|
||||
{
|
||||
emplace(code, ch);
|
||||
}
|
||||
|
||||
using super::emplace;
|
||||
void emplace(EKEY_CODE code, wchar_t ch)
|
||||
{
|
||||
if (isValid(code))
|
||||
emplace<EKEY_CODE>(code);
|
||||
else
|
||||
emplace<wchar_t>(ch);
|
||||
}
|
||||
|
||||
static bool isValid(EKEY_CODE code)
|
||||
{
|
||||
return code > 0 && code < KEY_KEY_CODES_COUNT;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "COSOperator.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "SIrrCreationParameters.h"
|
||||
#include <SDL_video.h>
|
||||
|
||||
@ -27,6 +29,14 @@
|
||||
|
||||
#include "CSDLManager.h"
|
||||
|
||||
// Since SDL doesn't have mouse keys as keycodes we fall back to EKEY_CODE
|
||||
static const std::unordered_set<irr::EKEY_CODE> fake_keys = {
|
||||
irr::KEY_LBUTTON, irr::KEY_MBUTTON, irr::KEY_RBUTTON, irr::KEY_XBUTTON1, irr::KEY_XBUTTON2
|
||||
};
|
||||
static inline bool is_fake_key(irr::EKEY_CODE key) {
|
||||
return fake_keys.find(key) != fake_keys.end();
|
||||
}
|
||||
|
||||
static int SDLDeviceInstances = 0;
|
||||
|
||||
namespace irr
|
||||
@ -220,6 +230,51 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtK
|
||||
}
|
||||
}
|
||||
|
||||
// Irrlicht has some EKEY_CODE entries that only appear to make sense in a SDL-scancode-like context.
|
||||
// These keycodes are passed by (in particular) the X11 IrrlichtDevice.
|
||||
// Perform one-way conversion if we encounter them.
|
||||
std::unordered_map<EKEY_CODE, SDL_Scancode> ekey_scancodes = {
|
||||
{KEY_OEM_1, SDL_SCANCODE_SEMICOLON},
|
||||
{KEY_OEM_2, SDL_SCANCODE_SLASH},
|
||||
{KEY_OEM_3, SDL_SCANCODE_GRAVE},
|
||||
{KEY_OEM_4, SDL_SCANCODE_LEFTBRACKET},
|
||||
{KEY_OEM_5, SDL_SCANCODE_BACKSLASH},
|
||||
{KEY_OEM_6, SDL_SCANCODE_RIGHTBRACKET},
|
||||
{KEY_OEM_7, SDL_SCANCODE_APOSTROPHE},
|
||||
// KEY_OEM_8 is apparently unused -> ignored here
|
||||
// KEY_OEM_AX does not appear to be sent by Irrlicht -> ignored here
|
||||
{KEY_OEM_102, SDL_SCANCODE_NONUSBACKSLASH},
|
||||
};
|
||||
|
||||
std::variant<u32, EKEY_CODE> CIrrDeviceSDL::getScancodeFromKey(const Keycode &key) const
|
||||
{
|
||||
u32 keynum = 0;
|
||||
if (const auto *keycode = std::get_if<EKEY_CODE>(&key)) {
|
||||
if (is_fake_key(*keycode))
|
||||
return *keycode;
|
||||
if (const auto &ent = ekey_scancodes.find(*keycode); ent != ekey_scancodes.end())
|
||||
return ent->second;
|
||||
for (const auto &entry: KeyMap) {
|
||||
if (entry.second == *keycode) {
|
||||
keynum = entry.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
keynum = std::get<wchar_t>(key);
|
||||
}
|
||||
return (u32)SDL_GetScancodeFromKey(keynum);
|
||||
}
|
||||
|
||||
Keycode CIrrDeviceSDL::getKeyFromScancode(const u32 scancode) const
|
||||
{
|
||||
auto keycode = SDL_GetKeyFromScancode((SDL_Scancode)scancode);
|
||||
const auto &keyentry = KeyMap.find(keycode);
|
||||
auto irrcode = keyentry != KeyMap.end() ? keyentry->second : KEY_UNKNOWN;
|
||||
auto keychar = findCharToPassToIrrlicht(keycode, irrcode, false);
|
||||
return Keycode(irrcode, keychar);
|
||||
}
|
||||
|
||||
void CIrrDeviceSDL::resetReceiveTextInputEvents()
|
||||
{
|
||||
gui::IGUIElement *elem = GUIEnvironment->getFocus();
|
||||
@ -814,18 +869,27 @@ bool CIrrDeviceSDL::run()
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP: {
|
||||
SKeyMap mp;
|
||||
mp.SDLKey = SDL_event.key.keysym.sym;
|
||||
s32 idx = KeyMap.binary_search(mp);
|
||||
auto keysym = SDL_event.key.keysym.sym;
|
||||
auto scancode = SDL_event.key.keysym.scancode;
|
||||
|
||||
EKEY_CODE key;
|
||||
if (idx == -1)
|
||||
key = (EKEY_CODE)0;
|
||||
else
|
||||
key = (EKEY_CODE)KeyMap[idx].Win32Key;
|
||||
// Treat AC_BACK as the Escape key
|
||||
if (scancode == SDL_SCANCODE_AC_BACK || scancode == SDL_SCANCODE_ESCAPE)
|
||||
{
|
||||
if (SDL_event.type == SDL_KEYDOWN)
|
||||
escapeKeys.insert(scancode);
|
||||
else
|
||||
escapeKeys.erase(scancode);
|
||||
if (SDL_event.type == SDL_KEYUP && !escapeKeys.empty())
|
||||
break; // avoid sending KEYUP twice if AC_BACK and ESCAPE are both released
|
||||
scancode = SDL_SCANCODE_ESCAPE;
|
||||
keysym = SDLK_ESCAPE;
|
||||
}
|
||||
|
||||
if (key == (EKEY_CODE)0)
|
||||
os::Printer::log("keycode not mapped", core::stringc(mp.SDLKey), ELL_DEBUG);
|
||||
const auto &entry = KeyMap.find(keysym);
|
||||
auto key = entry == KeyMap.end() ? KEY_UNKNOWN : entry->second;
|
||||
|
||||
if (!Keycode::isValid(key))
|
||||
os::Printer::log("keycode not mapped", core::stringc(keysym), ELL_DEBUG);
|
||||
|
||||
// Make sure to only input special characters if something is in focus, as SDL_TEXTINPUT handles normal unicode already
|
||||
if (SDL_IsTextInputActive() && !keyIsKnownSpecial(key) && (SDL_event.key.keysym.mod & KMOD_CTRL) == 0)
|
||||
@ -836,8 +900,10 @@ bool CIrrDeviceSDL::run()
|
||||
irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
|
||||
irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
|
||||
irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0;
|
||||
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key,
|
||||
irrevent.KeyInput.Char = findCharToPassToIrrlicht(keysym, key,
|
||||
(SDL_event.key.keysym.mod & KMOD_NUM) != 0);
|
||||
irrevent.KeyInput.SystemKeyCode = scancode;
|
||||
|
||||
postEventFromUser(irrevent);
|
||||
} break;
|
||||
|
||||
@ -1302,142 +1368,135 @@ void CIrrDeviceSDL::createKeyMap()
|
||||
// the lookuptable, but I'll leave it like that until
|
||||
// I find a better version.
|
||||
|
||||
KeyMap.reallocate(105);
|
||||
|
||||
// buttons missing
|
||||
|
||||
// Android back button = ESC
|
||||
KeyMap.push_back(SKeyMap(SDLK_AC_BACK, KEY_ESCAPE));
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_BACKSPACE, KEY_BACK));
|
||||
KeyMap.push_back(SKeyMap(SDLK_TAB, KEY_TAB));
|
||||
KeyMap.push_back(SKeyMap(SDLK_CLEAR, KEY_CLEAR));
|
||||
KeyMap.push_back(SKeyMap(SDLK_RETURN, KEY_RETURN));
|
||||
KeyMap.emplace(SDLK_BACKSPACE, KEY_BACK);
|
||||
KeyMap.emplace(SDLK_TAB, KEY_TAB);
|
||||
KeyMap.emplace(SDLK_CLEAR, KEY_CLEAR);
|
||||
KeyMap.emplace(SDLK_RETURN, KEY_RETURN);
|
||||
|
||||
// combined modifiers missing
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_PAUSE, KEY_PAUSE));
|
||||
KeyMap.push_back(SKeyMap(SDLK_CAPSLOCK, KEY_CAPITAL));
|
||||
KeyMap.emplace(SDLK_PAUSE, KEY_PAUSE);
|
||||
KeyMap.emplace(SDLK_CAPSLOCK, KEY_CAPITAL);
|
||||
|
||||
// asian letter keys missing
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_ESCAPE, KEY_ESCAPE));
|
||||
KeyMap.emplace(SDLK_ESCAPE, KEY_ESCAPE);
|
||||
|
||||
// asian letter keys missing
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_SPACE, KEY_SPACE));
|
||||
KeyMap.push_back(SKeyMap(SDLK_PAGEUP, KEY_PRIOR));
|
||||
KeyMap.push_back(SKeyMap(SDLK_PAGEDOWN, KEY_NEXT));
|
||||
KeyMap.push_back(SKeyMap(SDLK_END, KEY_END));
|
||||
KeyMap.push_back(SKeyMap(SDLK_HOME, KEY_HOME));
|
||||
KeyMap.push_back(SKeyMap(SDLK_LEFT, KEY_LEFT));
|
||||
KeyMap.push_back(SKeyMap(SDLK_UP, KEY_UP));
|
||||
KeyMap.push_back(SKeyMap(SDLK_RIGHT, KEY_RIGHT));
|
||||
KeyMap.push_back(SKeyMap(SDLK_DOWN, KEY_DOWN));
|
||||
KeyMap.emplace(SDLK_SPACE, KEY_SPACE);
|
||||
KeyMap.emplace(SDLK_PAGEUP, KEY_PRIOR);
|
||||
KeyMap.emplace(SDLK_PAGEDOWN, KEY_NEXT);
|
||||
KeyMap.emplace(SDLK_END, KEY_END);
|
||||
KeyMap.emplace(SDLK_HOME, KEY_HOME);
|
||||
KeyMap.emplace(SDLK_LEFT, KEY_LEFT);
|
||||
KeyMap.emplace(SDLK_UP, KEY_UP);
|
||||
KeyMap.emplace(SDLK_RIGHT, KEY_RIGHT);
|
||||
KeyMap.emplace(SDLK_DOWN, KEY_DOWN);
|
||||
|
||||
// select missing
|
||||
KeyMap.push_back(SKeyMap(SDLK_PRINTSCREEN, KEY_PRINT));
|
||||
KeyMap.emplace(SDLK_PRINTSCREEN, KEY_PRINT);
|
||||
// execute missing
|
||||
KeyMap.push_back(SKeyMap(SDLK_PRINTSCREEN, KEY_SNAPSHOT));
|
||||
KeyMap.emplace(SDLK_PRINTSCREEN, KEY_SNAPSHOT);
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_INSERT, KEY_INSERT));
|
||||
KeyMap.push_back(SKeyMap(SDLK_DELETE, KEY_DELETE));
|
||||
KeyMap.push_back(SKeyMap(SDLK_HELP, KEY_HELP));
|
||||
KeyMap.emplace(SDLK_INSERT, KEY_INSERT);
|
||||
KeyMap.emplace(SDLK_DELETE, KEY_DELETE);
|
||||
KeyMap.emplace(SDLK_HELP, KEY_HELP);
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_0, KEY_KEY_0));
|
||||
KeyMap.push_back(SKeyMap(SDLK_1, KEY_KEY_1));
|
||||
KeyMap.push_back(SKeyMap(SDLK_2, KEY_KEY_2));
|
||||
KeyMap.push_back(SKeyMap(SDLK_3, KEY_KEY_3));
|
||||
KeyMap.push_back(SKeyMap(SDLK_4, KEY_KEY_4));
|
||||
KeyMap.push_back(SKeyMap(SDLK_5, KEY_KEY_5));
|
||||
KeyMap.push_back(SKeyMap(SDLK_6, KEY_KEY_6));
|
||||
KeyMap.push_back(SKeyMap(SDLK_7, KEY_KEY_7));
|
||||
KeyMap.push_back(SKeyMap(SDLK_8, KEY_KEY_8));
|
||||
KeyMap.push_back(SKeyMap(SDLK_9, KEY_KEY_9));
|
||||
KeyMap.emplace(SDLK_0, KEY_KEY_0);
|
||||
KeyMap.emplace(SDLK_1, KEY_KEY_1);
|
||||
KeyMap.emplace(SDLK_2, KEY_KEY_2);
|
||||
KeyMap.emplace(SDLK_3, KEY_KEY_3);
|
||||
KeyMap.emplace(SDLK_4, KEY_KEY_4);
|
||||
KeyMap.emplace(SDLK_5, KEY_KEY_5);
|
||||
KeyMap.emplace(SDLK_6, KEY_KEY_6);
|
||||
KeyMap.emplace(SDLK_7, KEY_KEY_7);
|
||||
KeyMap.emplace(SDLK_8, KEY_KEY_8);
|
||||
KeyMap.emplace(SDLK_9, KEY_KEY_9);
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_a, KEY_KEY_A));
|
||||
KeyMap.push_back(SKeyMap(SDLK_b, KEY_KEY_B));
|
||||
KeyMap.push_back(SKeyMap(SDLK_c, KEY_KEY_C));
|
||||
KeyMap.push_back(SKeyMap(SDLK_d, KEY_KEY_D));
|
||||
KeyMap.push_back(SKeyMap(SDLK_e, KEY_KEY_E));
|
||||
KeyMap.push_back(SKeyMap(SDLK_f, KEY_KEY_F));
|
||||
KeyMap.push_back(SKeyMap(SDLK_g, KEY_KEY_G));
|
||||
KeyMap.push_back(SKeyMap(SDLK_h, KEY_KEY_H));
|
||||
KeyMap.push_back(SKeyMap(SDLK_i, KEY_KEY_I));
|
||||
KeyMap.push_back(SKeyMap(SDLK_j, KEY_KEY_J));
|
||||
KeyMap.push_back(SKeyMap(SDLK_k, KEY_KEY_K));
|
||||
KeyMap.push_back(SKeyMap(SDLK_l, KEY_KEY_L));
|
||||
KeyMap.push_back(SKeyMap(SDLK_m, KEY_KEY_M));
|
||||
KeyMap.push_back(SKeyMap(SDLK_n, KEY_KEY_N));
|
||||
KeyMap.push_back(SKeyMap(SDLK_o, KEY_KEY_O));
|
||||
KeyMap.push_back(SKeyMap(SDLK_p, KEY_KEY_P));
|
||||
KeyMap.push_back(SKeyMap(SDLK_q, KEY_KEY_Q));
|
||||
KeyMap.push_back(SKeyMap(SDLK_r, KEY_KEY_R));
|
||||
KeyMap.push_back(SKeyMap(SDLK_s, KEY_KEY_S));
|
||||
KeyMap.push_back(SKeyMap(SDLK_t, KEY_KEY_T));
|
||||
KeyMap.push_back(SKeyMap(SDLK_u, KEY_KEY_U));
|
||||
KeyMap.push_back(SKeyMap(SDLK_v, KEY_KEY_V));
|
||||
KeyMap.push_back(SKeyMap(SDLK_w, KEY_KEY_W));
|
||||
KeyMap.push_back(SKeyMap(SDLK_x, KEY_KEY_X));
|
||||
KeyMap.push_back(SKeyMap(SDLK_y, KEY_KEY_Y));
|
||||
KeyMap.push_back(SKeyMap(SDLK_z, KEY_KEY_Z));
|
||||
KeyMap.emplace(SDLK_a, KEY_KEY_A);
|
||||
KeyMap.emplace(SDLK_b, KEY_KEY_B);
|
||||
KeyMap.emplace(SDLK_c, KEY_KEY_C);
|
||||
KeyMap.emplace(SDLK_d, KEY_KEY_D);
|
||||
KeyMap.emplace(SDLK_e, KEY_KEY_E);
|
||||
KeyMap.emplace(SDLK_f, KEY_KEY_F);
|
||||
KeyMap.emplace(SDLK_g, KEY_KEY_G);
|
||||
KeyMap.emplace(SDLK_h, KEY_KEY_H);
|
||||
KeyMap.emplace(SDLK_i, KEY_KEY_I);
|
||||
KeyMap.emplace(SDLK_j, KEY_KEY_J);
|
||||
KeyMap.emplace(SDLK_k, KEY_KEY_K);
|
||||
KeyMap.emplace(SDLK_l, KEY_KEY_L);
|
||||
KeyMap.emplace(SDLK_m, KEY_KEY_M);
|
||||
KeyMap.emplace(SDLK_n, KEY_KEY_N);
|
||||
KeyMap.emplace(SDLK_o, KEY_KEY_O);
|
||||
KeyMap.emplace(SDLK_p, KEY_KEY_P);
|
||||
KeyMap.emplace(SDLK_q, KEY_KEY_Q);
|
||||
KeyMap.emplace(SDLK_r, KEY_KEY_R);
|
||||
KeyMap.emplace(SDLK_s, KEY_KEY_S);
|
||||
KeyMap.emplace(SDLK_t, KEY_KEY_T);
|
||||
KeyMap.emplace(SDLK_u, KEY_KEY_U);
|
||||
KeyMap.emplace(SDLK_v, KEY_KEY_V);
|
||||
KeyMap.emplace(SDLK_w, KEY_KEY_W);
|
||||
KeyMap.emplace(SDLK_x, KEY_KEY_X);
|
||||
KeyMap.emplace(SDLK_y, KEY_KEY_Y);
|
||||
KeyMap.emplace(SDLK_z, KEY_KEY_Z);
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_LGUI, KEY_LWIN));
|
||||
KeyMap.push_back(SKeyMap(SDLK_RGUI, KEY_RWIN));
|
||||
KeyMap.emplace(SDLK_LGUI, KEY_LWIN);
|
||||
KeyMap.emplace(SDLK_RGUI, KEY_RWIN);
|
||||
// apps missing
|
||||
KeyMap.push_back(SKeyMap(SDLK_POWER, KEY_SLEEP)); //??
|
||||
KeyMap.emplace(SDLK_POWER, KEY_SLEEP); //??
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_0, KEY_NUMPAD0));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_1, KEY_NUMPAD1));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_2, KEY_NUMPAD2));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_3, KEY_NUMPAD3));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_4, KEY_NUMPAD4));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_5, KEY_NUMPAD5));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_6, KEY_NUMPAD6));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_7, KEY_NUMPAD7));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_8, KEY_NUMPAD8));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_9, KEY_NUMPAD9));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_MULTIPLY, KEY_MULTIPLY));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_PLUS, KEY_ADD));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_ENTER, KEY_RETURN));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_MINUS, KEY_SUBTRACT));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_PERIOD, KEY_DECIMAL));
|
||||
KeyMap.push_back(SKeyMap(SDLK_KP_DIVIDE, KEY_DIVIDE));
|
||||
KeyMap.emplace(SDLK_KP_0, KEY_NUMPAD0);
|
||||
KeyMap.emplace(SDLK_KP_1, KEY_NUMPAD1);
|
||||
KeyMap.emplace(SDLK_KP_2, KEY_NUMPAD2);
|
||||
KeyMap.emplace(SDLK_KP_3, KEY_NUMPAD3);
|
||||
KeyMap.emplace(SDLK_KP_4, KEY_NUMPAD4);
|
||||
KeyMap.emplace(SDLK_KP_5, KEY_NUMPAD5);
|
||||
KeyMap.emplace(SDLK_KP_6, KEY_NUMPAD6);
|
||||
KeyMap.emplace(SDLK_KP_7, KEY_NUMPAD7);
|
||||
KeyMap.emplace(SDLK_KP_8, KEY_NUMPAD8);
|
||||
KeyMap.emplace(SDLK_KP_9, KEY_NUMPAD9);
|
||||
KeyMap.emplace(SDLK_KP_MULTIPLY, KEY_MULTIPLY);
|
||||
KeyMap.emplace(SDLK_KP_PLUS, KEY_ADD);
|
||||
KeyMap.emplace(SDLK_KP_ENTER, KEY_RETURN);
|
||||
KeyMap.emplace(SDLK_KP_MINUS, KEY_SUBTRACT);
|
||||
KeyMap.emplace(SDLK_KP_PERIOD, KEY_DECIMAL);
|
||||
KeyMap.emplace(SDLK_KP_DIVIDE, KEY_DIVIDE);
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_F1, KEY_F1));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F2, KEY_F2));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F3, KEY_F3));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F4, KEY_F4));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F5, KEY_F5));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F6, KEY_F6));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F7, KEY_F7));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F8, KEY_F8));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F9, KEY_F9));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F10, KEY_F10));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F11, KEY_F11));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F12, KEY_F12));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F13, KEY_F13));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F14, KEY_F14));
|
||||
KeyMap.push_back(SKeyMap(SDLK_F15, KEY_F15));
|
||||
KeyMap.emplace(SDLK_F1, KEY_F1);
|
||||
KeyMap.emplace(SDLK_F2, KEY_F2);
|
||||
KeyMap.emplace(SDLK_F3, KEY_F3);
|
||||
KeyMap.emplace(SDLK_F4, KEY_F4);
|
||||
KeyMap.emplace(SDLK_F5, KEY_F5);
|
||||
KeyMap.emplace(SDLK_F6, KEY_F6);
|
||||
KeyMap.emplace(SDLK_F7, KEY_F7);
|
||||
KeyMap.emplace(SDLK_F8, KEY_F8);
|
||||
KeyMap.emplace(SDLK_F9, KEY_F9);
|
||||
KeyMap.emplace(SDLK_F10, KEY_F10);
|
||||
KeyMap.emplace(SDLK_F11, KEY_F11);
|
||||
KeyMap.emplace(SDLK_F12, KEY_F12);
|
||||
KeyMap.emplace(SDLK_F13, KEY_F13);
|
||||
KeyMap.emplace(SDLK_F14, KEY_F14);
|
||||
KeyMap.emplace(SDLK_F15, KEY_F15);
|
||||
// no higher F-keys
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_NUMLOCKCLEAR, KEY_NUMLOCK));
|
||||
KeyMap.push_back(SKeyMap(SDLK_SCROLLLOCK, KEY_SCROLL));
|
||||
KeyMap.push_back(SKeyMap(SDLK_LSHIFT, KEY_LSHIFT));
|
||||
KeyMap.push_back(SKeyMap(SDLK_RSHIFT, KEY_RSHIFT));
|
||||
KeyMap.push_back(SKeyMap(SDLK_LCTRL, KEY_LCONTROL));
|
||||
KeyMap.push_back(SKeyMap(SDLK_RCTRL, KEY_RCONTROL));
|
||||
KeyMap.push_back(SKeyMap(SDLK_LALT, KEY_LMENU));
|
||||
KeyMap.push_back(SKeyMap(SDLK_RALT, KEY_RMENU));
|
||||
KeyMap.emplace(SDLK_NUMLOCKCLEAR, KEY_NUMLOCK);
|
||||
KeyMap.emplace(SDLK_SCROLLLOCK, KEY_SCROLL);
|
||||
KeyMap.emplace(SDLK_LSHIFT, KEY_LSHIFT);
|
||||
KeyMap.emplace(SDLK_RSHIFT, KEY_RSHIFT);
|
||||
KeyMap.emplace(SDLK_LCTRL, KEY_LCONTROL);
|
||||
KeyMap.emplace(SDLK_RCTRL, KEY_RCONTROL);
|
||||
KeyMap.emplace(SDLK_LALT, KEY_LMENU);
|
||||
KeyMap.emplace(SDLK_RALT, KEY_RMENU);
|
||||
|
||||
KeyMap.push_back(SKeyMap(SDLK_PLUS, KEY_PLUS));
|
||||
KeyMap.push_back(SKeyMap(SDLK_COMMA, KEY_COMMA));
|
||||
KeyMap.push_back(SKeyMap(SDLK_MINUS, KEY_MINUS));
|
||||
KeyMap.push_back(SKeyMap(SDLK_PERIOD, KEY_PERIOD));
|
||||
KeyMap.emplace(SDLK_PLUS, KEY_PLUS);
|
||||
KeyMap.emplace(SDLK_COMMA, KEY_COMMA);
|
||||
KeyMap.emplace(SDLK_MINUS, KEY_MINUS);
|
||||
KeyMap.emplace(SDLK_PERIOD, KEY_PERIOD);
|
||||
|
||||
// some special keys missing
|
||||
|
||||
KeyMap.sort();
|
||||
}
|
||||
|
||||
void CIrrDeviceSDL::CCursorControl::initCursors()
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -286,6 +288,9 @@ private:
|
||||
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
|
||||
static int findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock);
|
||||
|
||||
std::variant<u32, EKEY_CODE> getScancodeFromKey(const Keycode &key) const override;
|
||||
Keycode getKeyFromScancode(const u32 scancode) const override;
|
||||
|
||||
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
|
||||
void resetReceiveTextInputEvents();
|
||||
|
||||
@ -319,28 +324,13 @@ private:
|
||||
|
||||
core::rect<s32> lastElemPos;
|
||||
|
||||
struct SKeyMap
|
||||
{
|
||||
SKeyMap() {}
|
||||
SKeyMap(s32 x11, s32 win32) :
|
||||
SDLKey(x11), Win32Key(win32)
|
||||
{
|
||||
}
|
||||
|
||||
s32 SDLKey;
|
||||
s32 Win32Key;
|
||||
|
||||
bool operator<(const SKeyMap &o) const
|
||||
{
|
||||
return SDLKey < o.SDLKey;
|
||||
}
|
||||
};
|
||||
|
||||
core::array<SKeyMap> KeyMap;
|
||||
std::unordered_map<s32, EKEY_CODE> KeyMap;
|
||||
SDL_SysWMinfo Info;
|
||||
|
||||
s32 CurrentTouchCount;
|
||||
bool IsInBackground;
|
||||
|
||||
std::unordered_set<SDL_Scancode> escapeKeys;
|
||||
};
|
||||
|
||||
} // end namespace irr
|
||||
|
@ -143,7 +143,7 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
||||
// Remember whether each key is down or up
|
||||
if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
|
||||
const KeyPress keyCode(event.KeyInput);
|
||||
if (keysListenedFor[keyCode]) {
|
||||
if (keyCode && keysListenedFor[keyCode]) {
|
||||
if (event.KeyInput.PressedDown) {
|
||||
if (!IsKeyDown(keyCode))
|
||||
keyWasPressed.set(keyCode);
|
||||
|
@ -6,15 +6,17 @@
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
#include "debug.h"
|
||||
#include "renderingengine.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/string.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include <vector>
|
||||
|
||||
struct table_key {
|
||||
const char *Name;
|
||||
std::string Name;
|
||||
irr::EKEY_CODE Key;
|
||||
wchar_t Char; // L'\0' means no character assigned
|
||||
const char *LangName; // NULL means it doesn't have a human description
|
||||
std::string LangName; // NULL means it doesn't have a human description
|
||||
};
|
||||
|
||||
#define DEFINEKEY1(x, lang) /* Irrlicht key without character */ \
|
||||
@ -30,7 +32,7 @@ struct table_key {
|
||||
|
||||
#define N_(text) text
|
||||
|
||||
static const struct table_key table[] = {
|
||||
static std::vector<table_key> table = {
|
||||
// Keys that can be reliably mapped between Char and Key
|
||||
DEFINEKEY3(0)
|
||||
DEFINEKEY3(1)
|
||||
@ -126,7 +128,7 @@ static const struct table_key table[] = {
|
||||
DEFINEKEY1(KEY_ADD, N_("Numpad +"))
|
||||
DEFINEKEY1(KEY_SEPARATOR, N_("Numpad ."))
|
||||
DEFINEKEY1(KEY_SUBTRACT, N_("Numpad -"))
|
||||
DEFINEKEY1(KEY_DECIMAL, NULL)
|
||||
DEFINEKEY1(KEY_DECIMAL, N_("Numpad ."))
|
||||
DEFINEKEY1(KEY_DIVIDE, N_("Numpad /"))
|
||||
DEFINEKEY4(1)
|
||||
DEFINEKEY4(2)
|
||||
@ -221,122 +223,177 @@ static const struct table_key table[] = {
|
||||
DEFINEKEY5("_")
|
||||
};
|
||||
|
||||
static const table_key invalid_key = {"", irr::KEY_UNKNOWN, L'\0', ""};
|
||||
|
||||
#undef N_
|
||||
|
||||
|
||||
static const table_key &lookup_keyname(const char *name)
|
||||
{
|
||||
for (const auto &table_key : table) {
|
||||
if (strcmp(table_key.Name, name) == 0)
|
||||
return table_key;
|
||||
}
|
||||
|
||||
throw UnknownKeycode(name);
|
||||
}
|
||||
|
||||
static const table_key &lookup_keykey(irr::EKEY_CODE key)
|
||||
{
|
||||
for (const auto &table_key : table) {
|
||||
if (table_key.Key == key)
|
||||
return table_key;
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "<Keycode " << (int) key << ">";
|
||||
throw UnknownKeycode(os.str().c_str());
|
||||
}
|
||||
|
||||
static const table_key &lookup_keychar(wchar_t Char)
|
||||
{
|
||||
if (Char == L'\0')
|
||||
return invalid_key;
|
||||
|
||||
for (const auto &table_key : table) {
|
||||
if (table_key.Char == Char)
|
||||
return table_key;
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "<Char " << hex_encode((char*) &Char, sizeof(wchar_t)) << ">";
|
||||
throw UnknownKeycode(os.str().c_str());
|
||||
auto newsym = wide_to_utf8(std::wstring_view(&Char, 1));
|
||||
table_key new_key {newsym, irr::KEY_KEY_CODES_COUNT, Char, newsym};
|
||||
return table.emplace_back(std::move(new_key));
|
||||
}
|
||||
|
||||
KeyPress::KeyPress(const char *name)
|
||||
static const table_key &lookup_keykey(irr::EKEY_CODE key)
|
||||
{
|
||||
if (strlen(name) == 0) {
|
||||
Key = irr::KEY_KEY_CODES_COUNT;
|
||||
Char = L'\0';
|
||||
m_name = "";
|
||||
if (!Keycode::isValid(key))
|
||||
return invalid_key;
|
||||
|
||||
for (const auto &table_key : table) {
|
||||
if (table_key.Key == key)
|
||||
return table_key;
|
||||
}
|
||||
|
||||
return invalid_key;
|
||||
}
|
||||
|
||||
static const table_key &lookup_keyname(std::string_view name)
|
||||
{
|
||||
if (name.empty())
|
||||
return invalid_key;
|
||||
|
||||
for (const auto &table_key : table) {
|
||||
if (table_key.Name == name)
|
||||
return table_key;
|
||||
}
|
||||
|
||||
auto wname = utf8_to_wide(name);
|
||||
if (wname.empty())
|
||||
return invalid_key;
|
||||
return lookup_keychar(wname[0]);
|
||||
}
|
||||
|
||||
#if USE_SDL2
|
||||
static const table_key &lookup_scancode(const u32 scancode)
|
||||
{
|
||||
auto key = RenderingEngine::get_raw_device()->getKeyFromScancode(scancode);
|
||||
return std::holds_alternative<EKEY_CODE>(key) ?
|
||||
lookup_keykey(std::get<irr::EKEY_CODE>(key)) :
|
||||
lookup_keychar(std::get<wchar_t>(key));
|
||||
}
|
||||
|
||||
static const table_key &lookup_scancode(const std::variant<u32, irr::EKEY_CODE> &scancode)
|
||||
{
|
||||
return std::holds_alternative<irr::EKEY_CODE>(scancode) ?
|
||||
lookup_keykey(std::get<irr::EKEY_CODE>(scancode)) :
|
||||
lookup_scancode(std::get<u32>(scancode));
|
||||
}
|
||||
#endif
|
||||
|
||||
KeyPress::KeyPress(std::string_view name)
|
||||
{
|
||||
#if USE_SDL2
|
||||
if (loadFromScancode(name))
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(name) <= 4) {
|
||||
// Lookup by resulting character
|
||||
int chars_read = mbtowc(&Char, name, 1);
|
||||
FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
|
||||
try {
|
||||
auto &k = lookup_keychar(Char);
|
||||
m_name = k.Name;
|
||||
Key = k.Key;
|
||||
return;
|
||||
} catch (UnknownKeycode &e) {};
|
||||
} else {
|
||||
// Lookup by name
|
||||
m_name = name;
|
||||
try {
|
||||
auto &k = lookup_keyname(name);
|
||||
Key = k.Key;
|
||||
Char = k.Char;
|
||||
return;
|
||||
} catch (UnknownKeycode &e) {};
|
||||
}
|
||||
|
||||
// It's not a known key, complain and try to do something
|
||||
Key = irr::KEY_KEY_CODES_COUNT;
|
||||
int chars_read = mbtowc(&Char, name, 1);
|
||||
FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
|
||||
m_name = "";
|
||||
warningstream << "KeyPress: Unknown key '" << name
|
||||
<< "', falling back to first char." << std::endl;
|
||||
const auto &key = lookup_keyname(name);
|
||||
Keycode keycode(key.Key, key.Char);
|
||||
scancode = RenderingEngine::get_raw_device()->getScancodeFromKey(keycode);
|
||||
#else
|
||||
const auto &key = lookup_keyname(name);
|
||||
Key = key.Key;
|
||||
Char = key.Char;
|
||||
#endif
|
||||
}
|
||||
|
||||
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
|
||||
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in)
|
||||
#if USE_SDL2
|
||||
{
|
||||
if (prefer_character)
|
||||
Key = irr::KEY_KEY_CODES_COUNT;
|
||||
if (in.SystemKeyCode)
|
||||
scancode.emplace<u32>(in.SystemKeyCode);
|
||||
else
|
||||
Key = in.Key;
|
||||
Char = in.Char;
|
||||
|
||||
try {
|
||||
if (valid_kcode(Key))
|
||||
m_name = lookup_keykey(Key).Name;
|
||||
else
|
||||
m_name = lookup_keychar(Char).Name;
|
||||
} catch (UnknownKeycode &e) {
|
||||
m_name.clear();
|
||||
};
|
||||
scancode.emplace<irr::EKEY_CODE>(in.Key);
|
||||
}
|
||||
#else
|
||||
: Key(in.Key), Char(in.Char ? in.Char : lookup_keykey(in.Key).Char) {}
|
||||
#endif
|
||||
|
||||
const char *KeyPress::sym() const
|
||||
#if USE_SDL2
|
||||
std::string KeyPress::formatScancode() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
if (auto pv = std::get_if<u32>(&scancode))
|
||||
return *pv == 0 ? "" : "<" + std::to_string(*pv) + ">";
|
||||
return lookup_keykey(std::get<irr::EKEY_CODE>(scancode)).Name;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *KeyPress::name() const
|
||||
std::string KeyPress::sym(const bool force_scancode) const
|
||||
{
|
||||
if (m_name.empty())
|
||||
return "";
|
||||
const char *ret;
|
||||
if (valid_kcode(Key))
|
||||
ret = lookup_keykey(Key).LangName;
|
||||
else
|
||||
ret = lookup_keychar(Char).LangName;
|
||||
return ret ? ret : "<Unnamed key>";
|
||||
#if USE_SDL2
|
||||
if (force_scancode)
|
||||
return formatScancode();
|
||||
const auto &name = lookup_scancode(scancode).Name;
|
||||
if (!name.empty())
|
||||
return name;
|
||||
return formatScancode();
|
||||
#else
|
||||
if (Keycode::isValid(Key))
|
||||
if (const auto &sym = lookup_keykey(Key).Name; !sym.empty())
|
||||
return sym;
|
||||
return lookup_keychar(Char).Name;
|
||||
#endif
|
||||
}
|
||||
|
||||
const KeyPress EscapeKey("KEY_ESCAPE");
|
||||
std::string KeyPress::name() const
|
||||
{
|
||||
#if USE_SDL2
|
||||
const auto &name = lookup_scancode(scancode).LangName;
|
||||
if (!name.empty())
|
||||
return name;
|
||||
return formatScancode();
|
||||
#else
|
||||
return (Keycode::isValid(Key) ? lookup_keykey(Key) : lookup_keychar(Char)).LangName;
|
||||
#endif
|
||||
}
|
||||
|
||||
const KeyPress LMBKey("KEY_LBUTTON");
|
||||
const KeyPress MMBKey("KEY_MBUTTON");
|
||||
const KeyPress RMBKey("KEY_RBUTTON");
|
||||
irr::EKEY_CODE KeyPress::getKeycode() const
|
||||
{
|
||||
#if USE_SDL2
|
||||
return lookup_scancode(scancode).Key;
|
||||
#else
|
||||
return Key;
|
||||
#endif
|
||||
}
|
||||
|
||||
wchar_t KeyPress::getKeychar() const
|
||||
{
|
||||
#if USE_SDL2
|
||||
return lookup_scancode(scancode).Char;
|
||||
#else
|
||||
return Char;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_SDL2
|
||||
bool KeyPress::loadFromScancode(std::string_view name)
|
||||
{
|
||||
if (name.size() < 2 || name[0] != '<' || name.back() != '>')
|
||||
return false;
|
||||
char *p;
|
||||
const auto code = strtoul(name.data()+1, &p, 10);
|
||||
if (p != name.data() + name.size() - 1)
|
||||
return false;
|
||||
scancode.emplace<u32>(code);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unordered_map<std::string, KeyPress> KeyPress::specialKeyCache;
|
||||
const KeyPress &KeyPress::getSpecialKey(const std::string &name)
|
||||
{
|
||||
auto &key = specialKeyCache[name];
|
||||
if (!key)
|
||||
key = KeyPress(name);
|
||||
return key;
|
||||
}
|
||||
|
||||
/*
|
||||
Key config
|
||||
@ -345,14 +402,14 @@ const KeyPress RMBKey("KEY_RBUTTON");
|
||||
// A simple cache for quicker lookup
|
||||
static std::unordered_map<std::string, KeyPress> g_key_setting_cache;
|
||||
|
||||
const KeyPress &getKeySetting(const char *settingname)
|
||||
const KeyPress &getKeySetting(const std::string &settingname)
|
||||
{
|
||||
auto n = g_key_setting_cache.find(settingname);
|
||||
if (n != g_key_setting_cache.end())
|
||||
return n->second;
|
||||
|
||||
auto &ref = g_key_setting_cache[settingname];
|
||||
ref = g_settings->get(settingname).c_str();
|
||||
ref = std::string_view(g_settings->get(settingname));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@ -360,8 +417,3 @@ void clearKeyCache()
|
||||
{
|
||||
g_key_setting_cache.clear();
|
||||
}
|
||||
|
||||
irr::EKEY_CODE keyname_to_keycode(const char *name)
|
||||
{
|
||||
return lookup_keyname(name).Key;
|
||||
}
|
||||
|
@ -4,62 +4,86 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "exceptions.h"
|
||||
#include "irrlichttypes.h"
|
||||
#include "cmake_config.h" // USE_SDL2
|
||||
#include <Keycodes.h>
|
||||
#include <IEventReceiver.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
class UnknownKeycode : public BaseException
|
||||
{
|
||||
public:
|
||||
UnknownKeycode(const char *s) :
|
||||
BaseException(s) {};
|
||||
};
|
||||
|
||||
/* A key press, consisting of either an Irrlicht keycode
|
||||
or an actual char */
|
||||
|
||||
/* A key press, consisting of a scancode or a keycode */
|
||||
class KeyPress
|
||||
{
|
||||
public:
|
||||
KeyPress() = default;
|
||||
|
||||
KeyPress(const char *name);
|
||||
KeyPress(std::string_view name);
|
||||
|
||||
KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character = false);
|
||||
KeyPress(const irr::SEvent::SKeyInput &in);
|
||||
|
||||
std::string sym(const bool force_scancode = false) const;
|
||||
std::string name() const;
|
||||
|
||||
irr::EKEY_CODE getKeycode() const;
|
||||
wchar_t getKeychar() const;
|
||||
|
||||
//TODO: drop this once we fully switch to SDL
|
||||
u32 getScancode() const
|
||||
{
|
||||
#if USE_SDL2
|
||||
if (auto pv = std::get_if<u32>(&scancode))
|
||||
return *pv;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator==(const KeyPress &o) const
|
||||
{
|
||||
return (Char > 0 && Char == o.Char) || (valid_kcode(Key) && Key == o.Key);
|
||||
#if USE_SDL2
|
||||
return scancode == o.scancode;
|
||||
#else
|
||||
return (Char > 0 && Char == o.Char) || (Keycode::isValid(Key) && Key == o.Key);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *sym() const;
|
||||
const char *name() const;
|
||||
|
||||
protected:
|
||||
static bool valid_kcode(irr::EKEY_CODE k)
|
||||
operator bool() const
|
||||
{
|
||||
return k > 0 && k < irr::KEY_KEY_CODES_COUNT;
|
||||
#if USE_SDL2
|
||||
return std::holds_alternative<irr::EKEY_CODE>(scancode) ?
|
||||
Keycode::isValid(std::get<irr::EKEY_CODE>(scancode)) :
|
||||
std::get<u32>(scancode) != 0;
|
||||
#else
|
||||
return Char > 0 || Keycode::isValid(Key);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const KeyPress &getSpecialKey(const std::string &name);
|
||||
|
||||
private:
|
||||
#if USE_SDL2
|
||||
bool loadFromScancode(std::string_view name);
|
||||
std::string formatScancode() const;
|
||||
|
||||
std::variant<u32, irr::EKEY_CODE> scancode = irr::KEY_UNKNOWN;
|
||||
#else
|
||||
irr::EKEY_CODE Key = irr::KEY_KEY_CODES_COUNT;
|
||||
wchar_t Char = L'\0';
|
||||
std::string m_name = "";
|
||||
#endif
|
||||
|
||||
static std::unordered_map<std::string, KeyPress> specialKeyCache;
|
||||
};
|
||||
|
||||
// Global defines for convenience
|
||||
|
||||
extern const KeyPress EscapeKey;
|
||||
|
||||
extern const KeyPress LMBKey;
|
||||
extern const KeyPress MMBKey; // Middle Mouse Button
|
||||
extern const KeyPress RMBKey;
|
||||
// This implementation defers creation of the objects to make sure that the
|
||||
// IrrlichtDevice is initialized.
|
||||
#define EscapeKey KeyPress::getSpecialKey("KEY_ESCAPE")
|
||||
#define LMBKey KeyPress::getSpecialKey("KEY_LBUTTON")
|
||||
#define MMBKey KeyPress::getSpecialKey("KEY_MBUTTON") // Middle Mouse Button
|
||||
#define RMBKey KeyPress::getSpecialKey("KEY_RBUTTON")
|
||||
|
||||
// Key configuration getter
|
||||
const KeyPress &getKeySetting(const char *settingname);
|
||||
const KeyPress &getKeySetting(const std::string &settingname);
|
||||
|
||||
// Clear fast lookup cache
|
||||
void clearKeyCache();
|
||||
|
||||
irr::EKEY_CODE keyname_to_keycode(const char *name);
|
||||
|
@ -82,6 +82,7 @@ void set_default_settings()
|
||||
|
||||
// Client
|
||||
settings->setDefault("address", "");
|
||||
settings->setDefault("remote_port", "30000");
|
||||
settings->setDefault("enable_sound", "true");
|
||||
#if defined(__unix__) && !defined(__APPLE__) && !defined (__ANDROID__)
|
||||
// On Linux+X11 (not Linux+Wayland or Linux+XWayland), I've encountered a bug
|
||||
@ -129,7 +130,43 @@ void set_default_settings()
|
||||
settings->setDefault("chat_weblink_color", "#8888FF");
|
||||
|
||||
// Keymap
|
||||
settings->setDefault("remote_port", "30000");
|
||||
#if USE_SDL2
|
||||
settings->setDefault("save_keys_as_scancodes", "true");
|
||||
settings->setDefault("keymap_forward", "<26>");
|
||||
settings->setDefault("keymap_autoforward", "");
|
||||
settings->setDefault("keymap_backward", "<22>");
|
||||
settings->setDefault("keymap_left", "<4>");
|
||||
settings->setDefault("keymap_right", "<7>");
|
||||
settings->setDefault("keymap_jump", "<44>");
|
||||
settings->setDefault("keymap_sneak", "KEY_LSHIFT");
|
||||
settings->setDefault("keymap_dig", "KEY_LBUTTON");
|
||||
settings->setDefault("keymap_place", "KEY_RBUTTON");
|
||||
settings->setDefault("keymap_drop", "<20>");
|
||||
settings->setDefault("keymap_zoom", "<29>");
|
||||
settings->setDefault("keymap_inventory", "<12>");
|
||||
settings->setDefault("keymap_aux1", "<8>");
|
||||
settings->setDefault("keymap_chat", "<23>");
|
||||
settings->setDefault("keymap_cmd", "<56>");
|
||||
settings->setDefault("keymap_cmd_local", "<55>");
|
||||
settings->setDefault("keymap_minimap", "<25>");
|
||||
settings->setDefault("keymap_console", "KEY_F10");
|
||||
|
||||
// See https://github.com/minetest/minetest/issues/12792
|
||||
settings->setDefault("keymap_rangeselect", has_touch ? "<21>" : "");
|
||||
|
||||
settings->setDefault("keymap_freemove", "<14>");
|
||||
settings->setDefault("keymap_pitchmove", "");
|
||||
settings->setDefault("keymap_fastmove", "<13>");
|
||||
settings->setDefault("keymap_noclip", "<11>");
|
||||
settings->setDefault("keymap_hotbar_next", "<17>");
|
||||
settings->setDefault("keymap_hotbar_previous", "<5>");
|
||||
settings->setDefault("keymap_mute", "<16>");
|
||||
settings->setDefault("keymap_camera_mode", "<6>");
|
||||
settings->setDefault("keymap_increase_viewing_range_min", "<46>");
|
||||
settings->setDefault("keymap_decrease_viewing_range_min", "<45>");
|
||||
#else
|
||||
// TODO: Remove this once we fully switch to SDL2
|
||||
settings->setDefault("save_keys_as_scancodes", "false");
|
||||
settings->setDefault("keymap_forward", "KEY_KEY_W");
|
||||
settings->setDefault("keymap_autoforward", "");
|
||||
settings->setDefault("keymap_backward", "KEY_KEY_S");
|
||||
@ -159,6 +196,11 @@ void set_default_settings()
|
||||
settings->setDefault("keymap_hotbar_next", "KEY_KEY_N");
|
||||
settings->setDefault("keymap_hotbar_previous", "KEY_KEY_B");
|
||||
settings->setDefault("keymap_mute", "KEY_KEY_M");
|
||||
settings->setDefault("keymap_camera_mode", "KEY_KEY_C");
|
||||
settings->setDefault("keymap_increase_viewing_range_min", "+");
|
||||
settings->setDefault("keymap_decrease_viewing_range_min", "-");
|
||||
#endif
|
||||
|
||||
settings->setDefault("keymap_increase_volume", "");
|
||||
settings->setDefault("keymap_decrease_volume", "");
|
||||
settings->setDefault("keymap_cinematic", "");
|
||||
@ -173,11 +215,8 @@ void set_default_settings()
|
||||
#endif
|
||||
settings->setDefault("keymap_toggle_debug", "KEY_F5");
|
||||
settings->setDefault("keymap_toggle_profiler", "KEY_F6");
|
||||
settings->setDefault("keymap_camera_mode", "KEY_KEY_C");
|
||||
settings->setDefault("keymap_screenshot", "KEY_F12");
|
||||
settings->setDefault("keymap_fullscreen", "KEY_F11");
|
||||
settings->setDefault("keymap_increase_viewing_range_min", "+");
|
||||
settings->setDefault("keymap_decrease_viewing_range_min", "-");
|
||||
settings->setDefault("keymap_slot1", "KEY_KEY_1");
|
||||
settings->setDefault("keymap_slot2", "KEY_KEY_2");
|
||||
settings->setDefault("keymap_slot3", "KEY_KEY_3");
|
||||
|
@ -66,6 +66,7 @@ enum
|
||||
GUI_ID_CB_AUX1_DESCENDS,
|
||||
GUI_ID_CB_DOUBLETAP_JUMP,
|
||||
GUI_ID_CB_AUTOJUMP,
|
||||
GUI_ID_SAVE_AS_SCANCODES,
|
||||
};
|
||||
|
||||
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
|
||||
@ -93,7 +94,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
|
||||
removeAllChildren();
|
||||
key_used_text = nullptr;
|
||||
|
||||
ScalingInfo info = getScalingInfo(screensize, v2u32(835, 430));
|
||||
ScalingInfo info = getScalingInfo(screensize, v2u32(835, 455));
|
||||
const float s = info.scale;
|
||||
DesiredRect = info.rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
@ -177,6 +178,21 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
|
||||
offset += v2s32(0, 25);
|
||||
}
|
||||
|
||||
#if USE_SDL2
|
||||
{
|
||||
s32 option_x = offset.X;
|
||||
s32 option_y = offset.Y + 5 * s;
|
||||
u32 option_w = 280 * s;
|
||||
{
|
||||
core::rect<s32> rect(0, 0, option_w, 30 * s);
|
||||
rect += topleft + v2s32(option_x, option_y);
|
||||
Environment->addCheckBox(g_settings->getBool("save_keys_as_scancodes"), rect, this,
|
||||
GUI_ID_SAVE_AS_SCANCODES, wstrgettext("Save keybindings as scancodes").c_str());
|
||||
}
|
||||
offset += v2s32(0, 25 * s);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 100 * s, 30 * s);
|
||||
rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s);
|
||||
@ -206,15 +222,7 @@ void GUIKeyChangeMenu::drawMenu()
|
||||
|
||||
bool GUIKeyChangeMenu::acceptInput()
|
||||
{
|
||||
for (key_setting *k : key_settings) {
|
||||
std::string default_key;
|
||||
Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
|
||||
|
||||
if (k->key.sym() != default_key)
|
||||
g_settings->set(k->setting_name, k->key.sym());
|
||||
else
|
||||
g_settings->remove(k->setting_name);
|
||||
}
|
||||
bool save_as_scancodes = g_settings->getBool("save_keys_as_scancodes");
|
||||
|
||||
{
|
||||
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
|
||||
@ -231,6 +239,25 @@ bool GUIKeyChangeMenu::acceptInput()
|
||||
if(e && e->getType() == gui::EGUIET_CHECK_BOX)
|
||||
g_settings->setBool("autojump", ((gui::IGUICheckBox*)e)->isChecked());
|
||||
}
|
||||
{
|
||||
const auto *e = getElementFromId(GUI_ID_SAVE_AS_SCANCODES);
|
||||
if(e && e->getType() == gui::EGUIET_CHECK_BOX) {
|
||||
save_as_scancodes = dynamic_cast<const gui::IGUICheckBox*>(e)->isChecked();
|
||||
g_settings->setBool("save_keys_as_scancodes", save_as_scancodes);
|
||||
}
|
||||
}
|
||||
|
||||
for (key_setting *k : key_settings) {
|
||||
std::string default_key;
|
||||
Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
|
||||
|
||||
auto keysym = k->key.sym(save_as_scancodes);
|
||||
|
||||
if (keysym != default_key)
|
||||
g_settings->set(k->setting_name, keysym);
|
||||
else
|
||||
g_settings->remove(k->setting_name);
|
||||
}
|
||||
|
||||
clearKeyCache();
|
||||
|
||||
@ -253,8 +280,7 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
|
||||
if (event.EventType == EET_KEY_INPUT_EVENT && active_key
|
||||
&& event.KeyInput.PressedDown) {
|
||||
|
||||
bool prefer_character = shift_down;
|
||||
KeyPress kp(event.KeyInput, prefer_character);
|
||||
KeyPress kp(event.KeyInput);
|
||||
|
||||
if (event.KeyInput.Key == irr::KEY_DELETE)
|
||||
kp = KeyPress(""); // To erase key settings
|
||||
@ -270,7 +296,7 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
|
||||
|
||||
// Display Key already in use message
|
||||
bool key_in_use = false;
|
||||
if (strcmp(kp.sym(), "") != 0) {
|
||||
if (kp) {
|
||||
for (key_setting *ks : key_settings) {
|
||||
if (ks != active_key && ks->key == kp) {
|
||||
key_in_use = true;
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "porting.h"
|
||||
#include "settings.h"
|
||||
#include "client/guiscalingfilter.h"
|
||||
#include "client/keycode.h"
|
||||
#include "client/renderingengine.h"
|
||||
#include "client/texturesource.h"
|
||||
#include "util/numeric.h"
|
||||
@ -29,15 +28,16 @@
|
||||
|
||||
TouchControls *g_touchcontrols;
|
||||
|
||||
void TouchControls::emitKeyboardEvent(EKEY_CODE keycode, bool pressed)
|
||||
void TouchControls::emitKeyboardEvent(const KeyPress &key, bool pressed)
|
||||
{
|
||||
SEvent e{};
|
||||
e.EventType = EET_KEY_INPUT_EVENT;
|
||||
e.KeyInput.Key = keycode;
|
||||
e.KeyInput.Control = false;
|
||||
e.KeyInput.Shift = false;
|
||||
e.KeyInput.Char = 0;
|
||||
e.KeyInput.PressedDown = pressed;
|
||||
e.EventType = EET_KEY_INPUT_EVENT;
|
||||
e.KeyInput.Key = key.getKeycode();
|
||||
e.KeyInput.Control = false;
|
||||
e.KeyInput.Shift = false;
|
||||
e.KeyInput.Char = key.getKeychar();
|
||||
e.KeyInput.SystemKeyCode = key.getScancode();
|
||||
e.KeyInput.PressedDown = pressed;
|
||||
m_receiver->OnEvent(e);
|
||||
}
|
||||
|
||||
@ -52,10 +52,10 @@ void TouchControls::loadButtonTexture(IGUIImage *gui_button, const std::string &
|
||||
|
||||
void TouchControls::buttonEmitAction(button_info &btn, bool action)
|
||||
{
|
||||
if (btn.keycode == KEY_UNKNOWN)
|
||||
if (!btn.keypress)
|
||||
return;
|
||||
|
||||
emitKeyboardEvent(btn.keycode, action);
|
||||
emitKeyboardEvent(btn.keypress, action);
|
||||
|
||||
if (action) {
|
||||
if (btn.toggleable == button_info::FIRST_TEXTURE) {
|
||||
@ -131,12 +131,11 @@ bool TouchControls::buttonsStep(std::vector<button_info> &buttons, float dtime)
|
||||
return has_pointers;
|
||||
}
|
||||
|
||||
static EKEY_CODE id_to_keycode(touch_gui_button_id id)
|
||||
static const KeyPress &id_to_keypress(touch_gui_button_id id)
|
||||
{
|
||||
EKEY_CODE code;
|
||||
// ESC isn't part of the keymap.
|
||||
if (id == exit_id)
|
||||
return KEY_ESCAPE;
|
||||
return EscapeKey;
|
||||
|
||||
std::string key = "";
|
||||
switch (id) {
|
||||
@ -189,15 +188,7 @@ static EKEY_CODE id_to_keycode(touch_gui_button_id id)
|
||||
break;
|
||||
}
|
||||
assert(!key.empty());
|
||||
std::string resolved = g_settings->get("keymap_" + key);
|
||||
try {
|
||||
code = keyname_to_keycode(resolved.c_str());
|
||||
} catch (UnknownKeycode &e) {
|
||||
code = KEY_UNKNOWN;
|
||||
warningstream << "TouchControls: Unknown key '" << resolved
|
||||
<< "' for '" << key << "', hiding button." << std::endl;
|
||||
}
|
||||
return code;
|
||||
return getKeySetting("keymap_" + key);
|
||||
}
|
||||
|
||||
|
||||
@ -323,7 +314,7 @@ bool TouchControls::mayAddButton(touch_gui_button_id id)
|
||||
return false;
|
||||
if (id == aux1_id && m_joystick_triggers_aux1)
|
||||
return false;
|
||||
if (id != overflow_id && id_to_keycode(id) == KEY_UNKNOWN)
|
||||
if (id != overflow_id && !id_to_keypress(id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -336,7 +327,7 @@ void TouchControls::addButton(std::vector<button_info> &buttons, touch_gui_butto
|
||||
loadButtonTexture(btn_gui_button, image);
|
||||
|
||||
button_info &btn = buttons.emplace_back();
|
||||
btn.keycode = id_to_keycode(id);
|
||||
btn.keypress = id_to_keypress(id);
|
||||
btn.gui_button = grab_gui_element<IGUIImage>(btn_gui_button);
|
||||
}
|
||||
|
||||
@ -601,7 +592,7 @@ void TouchControls::translateEvent(const SEvent &event)
|
||||
void TouchControls::applyJoystickStatus()
|
||||
{
|
||||
if (m_joystick_triggers_aux1) {
|
||||
auto key = id_to_keycode(aux1_id);
|
||||
auto key = id_to_keypress(aux1_id);
|
||||
emitKeyboardEvent(key, false);
|
||||
if (m_joystick_status_aux1)
|
||||
emitKeyboardEvent(key, true);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "itemdef.h"
|
||||
#include "touchscreenlayout.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include "client/keycode.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
@ -57,7 +58,7 @@ enum class TapState
|
||||
struct button_info
|
||||
{
|
||||
float repeat_counter;
|
||||
EKEY_CODE keycode;
|
||||
KeyPress keypress;
|
||||
std::vector<size_t> pointer_ids;
|
||||
std::shared_ptr<IGUIImage> gui_button = nullptr;
|
||||
|
||||
@ -187,7 +188,7 @@ private:
|
||||
// for its buttons. We only want static image display, not interactivity,
|
||||
// from Irrlicht.
|
||||
|
||||
void emitKeyboardEvent(EKEY_CODE keycode, bool pressed);
|
||||
void emitKeyboardEvent(const KeyPress &keycode, bool pressed);
|
||||
|
||||
void loadButtonTexture(IGUIImage *gui_button, const std::string &path);
|
||||
void buttonEmitAction(button_info &btn, bool action);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include "exceptions.h"
|
||||
#include "client/keycode.h"
|
||||
#include "client/renderingengine.h" // scancode<->keycode conversion
|
||||
|
||||
class TestKeycode : public TestBase {
|
||||
public:
|
||||
@ -24,42 +25,67 @@ static TestKeycode g_test_instance;
|
||||
|
||||
void TestKeycode::runTests(IGameDef *gamedef)
|
||||
{
|
||||
// TODO: Restore unittests once we fully switch to SDL.
|
||||
// Old test cases are kept for completeness.
|
||||
/*
|
||||
TEST(testCreateFromString);
|
||||
TEST(testCreateFromSKeyInput);
|
||||
TEST(testCompare);
|
||||
*/
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define UASSERTEQ_STR(one, two) UASSERT(strcmp(one, two) == 0)
|
||||
#define UASSERTEQ_STR(one, two) UASSERT(one == two)
|
||||
#define UASSERT_HAS_NAME(k) UASSERT(!k.name().empty())
|
||||
|
||||
void TestKeycode::testCreateFromString()
|
||||
{
|
||||
KeyPress k;
|
||||
|
||||
k = KeyPress();
|
||||
UASSERTEQ_STR(k.sym(true), "");
|
||||
|
||||
k = KeyPress("<0>");
|
||||
UASSERTEQ_STR(k.sym(true), "");
|
||||
|
||||
k = KeyPress("<20>");
|
||||
UASSERTEQ_STR(k.sym(true), "<20>");
|
||||
|
||||
// Character key, from char
|
||||
k = KeyPress("R");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_R");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0); // should have human description
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Character key, from identifier
|
||||
k = KeyPress("KEY_KEY_B");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_B");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Non-Character key, from identifier
|
||||
k = KeyPress("KEY_UP");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_UP");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
k = KeyPress("KEY_F6");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_F6");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Irrlicht-unknown key, from char
|
||||
k = KeyPress("/");
|
||||
UASSERTEQ_STR(k.sym(), "/");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT_HAS_NAME(k);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
static u32 toScancode(Args... args)
|
||||
{
|
||||
#if USE_SDL2
|
||||
if (const auto &scancode = RenderingEngine::get_raw_device()->getScancodeFromKey(Keycode(args...));
|
||||
const auto &pv = std::get_if<u32>(&scancode))
|
||||
return *pv;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TestKeycode::testCreateFromSKeyInput()
|
||||
@ -67,48 +93,52 @@ void TestKeycode::testCreateFromSKeyInput()
|
||||
KeyPress k;
|
||||
irr::SEvent::SKeyInput in;
|
||||
|
||||
in.SystemKeyCode = 20;
|
||||
k = KeyPress(in);
|
||||
UASSERTEQ_STR(k.sym(true), "<20>");
|
||||
|
||||
// Character key
|
||||
in.Key = irr::KEY_KEY_3;
|
||||
in.Char = L'3';
|
||||
in.SystemKeyCode = toScancode(irr::KEY_KEY_3, L'3');
|
||||
k = KeyPress(in);
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_3");
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Non-Character key
|
||||
in.Key = irr::KEY_RSHIFT;
|
||||
in.Char = L'\0';
|
||||
in.SystemKeyCode = toScancode(irr::KEY_RSHIFT, L'\0');
|
||||
k = KeyPress(in);
|
||||
UASSERTEQ_STR(k.sym(), "KEY_RSHIFT");
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Irrlicht-unknown key
|
||||
in.Key = irr::KEY_KEY_CODES_COUNT;
|
||||
in.Char = L'?';
|
||||
in.SystemKeyCode = toScancode(KEY_KEY_CODES_COUNT, L'?');
|
||||
k = KeyPress(in);
|
||||
UASSERTEQ_STR(k.sym(), "?");
|
||||
|
||||
// prefer_character mode
|
||||
in.Key = irr::KEY_COMMA;
|
||||
in.Char = L'G';
|
||||
k = KeyPress(in, true);
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_G");
|
||||
UASSERT_HAS_NAME(k);
|
||||
}
|
||||
|
||||
void TestKeycode::testCompare()
|
||||
{
|
||||
// "Empty" key
|
||||
UASSERT(KeyPress() == KeyPress("<0>"));
|
||||
|
||||
irr::SEvent::SKeyInput in;
|
||||
in.SystemKeyCode = 20;
|
||||
UASSERT(KeyPress(in) == KeyPress("<20>"));
|
||||
|
||||
// Basic comparison
|
||||
UASSERT(KeyPress("5") == KeyPress("KEY_KEY_5"));
|
||||
UASSERT(!(KeyPress("5") == KeyPress("KEY_NUMPAD5")));
|
||||
|
||||
// Matching char suffices
|
||||
// note: This is a real-world example, Irrlicht maps XK_equal to irr::KEY_PLUS on Linux
|
||||
irr::SEvent::SKeyInput in;
|
||||
// TODO: Is this still relevant for scancodes?
|
||||
in.Key = irr::KEY_PLUS;
|
||||
in.Char = L'=';
|
||||
UASSERT(KeyPress("=") == KeyPress(in));
|
||||
|
||||
// Matching keycode suffices
|
||||
irr::SEvent::SKeyInput in2;
|
||||
in.Key = in2.Key = irr::KEY_OEM_CLEAR;
|
||||
in.Char = L'\0';
|
||||
in2.Char = L';';
|
||||
in.SystemKeyCode = toScancode(irr::KEY_OEM_CLEAR, L'\0');
|
||||
in2.SystemKeyCode = toScancode(irr::KEY_OEM_CLEAR, L';');
|
||||
UASSERT(KeyPress(in) == KeyPress(in2));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user