Skip to content

Commit

Permalink
Use std::variant for scancode to properly handle mouse keys
Browse files Browse the repository at this point in the history
  • Loading branch information
y5nw committed Dec 13, 2024
1 parent aed7338 commit f52baf2
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 52 deletions.
24 changes: 9 additions & 15 deletions irr/include/IrrlichtDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "IrrCompileConfig.h"
#include "position2d.h"
#include "SColor.h" // video::ECOLOR_FORMAT
#include <variant>

namespace irr
{
Expand Down Expand Up @@ -346,26 +347,19 @@ class IrrlichtDevice : public virtual IReferenceCounted
// This is a trivial (near-identity) mapping for converting between scancodes and keycodes for devices that do
// not implement this.

#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) || USE_SDL2
//! Get the scancode of the corresponding keycode.
virtual u32 getScancodeFromKey(const Keycode &key) const
{
if (const auto *keycode = std::get_if<EKEY_CODE>(&key))
// treat KEY_UNKNOWN and KEY_KEY_CODES_COUNT as the same and return 0.
return Keycode::isValid(*keycode) ? *keycode : 0;
const auto keychar = std::get<wchar_t>(key);
return keychar == 0 ? 0 : KEY_KEY_CODES_COUNT + keychar;
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
{
Keycode key;
if (scancode < KEY_KEY_CODES_COUNT)
key.emplace<EKEY_CODE>((EKEY_CODE)scancode);
else
key.emplace<wchar_t>(scancode - KEY_KEY_CODES_COUNT);
return key;
virtual Keycode getKeyFromScancode(const u32 scancode) const {
return Keycode(KEY_UNKNOWN, (wchar_t)scancode);
}
#endif
};

} // end namespace irr
29 changes: 13 additions & 16 deletions irr/src/CIrrDeviceSDL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "COSOperator.h"
#include <cstdio>
#include <cstdlib>
#include <unordered_set>
#include "SIrrCreationParameters.h"
#include <SDL_video.h>

Expand All @@ -27,10 +28,13 @@

#include "CSDLManager.h"

// Since SDL doesn't have mouse keys as keycodes we create artificial entries.
#define FAKE_MOUSEKEY_MASK ((1<<30)|(1<<29))
#define FAKE_MOUSEKEY(x) (FAKE_MOUSEKEY_MASK | (x))
#define IS_FAKE_MOUSEKEY(x) (((x) & FAKE_MOUSEKEY_MASK) == FAKE_MOUSEKEY_MASK)
// 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;

Expand Down Expand Up @@ -225,27 +229,27 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtK
}
}

u32 CIrrDeviceSDL::getScancodeFromKey(const Keycode &key) const
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;
for (const auto &entry: KeyMap) {
if (entry.second == *keycode) {
keynum = entry.first;
break;
}
}
if (IS_FAKE_MOUSEKEY(keynum))
return keynum;
} else {
keynum = std::get<wchar_t>(key);
}
return SDL_GetScancodeFromKey(keynum);
return (u32)SDL_GetScancodeFromKey(keynum);
}

Keycode CIrrDeviceSDL::getKeyFromScancode(const u32 scancode) const
{
auto keycode = IS_FAKE_MOUSEKEY(scancode) ? scancode : SDL_GetKeyFromScancode((SDL_Scancode)scancode);
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);
Expand Down Expand Up @@ -1478,13 +1482,6 @@ void CIrrDeviceSDL::createKeyMap()
KeyMap.emplace(SDLK_PERIOD, KEY_PERIOD);

// some special keys missing

// fake mouse keys for SDL since these don't have scancodes or keycodes.
KeyMap.emplace(FAKE_MOUSEKEY(SDL_BUTTON_LEFT), KEY_LBUTTON);
KeyMap.emplace(FAKE_MOUSEKEY(SDL_BUTTON_MIDDLE), KEY_MBUTTON);
KeyMap.emplace(FAKE_MOUSEKEY(SDL_BUTTON_RIGHT), KEY_RBUTTON);
KeyMap.emplace(FAKE_MOUSEKEY(SDL_BUTTON_X1), KEY_XBUTTON1);
KeyMap.emplace(FAKE_MOUSEKEY(SDL_BUTTON_X2), KEY_XBUTTON2);
}

