forked from 0ad/0ad
Improve correctness of hotkeys at low framerates.
SDL queues up all the input events received in a frame. When the hotkey system saw a key up/down event, it immediately updated its HotkeyIsPressed state and then pushed a hotkey event onto the end of the queue. If the initial queue was e.g. [key-down shift, key-press Z, key-up shift], the hotkey event triggered by Z would be processed after the key-up shift had updated the HotkeyIsPressed state, so the handler of the Z hotkey would not think the shift hotkey was pressed. If the initial queue was e.g. [key-press Z, mouse-click], the hotkey triggered by Z would be processed after the mouse-click event, so it could apply to the wrong building selection. Fix by pushing the hotkey events onto a special queue that gets processed before any subsequent SDL input events. Also update the HotkeyIsPressed status when the HOTKEYDOWN/HOTKEYUP events are processed, not when they are generated, to guarantee they are consistent with the DOWN/UP events. Fixes #1869. This was SVN commit r14057.
This commit is contained in:
parent
11c7c44a0b
commit
2d1be3d47e
@ -36,6 +36,8 @@ const size_t MAX_HANDLERS = 8;
|
||||
static InHandler handler_stack[MAX_HANDLERS];
|
||||
static size_t handler_stack_top = 0;
|
||||
|
||||
static std::list<SDL_Event_> priority_events;
|
||||
|
||||
void in_add_handler(InHandler handler)
|
||||
{
|
||||
ENSURE(handler);
|
||||
@ -69,3 +71,20 @@ void in_dispatch_event(const SDL_Event_* ev)
|
||||
DEBUG_WARN_ERR(ERR::LOGIC); // invalid handler return value
|
||||
}
|
||||
}
|
||||
|
||||
void in_push_priority_event(const SDL_Event_* event)
|
||||
{
|
||||
priority_events.push_back(*event);
|
||||
}
|
||||
|
||||
int in_poll_event(SDL_Event_* event)
|
||||
{
|
||||
if (!priority_events.empty())
|
||||
{
|
||||
*event = priority_events.front();
|
||||
priority_events.pop_front();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return SDL_PollEvent(&event->ev);
|
||||
}
|
||||
|
@ -55,4 +55,13 @@ extern void in_reset_handlers();
|
||||
// send event to each handler (newest first) until one returns true
|
||||
extern void in_dispatch_event(const SDL_Event_* event);
|
||||
|
||||
// push an event onto the back of a high-priority queue - the new event will
|
||||
// be returned by in_poll_event before any standard SDL events
|
||||
extern void in_push_priority_event(const SDL_Event_* event);
|
||||
|
||||
// reads events that were pushed by in_push_priority_event, or (if there are
|
||||
// no high-priority events) reads from the SDL event queue with SDL_PollEvent.
|
||||
// returns 1 if an event was read, 0 otherwise.
|
||||
extern int in_poll_event(SDL_Event_* event);
|
||||
|
||||
#endif // #ifndef INCLUDED_INPUT
|
||||
|
@ -180,7 +180,7 @@ static void PumpEvents()
|
||||
PROFILE3("dispatch events");
|
||||
|
||||
SDL_Event_ ev;
|
||||
while (SDL_PollEvent(&ev.ev))
|
||||
while (in_poll_event(&ev))
|
||||
{
|
||||
PROFILE2("event");
|
||||
if (g_GUI)
|
||||
|
@ -213,6 +213,14 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
return IN_PASS;
|
||||
#endif
|
||||
|
||||
case SDL_HOTKEYDOWN:
|
||||
g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = true;
|
||||
return IN_PASS;
|
||||
|
||||
case SDL_HOTKEYUP:
|
||||
g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = false;
|
||||
return IN_PASS;
|
||||
|
||||
default:
|
||||
return IN_PASS;
|
||||
}
|
||||
@ -321,10 +329,6 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
|
||||
if( accept && !( consoleCapture && it->name != "console.toggle" ) )
|
||||
{
|
||||
// Tentatively set status to un-pressed, since it may be overridden by
|
||||
// a closer match. (The closest matches will be set to pressed later.)
|
||||
g_HotkeyStatus[it->name] = false;
|
||||
|
||||
// Check if this is an equally precise or more precise match
|
||||
if( it->requires.size() + 1 >= closestMapMatch )
|
||||
{
|
||||
@ -343,12 +347,10 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
|
||||
for (size_t i = 0; i < closestMapNames.size(); ++i)
|
||||
{
|
||||
g_HotkeyStatus[closestMapNames[i]] = true;
|
||||
|
||||
SDL_Event hotkeyNotification;
|
||||
hotkeyNotification.type = SDL_HOTKEYDOWN;
|
||||
hotkeyNotification.user.data1 = const_cast<char*>(closestMapNames[i]);
|
||||
SDL_PushEvent(&hotkeyNotification);
|
||||
SDL_Event_ hotkeyNotification;
|
||||
hotkeyNotification.ev.type = SDL_HOTKEYDOWN;
|
||||
hotkeyNotification.ev.user.data1 = const_cast<char*>(closestMapNames[i]);
|
||||
in_push_priority_event(&hotkeyNotification);
|
||||
}
|
||||
|
||||
// -- KEYUP SECTION --
|
||||
@ -386,11 +388,10 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
|
||||
if( accept )
|
||||
{
|
||||
g_HotkeyStatus[it->name] = false;
|
||||
SDL_Event hotkeyNotification;
|
||||
hotkeyNotification.type = SDL_HOTKEYUP;
|
||||
hotkeyNotification.user.data1 = const_cast<char*>(it->name.c_str());
|
||||
SDL_PushEvent( &hotkeyNotification );
|
||||
SDL_Event_ hotkeyNotification;
|
||||
hotkeyNotification.ev.type = SDL_HOTKEYUP;
|
||||
hotkeyNotification.ev.user.data1 = const_cast<char*>(it->name.c_str());
|
||||
in_push_priority_event(&hotkeyNotification);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ static void* RunEngine(void* data)
|
||||
|
||||
// Pump SDL events (e.g. hotkeys)
|
||||
SDL_Event_ ev;
|
||||
while (SDL_PollEvent(&ev.ev))
|
||||
while (in_poll_event(&ev))
|
||||
in_dispatch_event(&ev);
|
||||
|
||||
if (g_GUI)
|
||||
|
Loading…
Reference in New Issue
Block a user