Index: binaries/data/mods/public/gui/common/tab_buttons.xml =================================================================== --- binaries/data/mods/public/gui/common/tab_buttons.xml +++ binaries/data/mods/public/gui/common/tab_buttons.xml @@ -1,11 +1,11 @@ - selectNextTab(1); + selectNextTab(1); - selectNextTab(-1); + selectNextTab(-1); Index: binaries/data/mods/public/gui/session/hotkeys/misc.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/misc.xml +++ binaries/data/mods/public/gui/session/hotkeys/misc.xml @@ -64,19 +64,19 @@ - performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "delete"); + performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "delete"); - unloadAll(); + unloadAll(); - stopUnits(g_Selection.toList()); + stopUnits(g_Selection.toList()); - backToWork(); + backToWork(); @@ -98,15 +98,15 @@ - findIdleUnit(g_MilitaryTypes); + findIdleUnit(g_MilitaryTypes); - findIdleUnit(["!Domestic"]); + findIdleUnit(["!Domestic"]); - clearSelection(); + clearSelection(); Index: binaries/data/mods/public/gui/session/hotkeys/training.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/training.xml +++ binaries/data/mods/public/gui/session/hotkeys/training.xml @@ -2,36 +2,36 @@ - addTrainingByPosition(0); + addTrainingByPosition(0); - addTrainingByPosition(1); + addTrainingByPosition(1); - addTrainingByPosition(2); + addTrainingByPosition(2); - addTrainingByPosition(3); + addTrainingByPosition(3); - addTrainingByPosition(4); + addTrainingByPosition(4); - addTrainingByPosition(5); + addTrainingByPosition(5); - addTrainingByPosition(6); + addTrainingByPosition(6); Index: binaries/data/mods/public/gui/session/minimap_panel.xml =================================================================== --- binaries/data/mods/public/gui/session/minimap_panel.xml +++ binaries/data/mods/public/gui/session/minimap_panel.xml @@ -27,7 +27,7 @@ sprite_over="stretched:session/minimap-idle-highlight.png" sprite_disabled="stretched:session/minimap-idle-disabled.png" > - findIdleUnit(g_WorkerTypes); + findIdleUnit(g_WorkerTypes); Index: binaries/data/mods/public/gui/summary/summary.xml =================================================================== --- binaries/data/mods/public/gui/summary/summary.xml +++ binaries/data/mods/public/gui/summary/summary.xml @@ -17,11 +17,11 @@ - selectNextTab(1); + selectNextTab(1); - selectNextTab(-1); + selectNextTab(-1); Index: source/graphics/GameView.cpp =================================================================== --- source/graphics/GameView.cpp +++ source/graphics/GameView.cpp @@ -1033,10 +1033,9 @@ { switch(ev->ev.type) { - - case SDL_HOTKEYDOWN: + case SDL_HOTKEYPRESS: + { std::string hotkey = static_cast(ev->ev.user.data1); - if (hotkey == "wireframe") { if (g_XmppClient && g_rankedGame == true) @@ -1061,10 +1060,20 @@ } return IN_HANDLED; } + else if (hotkey == "camera.reset") + { + ResetCameraAngleZoom(); + return IN_HANDLED; + } + return IN_PASS; + } + case SDL_HOTKEYDOWN: + { + std::string hotkey = static_cast(ev->ev.user.data1); // Mouse wheel must be treated using events instead of polling, // because SDL auto-generates a sequence of mousedown/mouseup events // and we never get to see the "down" state inside Update(). - else if (hotkey == "camera.zoom.wheel.in") + if (hotkey == "camera.zoom.wheel.in") { m->Zoom.AddSmoothly(-m->ViewZoomSpeedWheel); return IN_HANDLED; @@ -1116,12 +1125,8 @@ m->ViewZoomSpeed /= m->ViewZoomSpeedModifier; return IN_HANDLED; } - else if (hotkey == "camera.reset") - { - ResetCameraAngleZoom(); - return IN_HANDLED; - } + return IN_PASS; + } } - return IN_PASS; } Index: source/gui/CGUI.cpp =================================================================== --- source/gui/CGUI.cpp +++ source/gui/CGUI.cpp @@ -67,7 +67,7 @@ { InReaction ret = IN_PASS; - if (ev->ev.type == SDL_HOTKEYDOWN || ev->ev.type == SDL_HOTKEYUP) + if (ev->ev.type == SDL_HOTKEYDOWN || ev->ev.type == SDL_HOTKEYPRESS || ev->ev.type == SDL_HOTKEYUP) { const char* hotkey = static_cast(ev->ev.user.data1); @@ -92,8 +92,10 @@ HotkeyInputHandler(ev); ret = IN_HANDLED; - if (ev->ev.type == SDL_HOTKEYDOWN) + if (ev->ev.type == SDL_HOTKEYPRESS) obj->SendEvent(GUIM_PRESSED, "press"); + else if (ev->ev.type == SDL_HOTKEYDOWN) + obj->SendEvent(GUIM_PRESSED, "keydown"); else obj->SendEvent(GUIM_RELEASED, "release"); } Index: source/gui/scripting/GuiScriptConversions.cpp =================================================================== --- source/gui/scripting/GuiScriptConversions.cpp +++ source/gui/scripting/GuiScriptConversions.cpp @@ -45,6 +45,7 @@ case SDL_MOUSEBUTTONDOWN: typeName = "mousebuttondown"; break; case SDL_MOUSEBUTTONUP: typeName = "mousebuttonup"; break; case SDL_QUIT: typeName = "quit"; break; + case SDL_HOTKEYPRESS: typeName = "hotkeypress"; break; case SDL_HOTKEYDOWN: typeName = "hotkeydown"; break; case SDL_HOTKEYUP: typeName = "hotkeyup"; break; default: typeName = "(unknown)"; break; @@ -108,6 +109,7 @@ SET(obj, "clicks", (int)val.ev.button.clicks); break; } + case SDL_HOTKEYPRESS: case SDL_HOTKEYDOWN: case SDL_HOTKEYUP: { Index: source/main.cpp =================================================================== --- source/main.cpp +++ source/main.cpp @@ -171,7 +171,7 @@ QuitEngine(); break; - case SDL_HOTKEYDOWN: + case SDL_HOTKEYPRESS: std::string hotkey = static_cast(ev->ev.user.data1); if (hotkey == "exit") { Index: source/ps/CConsole.cpp =================================================================== --- source/ps/CConsole.cpp +++ source/ps/CConsole.cpp @@ -635,7 +635,7 @@ InReaction conInputHandler(const SDL_Event_* ev) { - if ((int)ev->ev.type == SDL_HOTKEYDOWN) + if ((int)ev->ev.type == SDL_HOTKEYPRESS) { std::string hotkey = static_cast(ev->ev.user.data1); Index: source/ps/Hotkey.h =================================================================== --- source/ps/Hotkey.h +++ source/ps/Hotkey.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,9 +25,9 @@ * Hotkeys consist of a name (an arbitrary string), and a key mapping. * The names and mappings are loaded from the config system (any * config setting with the name prefix "hotkey."). - * When a hotkey is pressed or released, SDL_HOTKEYDOWN and SDL_HOTKEYUP - * events are triggered, with the hotkey name stored in ev.user.data1 - * as a const char*. + * When a hotkey is pressed one SDL_HOTKEYPRESS and repeated SDL_HOTKEYDOWN events + * are triggered. When a hotkey is released an SDL_HOTKEYUP event is triggered. All + * with the hotkey name stored in ev.user.data1 as a const char*. */ #include "CStr.h" @@ -38,8 +38,9 @@ // required for our HOTKEY event type definition. this is OK since // hotkey.h is not included from any headers. -const int SDL_HOTKEYDOWN = SDL_USEREVENT; -const int SDL_HOTKEYUP = SDL_USEREVENT + 1; +const int SDL_HOTKEYPRESS = SDL_USEREVENT; +const int SDL_HOTKEYDOWN = SDL_USEREVENT + 1; +const int SDL_HOTKEYUP = SDL_USEREVENT + 2; extern void LoadHotkeys(); extern void UnloadHotkeys(); Index: source/ps/Hotkey.cpp =================================================================== --- source/ps/Hotkey.cpp +++ source/ps/Hotkey.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -196,6 +196,7 @@ } return IN_PASS; + case SDL_HOTKEYPRESS: case SDL_HOTKEYDOWN: g_HotkeyStatus[static_cast(ev->ev.user.data1)] = true; return IN_PASS; @@ -214,6 +215,9 @@ SDL_Event_ phantom; phantom.ev.type = ((ev->ev.type == SDL_KEYDOWN) || (ev->ev.type == SDL_MOUSEBUTTONDOWN)) ? SDL_KEYDOWN : SDL_KEYUP; + if (phantom.ev.type == SDL_KEYDOWN) + phantom.ev.key.repeat = ev->ev.type == SDL_KEYDOWN ? ev->ev.key.repeat : 0; + if ((keycode == SDLK_LSHIFT) || (keycode == SDLK_RSHIFT)) { phantom.ev.key.keysym.sym = (SDL_Keycode)UNIFIED_SHIFT; @@ -257,7 +261,7 @@ // To avoid this, set the modifier keys for /all/ events this key would trigger // (Ctrl, for example, is both group-save and bookmark-save) - // but only send a HotkeyDown event for the event with bindings most precisely + // but only send a HotkeyPress/HotkeyDown event for the event with bindings most precisely // matching the conditions (i.e. the event with the highest number of auxiliary // keys, providing they're all down) @@ -304,10 +308,22 @@ for (size_t i = 0; i < closestMapNames.size(); ++i) { - SDL_Event_ hotkeyNotification; - hotkeyNotification.ev.type = SDL_HOTKEYDOWN; - hotkeyNotification.ev.user.data1 = const_cast(closestMapNames[i]); - in_push_priority_event(&hotkeyNotification); + // Send a KeyPress event when a key is pressed initially and on mouseButton and mouseWheel events. + if (ev->ev.type != SDL_KEYDOWN || ev->ev.key.repeat == 0) + { + SDL_Event_ hotkeyDownNotification; + hotkeyDownNotification.ev.type = SDL_HOTKEYPRESS; + hotkeyDownNotification.ev.user.data1 = const_cast(closestMapNames[i]); + in_push_priority_event(&hotkeyDownNotification); + } + + // Send a HotkeyDown event on every key, mouseButton and mouseWheel event. + // For keys the event is repeated depending on hardware and OS configured interval. + // On linux, modifier keys (shift, alt, ctrl) are not repeated, see https://github.com/SFML/SFML/issues/122. + SDL_Event_ hotkeyPressNotification; + hotkeyPressNotification.ev.type = SDL_HOTKEYDOWN; + hotkeyPressNotification.ev.user.data1 = const_cast(closestMapNames[i]); + in_push_priority_event(&hotkeyPressNotification); } // -- KEYUP SECTION -- Index: source/ps/ProfileViewer.cpp =================================================================== --- source/ps/ProfileViewer.cpp +++ source/ps/ProfileViewer.cpp @@ -335,7 +335,7 @@ } break; } - case SDL_HOTKEYDOWN: + case SDL_HOTKEYPRESS: std::string hotkey = static_cast(ev->ev.user.data1); if( hotkey == "profile.toggle" )