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:
Ykkrosh 2013-10-30 01:38:32 +00:00
parent 11c7c44a0b
commit 2d1be3d47e
5 changed files with 46 additions and 17 deletions

View File

@ -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);
}

View File

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

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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)