void CIrrDeviceSDL::CCursorControl::initCursors()
Expand Down
4 changes: 2 additions & 2 deletions irr/src/CIrrDeviceSDL.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ class CIrrDeviceSDL : public CIrrDeviceStub
// 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);

virtual u32 getScancodeFromKey(const Keycode &key) const override;
virtual Keycode getKeyFromScancode(const u32 scancode) const override;
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();
Expand Down
34 changes: 26 additions & 8 deletions src/client/keycode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,13 @@ static const table_key &lookup_scancode(const u32 scancode)
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)
Expand All @@ -297,18 +304,30 @@ KeyPress::KeyPress(std::string_view name)
#endif
}

KeyPress::KeyPress(const irr::SEvent::SKeyInput &in) :
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in)
#if USE_SDL2
scancode(in.SystemKeyCode) {}
{
if (in.SystemKeyCode)
scancode.emplace<u32>(in.SystemKeyCode);
else
scancode.emplace<irr::EKEY_CODE>(in.Key);
}
#else
Key(in.Key), Char(in.Char ? in.Char : lookup_keykey(in.Key).Char) {}
: Key(in.Key), Char(in.Char ? in.Char : lookup_keykey(in.Key).Char) {}
#endif

#if USE_SDL2
std::string KeyPress::formatScancode() const
{
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

std::string KeyPress::sym(const bool force_scancode) const
{
#if USE_SDL2
if (scancode == 0)
return "";
if (force_scancode)
return formatScancode();
const auto &name = lookup_scancode(scancode).Name;
Expand All @@ -327,8 +346,7 @@ std::string KeyPress::name() const
{
#if USE_SDL2
const auto &name = lookup_scancode(scancode).LangName;
auto table_key = lookup_scancode(scancode);
if (!name.empty() || scancode == 0)
if (!name.empty())
return name;
return formatScancode();
#else
Expand Down Expand Up @@ -363,7 +381,7 @@ bool KeyPress::loadFromScancode(std::string_view name)
const auto code = strtoul(name.data()+1, &p, 10);
if (p != name.data() + name.size() - 1)
return false;
scancode = code;
scancode.emplace<u32>(code);
return true;
}
#endif
Expand Down
19 changes: 9 additions & 10 deletions src/client/keycode.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <IEventReceiver.h>
#include <string>
#include <unordered_map>
#include <variant>

/* A key press, consisting of a scancode or a keycode */
class KeyPress
Expand All @@ -31,10 +32,10 @@ class KeyPress
u32 getScancode() const
{
#if USE_SDL2
return scancode;
#else
return 0;
if (auto pv = std::get_if<u32>(&scancode))
return *pv;
#endif
return 0;
}

irr::SEvent toKeyEvent(bool pressedDown = false) const
Expand All @@ -57,7 +58,9 @@ class KeyPress
operator bool() const
{
#if USE_SDL2
return scancode != 0;
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
Expand All @@ -68,13 +71,9 @@ class KeyPress
private:
#if USE_SDL2
bool loadFromScancode(std::string_view name);
std::string formatScancode() const;

inline std::string formatScancode() const
{
return "<" + std::to_string(scancode) + ">";
}

u32 scancode = 0;
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';
Expand Down
7 changes: 6 additions & 1 deletion src/unittest/test_keycode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ void TestKeycode::testCreateFromString()
template<typename ...Args>
static u32 toScancode(Args... args)
{
return RenderingEngine::get_raw_device()->getScancodeFromKey(Keycode(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()
Expand Down

0 comments on commit f52baf2

Please sign in to comment.