Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -296,8 +296,8 @@ unload = "U" ; Unload garrisoned units when a building/mechanical unit is selected move = "" ; Modifier to move to a point instead of another action (e.g. gather) attack = Ctrl ; Modifier to attack instead of another action (e.g. capture) -attackmove = Ctrl ; Modifier to attackmove when clicking on a point -attackmoveUnit = "Ctrl+Q" ; Modifier to attackmove targeting only units when clicking on a point (should contain the attackmove keys) +attackmove = Ctrl, "Ctrl+Q" ; Modifier to attackmove when clicking on a point +attackmoveUnit = "Ctrl+Q" ; Modifier to attackmove targeting only units when clicking on a point (attackmove must also be triggered). garrison = Ctrl ; Modifier to garrison when clicking on building autorallypoint = Ctrl ; Modifier to set the rally point on the building itself guard = "G" ; Modifier to escort/guard when clicking on unit/building Index: source/ps/Hotkey.cpp =================================================================== --- source/ps/Hotkey.cpp +++ source/ps/Hotkey.cpp @@ -310,9 +310,12 @@ if ((ev->ev.type == SDL_KEYDOWN) || (ev->ev.type == SDL_MOUSEBUTTONDOWN)) for (const SHotkeyMapping* hotkey : pressedHotkeys) { - if (hotkey->requires.size() + 1 < closestMapMatch) + if (std::find_if(newPressedHotkeys.begin(), newPressedHotkeys.end(), + [&hotkey](const SHotkeyMapping* v){ return v->name == hotkey->name; }) != newPressedHotkeys.end()) + continue; + else if (hotkey->requires.size() + 1 < closestMapMatch) releasedHotkeys.push_back(hotkey->name.c_str()); - else if (std::find(newPressedHotkeys.begin(), newPressedHotkeys.end(), hotkey) == newPressedHotkeys.end()) + else { // We need to check that all 'keys' are still pressed (because of mouse buttons). if (!isPressed(hotkey->primary)) Index: source/ps/tests/test_Hotkeys.h =================================================================== --- source/ps/tests/test_Hotkeys.h +++ source/ps/tests/test_Hotkeys.h @@ -55,6 +55,8 @@ TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache")); configDB = new CConfigDB; + + g_scancodes = {}; } void tearDown() @@ -70,7 +72,7 @@ configDB->SetValueString(CFG_SYSTEM, "hotkey.A", "A"); configDB->SetValueString(CFG_SYSTEM, "hotkey.AB", "A+B"); configDB->SetValueString(CFG_SYSTEM, "hotkey.ABC", "A+B+C"); - configDB->SetValueString(CFG_SYSTEM, "hotkey.D", "D"); + configDB->SetValueList(CFG_SYSTEM, "hotkey.D", { "D", "D+E" }); configDB->WriteFile(CFG_SYSTEM, "config/conf.cfg"); configDB->Reload(CFG_SYSTEM); @@ -115,6 +117,7 @@ fakeInput("B", true); fakeInput("A", true); + // Activating the more precise hotkey AB untriggers "A" TS_ASSERT_EQUALS(HotkeyIsPressed("A"), false); TS_ASSERT_EQUALS(HotkeyIsPressed("AB"), true); TS_ASSERT_EQUALS(HotkeyIsPressed("ABC"), false); @@ -135,6 +138,36 @@ TS_ASSERT_EQUALS(HotkeyIsPressed("ABC"), false); TS_ASSERT_EQUALS(HotkeyIsPressed("D"), false); + fakeInput("A", false); + fakeInput("D", true); + TS_ASSERT_EQUALS(HotkeyIsPressed("A"), false); + TS_ASSERT_EQUALS(HotkeyIsPressed("AB"), false); + TS_ASSERT_EQUALS(HotkeyIsPressed("ABC"), false); + TS_ASSERT_EQUALS(HotkeyIsPressed("D"), true); + + fakeInput("E", true); + // Changing from one hotkey to another more specific combination of the same hotkey keeps it active + TS_ASSERT_EQUALS(HotkeyIsPressed("A"), false); + TS_ASSERT_EQUALS(HotkeyIsPressed("AB"), false); + TS_ASSERT_EQUALS(HotkeyIsPressed("ABC"), false); + TS_ASSERT_EQUALS(HotkeyIsPressed("D"), true); + fakeInput("E", false); + // Likewise going the other way. + TS_ASSERT_EQUALS(HotkeyIsPressed("D"), true); + } + + void test_quirk() + { + configDB->SetValueString(CFG_SYSTEM, "hotkey.A", "A"); + configDB->SetValueString(CFG_SYSTEM, "hotkey.AB", "A+B"); + configDB->SetValueString(CFG_SYSTEM, "hotkey.ABC", "A+B+C"); + configDB->SetValueList(CFG_SYSTEM, "hotkey.D", { "D", "D+E" }); + configDB->WriteFile(CFG_SYSTEM, "config/conf.cfg"); + configDB->Reload(CFG_SYSTEM); + + UnloadHotkeys(); + LoadHotkeys(*configDB); + /** * Quirk of the implementation: hotkeys are allowed to fire with too many keys. * Further, hotkeys of the same specificity (i.e. same # of required keys)