Fix hotkeys not releasing properly.
a4852c4c01
changed hotkeys to use scancode, but didn't change g_keys to reflect that, and this could break hotkey release. This fixes that by explicitly using scancodes. Note that we might want a g_keys map in the future, but it seems un-necessary at the moment. Also remove the last remnants of 'negated' hotkeys, which were disabled followingb995135138
. Tested By: OptimusShepard Fixes #5922 Differential Revision: https://code.wildfiregames.com/D3379 This was SVN commit r24645.
This commit is contained in:
parent
7f1ee23d88
commit
12cceed3d9
@ -231,9 +231,9 @@ InReaction CInput::ManuallyHandleKeys(const SDL_Event_* ev)
|
||||
// TODO: there are probably other keys that we could ignore, but recognizing "non-glyph" keys isn't that trivial.
|
||||
// Further, don't input text if modifiers other than shift are pressed (the user is presumably trying to perform a hotkey).
|
||||
if (keyCode == SDLK_ESCAPE ||
|
||||
g_keys[SDLK_LCTRL] || g_keys[SDLK_RCTRL] ||
|
||||
g_keys[SDLK_LALT] || g_keys[SDLK_RALT] ||
|
||||
g_keys[SDLK_LGUI] || g_keys[SDLK_RGUI])
|
||||
g_scancodes[SDL_SCANCODE_LCTRL] || g_scancodes[SDL_SCANCODE_LCTRL] ||
|
||||
g_scancodes[SDL_SCANCODE_LALT] || g_scancodes[SDL_SCANCODE_RALT] ||
|
||||
g_scancodes[SDL_SCANCODE_LGUI] || g_scancodes[SDL_SCANCODE_RGUI])
|
||||
return IN_PASS;
|
||||
|
||||
if (m_ComposingText)
|
||||
@ -369,7 +369,7 @@ void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode)
|
||||
|
||||
void CInput::ManuallyImmutableHandleKeyDownEvent(const SDL_Keycode keyCode)
|
||||
{
|
||||
bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
|
||||
bool shiftKeyPressed = g_scancodes[SDL_SCANCODE_LSHIFT] || g_scancodes[SDL_SCANCODE_RSHIFT];
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
@ -609,7 +609,7 @@ void CInput::SetupGeneratedPlaceholderText()
|
||||
|
||||
InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
|
||||
{
|
||||
bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
|
||||
bool shiftKeyPressed = g_scancodes[SDL_SCANCODE_LSHIFT] || g_scancodes[SDL_SCANCODE_RSHIFT];
|
||||
|
||||
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
||||
|
||||
@ -962,7 +962,7 @@ void CInput::HandleMessage(SGUIMessage& Message)
|
||||
// instance, if we press between a and b, the point
|
||||
// should of course be placed accordingly. Other
|
||||
// special cases are handled like the input box norms.
|
||||
if (g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT])
|
||||
if (g_scancodes[SDL_SCANCODE_LSHIFT] || g_scancodes[SDL_SCANCODE_RSHIFT])
|
||||
m_iBufferPos = GetMouseHoveringTextPosition();
|
||||
else
|
||||
m_iBufferPos = m_iBufferPos_Tail = GetMouseHoveringTextPosition();
|
||||
|
@ -360,7 +360,7 @@ void CConsole::InsertChar(const int szChar, const wchar_t cooked)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_keys[SDLK_RCTRL] || g_keys[SDLK_LCTRL])
|
||||
if (g_scancodes[SDL_SCANCODE_LCTRL] || g_scancodes[SDL_SCANCODE_LCTRL])
|
||||
{
|
||||
// Make Ctrl-Delete delete up to end of line
|
||||
m_szBuffer[m_iBufferPos] = '\0';
|
||||
@ -379,7 +379,7 @@ void CConsole::InsertChar(const int szChar, const wchar_t cooked)
|
||||
return;
|
||||
|
||||
case SDLK_HOME:
|
||||
if (g_keys[SDLK_RCTRL] || g_keys[SDLK_LCTRL])
|
||||
if (g_scancodes[SDL_SCANCODE_LCTRL] || g_scancodes[SDL_SCANCODE_LCTRL])
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex); // needed for safe access to m_deqMsgHistory
|
||||
|
||||
@ -393,7 +393,7 @@ void CConsole::InsertChar(const int szChar, const wchar_t cooked)
|
||||
return;
|
||||
|
||||
case SDLK_END:
|
||||
if (g_keys[SDLK_RCTRL] || g_keys[SDLK_LCTRL])
|
||||
if (g_scancodes[SDL_SCANCODE_LCTRL] || g_scancodes[SDL_SCANCODE_LCTRL])
|
||||
{
|
||||
m_iMsgHistPos = 1;
|
||||
}
|
||||
|
@ -25,7 +25,8 @@
|
||||
bool g_app_minimized = false;
|
||||
bool g_app_has_focus = true;
|
||||
|
||||
std::map<int32_t, bool> g_keys;
|
||||
std::unordered_map<int32_t, bool> g_scancodes;
|
||||
|
||||
int g_mouse_x = 50, g_mouse_y = 50;
|
||||
bool g_mouse_active = true;
|
||||
|
||||
@ -85,7 +86,7 @@ InReaction GlobalsInputHandler(const SDL_Event_* ev)
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
g_keys[ev->ev.key.keysym.sym] = (ev->ev.type == SDL_KEYDOWN);
|
||||
g_scancodes[ev->ev.key.keysym.scancode] = (ev->ev.type == SDL_KEYDOWN);
|
||||
return IN_PASS;
|
||||
|
||||
default:
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "lib/frequency_filter.h"
|
||||
#include "ps/KeyName.h"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
// thin abstraction layer on top of SDL.
|
||||
// game code should use it instead of SDL_GetMouseState etc. because
|
||||
@ -40,11 +40,11 @@ extern int g_mouse_x, g_mouse_y;
|
||||
extern bool g_mouse_active;
|
||||
|
||||
/**
|
||||
* g_keys: Key states, indexed by SDLK* constants. If an entry is true,
|
||||
* g_scancodes: Key states, indexed by SDL_Scancode constants. If an entry is true,
|
||||
* it represents a pressed key.
|
||||
* Updated by GlobalsInputHandler in response to key press/release events.
|
||||
*/
|
||||
extern std::map<int32_t, bool> g_keys;
|
||||
extern std::unordered_map<int32_t, bool> g_scancodes;
|
||||
|
||||
/**
|
||||
* g_mouse_buttons: Mouse buttons states, indexed by SDL_BUTTON_* constants.
|
||||
|
@ -50,7 +50,6 @@ static void LoadConfigBindings()
|
||||
// Unused hotkeys must still be registered in the map to appear in the hotkey editor.
|
||||
SHotkeyMapping unusedCode;
|
||||
unusedCode.name = hotkeyName;
|
||||
unusedCode.negated = false;
|
||||
g_HotkeyMap[UNUSED_HOTKEY_CODE].push_back(unusedCode);
|
||||
continue;
|
||||
}
|
||||
@ -73,7 +72,7 @@ static void LoadConfigBindings()
|
||||
continue;
|
||||
}
|
||||
|
||||
SKey key = { scancode, false };
|
||||
SKey key = { scancode };
|
||||
keyCombination.push_back(key);
|
||||
}
|
||||
|
||||
@ -83,7 +82,6 @@ static void LoadConfigBindings()
|
||||
SHotkeyMapping bindCode;
|
||||
|
||||
bindCode.name = hotkeyName;
|
||||
bindCode.negated = itKey->negated;
|
||||
|
||||
for (itKey2 = keyCombination.begin(); itKey2 != keyCombination.end(); ++itKey2)
|
||||
if (itKey != itKey2) // Push any auxiliary keys
|
||||
@ -98,25 +96,6 @@ static void LoadConfigBindings()
|
||||
void LoadHotkeys()
|
||||
{
|
||||
LoadConfigBindings();
|
||||
|
||||
// Set up the state of the hotkeys given no key is down.
|
||||
// i.e. find those hotkeys triggered by all negations.
|
||||
|
||||
for (const std::pair<const int, KeyMapping>& p : g_HotkeyMap)
|
||||
for (const SHotkeyMapping& hotkey : p.second)
|
||||
{
|
||||
if (!hotkey.negated)
|
||||
continue;
|
||||
|
||||
bool allNegated = true;
|
||||
|
||||
for (const SKey& k : hotkey.requires)
|
||||
if (!k.negated)
|
||||
allNegated = false;
|
||||
|
||||
if (allNegated)
|
||||
g_HotkeyStatus[hotkey.name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void UnloadHotkeys()
|
||||
@ -125,19 +104,19 @@ void UnloadHotkeys()
|
||||
g_HotkeyStatus.clear();
|
||||
}
|
||||
|
||||
bool isNegated(const SKey& key)
|
||||
bool isPressed(const SKey& key)
|
||||
{
|
||||
// Normal keycodes are below EXTRA_KEYS_BASE
|
||||
if ((int)key.code < EXTRA_KEYS_BASE && g_keys[key.code] == key.negated)
|
||||
return false;
|
||||
if ((int)key.code < EXTRA_KEYS_BASE)
|
||||
return g_scancodes[key.code];
|
||||
|
||||
// Mouse 'keycodes' are after the modifier keys
|
||||
else if ((int)key.code < MOUSE_LAST && (int)key.code > MOUSE_BASE && g_mouse_buttons[key.code - MOUSE_BASE] == key.negated)
|
||||
return false;
|
||||
else if ((int)key.code < MOUSE_LAST && (int)key.code > MOUSE_BASE)
|
||||
return g_mouse_buttons[key.code - MOUSE_BASE];
|
||||
|
||||
// Modifier keycodes are between the normal keys and the mouse 'keys'
|
||||
else if ((int)key.code < UNIFIED_LAST && (int)key.code > SDL_NUM_SCANCODES && unified[key.code - UNIFIED_SHIFT] == key.negated)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
else if ((int)key.code < UNIFIED_LAST && (int)key.code > SDL_NUM_SCANCODES)
|
||||
return unified[key.code - UNIFIED_SHIFT];
|
||||
}
|
||||
|
||||
InReaction HotkeyStateChange(const SDL_Event_* ev)
|
||||
@ -256,23 +235,17 @@ InReaction HotkeyInputHandler(const SDL_Event_* ev)
|
||||
|
||||
bool typeKeyDown = ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN ) || (ev->ev.type == SDL_MOUSEWHEEL);
|
||||
|
||||
// -- KEYDOWN SECTION --
|
||||
|
||||
std::vector<const char*> closestMapNames;
|
||||
std::vector<const char*> pressedHotkeys;
|
||||
std::vector<const char*> releasedHotkeys;
|
||||
size_t closestMapMatch = 0;
|
||||
|
||||
for (const SHotkeyMapping& hotkey : g_HotkeyMap[scancode])
|
||||
{
|
||||
// If a key has been pressed, and this event triggers on its release, skip it.
|
||||
// Similarly, if the key's been released and the event triggers on a keypress, skip it.
|
||||
if (hotkey.negated == typeKeyDown)
|
||||
continue;
|
||||
|
||||
// Check for no unpermitted keys
|
||||
bool accept = true;
|
||||
for (const SKey& k : hotkey.requires)
|
||||
{
|
||||
accept = isNegated(k);
|
||||
accept = isPressed(k);
|
||||
if (!accept)
|
||||
break;
|
||||
}
|
||||
@ -286,23 +259,26 @@ InReaction HotkeyInputHandler(const SDL_Event_* ev)
|
||||
if (hotkey.requires.size() + 1 > closestMapMatch)
|
||||
{
|
||||
// Throw away the old less-precise matches
|
||||
closestMapNames.clear();
|
||||
pressedHotkeys.clear();
|
||||
releasedHotkeys.clear();
|
||||
closestMapMatch = hotkey.requires.size() + 1;
|
||||
}
|
||||
|
||||
closestMapNames.push_back(hotkey.name.c_str());
|
||||
if (typeKeyDown)
|
||||
pressedHotkeys.push_back(hotkey.name.c_str());
|
||||
else
|
||||
releasedHotkeys.push_back(hotkey.name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < closestMapNames.size(); ++i)
|
||||
for (const char* hotkeyName : pressedHotkeys)
|
||||
{
|
||||
// Send a KeyPress event when a key is pressed initially and on mouseButton and mouseWheel events.
|
||||
// Send a KeyPress event when a hotkey is pressed initially and on mouseButton and mouseWheel events.
|
||||
if (ev->ev.type != SDL_KEYDOWN || ev->ev.key.repeat == 0)
|
||||
{
|
||||
SDL_Event_ hotkeyPressNotification;
|
||||
hotkeyPressNotification.ev.type = SDL_HOTKEYPRESS;
|
||||
hotkeyPressNotification.ev.user.data1 = const_cast<char*>(closestMapNames[i]);
|
||||
hotkeyPressNotification.ev.user.data1 = const_cast<char*>(hotkeyName);
|
||||
in_push_priority_event(&hotkeyPressNotification);
|
||||
}
|
||||
|
||||
@ -311,37 +287,16 @@ InReaction HotkeyInputHandler(const SDL_Event_* ev)
|
||||
// On linux, modifier keys (shift, alt, ctrl) are not repeated, see https://github.com/SFML/SFML/issues/122.
|
||||
SDL_Event_ hotkeyDownNotification;
|
||||
hotkeyDownNotification.ev.type = SDL_HOTKEYDOWN;
|
||||
hotkeyDownNotification.ev.user.data1 = const_cast<char*>(closestMapNames[i]);
|
||||
hotkeyDownNotification.ev.user.data1 = const_cast<char*>(hotkeyName);
|
||||
in_push_priority_event(&hotkeyDownNotification);
|
||||
}
|
||||
|
||||
// -- KEYUP SECTION --
|
||||
|
||||
for (const SHotkeyMapping& hotkey : g_HotkeyMap[scancode])
|
||||
for (const char* hotkeyName : releasedHotkeys)
|
||||
{
|
||||
// If it's a keydown event, won't cause HotKeyUps in anything that doesn't
|
||||
// use this key negated => skip them
|
||||
// If it's a keyup event, won't cause HotKeyUps in anything that does use
|
||||
// this key negated => skip them too.
|
||||
if (hotkey.negated != typeKeyDown)
|
||||
continue;
|
||||
|
||||
// Check for no unpermitted keys
|
||||
bool accept = true;
|
||||
for (const SKey& k : hotkey.requires)
|
||||
{
|
||||
accept = isNegated(k);
|
||||
if (!accept)
|
||||
break;
|
||||
}
|
||||
|
||||
if (accept)
|
||||
{
|
||||
SDL_Event_ hotkeyNotification;
|
||||
hotkeyNotification.ev.type = SDL_HOTKEYUP;
|
||||
hotkeyNotification.ev.user.data1 = const_cast<char*>(hotkey.name.c_str());
|
||||
in_push_priority_event(&hotkeyNotification);
|
||||
}
|
||||
SDL_Event_ hotkeyNotification;
|
||||
hotkeyNotification.ev.type = SDL_HOTKEYUP;
|
||||
hotkeyNotification.ev.user.data1 = const_cast<char*>(hotkeyName);
|
||||
in_push_priority_event(&hotkeyNotification);
|
||||
}
|
||||
|
||||
return IN_PASS;
|
||||
|
@ -52,17 +52,14 @@ constexpr SDL_Scancode_ UNUSED_HOTKEY_CODE = 0; // == SDL_SCANCODE_UNKNOWN
|
||||
struct SKey
|
||||
{
|
||||
SDL_Scancode_ code; // scancode or MOUSE_ or UNIFIED_ value
|
||||
bool negated; // whether the key must be pressed (false) or unpressed (true)
|
||||
|
||||
bool operator<(const SKey& o) const { return code < o.code && negated < o.negated; }
|
||||
bool operator==(const SKey& o) const { return code == o.code && negated == o.negated; }
|
||||
bool operator<(const SKey& o) const { return code < o.code; }
|
||||
bool operator==(const SKey& o) const { return code == o.code; }
|
||||
};
|
||||
|
||||
// Hotkey data associated with an externally-specified 'primary' keycode
|
||||
struct SHotkeyMapping
|
||||
{
|
||||
CStr name; // name of the hotkey
|
||||
bool negated; // whether the primary key must be pressed (false) or unpressed (true)
|
||||
std::vector<SKey> requires; // list of non-primary keys that must also be active
|
||||
};
|
||||
|
||||
|
@ -145,7 +145,7 @@ JS::Value GetConflicts(ScriptInterface::CmptPrivate* pCmptPrivate, JS::HandleVal
|
||||
|
||||
std::set<SKey> codes;
|
||||
for (const std::string& key : keys)
|
||||
codes.insert(SKey{ FindScancode(key), false });
|
||||
codes.insert(SKey{ FindScancode(key) });
|
||||
|
||||
std::vector<CStr> conflicts;
|
||||
// This isn't very efficient, but we shouldn't iterate too many hotkeys
|
||||
|
Loading…
Reference in New Issue
Block a user