Implement keyDown event

Change HotkeyPress event to be non-repeating (HotkeyDown to replace the
repeating case)
Fix shiftlag
Make toggle hotkeys only respond to the first SDL event.

Many iterations of review by: elexis
Test done by: Imarok
Comments By: vladislav, Stan
Reviewed By: wraitii
Fixes: #5055
Differential Revision: https://code.wildfiregames.com/D1398
This was SVN commit r23701.
This commit is contained in:
bb 2020-05-26 21:47:03 +00:00
parent 892f97743b
commit 5cfce692e7
28 changed files with 155 additions and 89 deletions

View File

@ -12,7 +12,7 @@ class OverlayCounter
registerConfigChangeHandler(this.onConfigChange.bind(this));
if (this.Hotkey)
Engine.SetGlobalHotkey(this.Hotkey, this.toggle.bind(this));
Engine.SetGlobalHotkey(this.Hotkey, "Press", this.toggle.bind(this));
}
onConfigChange(changes)

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<object>
<object hotkey="tab.next">
<action on="Press">selectNextTab(1);</action>
<action on="KeyDown">selectNextTab(1);</action>
</object>
<object hotkey="tab.prev">
<action on="Press">selectNextTab(-1);</action>
<action on="KeyDown">selectNextTab(-1);</action>
</object>
<object name="tabButtons" type="image">

View File

@ -15,8 +15,8 @@ class CivInfoButton
"hotkey_structree": colorizeHotkey("%(hotkey)s", "structree")
});
Engine.SetGlobalHotkey("structree", this.openPage.bind(this, "page_structree.xml"));
Engine.SetGlobalHotkey("civinfo", this.openPage.bind(this, "page_civinfo.xml"));
Engine.SetGlobalHotkey("structree", "Press", this.openPage.bind(this, "page_structree.xml"));
Engine.SetGlobalHotkey("civinfo", "Press", this.openPage.bind(this, "page_civinfo.xml"));
}
onPress()

View File

@ -16,7 +16,7 @@ class GameSettingTabs
colorizeHotkey("\n" + this.HotkeyUpTooltip, this.ConfigNameHotkeyUp);
setupWindow.registerLoadHandler(this.onLoad.bind(this));
Engine.SetGlobalHotkey("cancel", selectPanel);
Engine.SetGlobalHotkey("cancel", "Press", selectPanel);
}
registerTabsResizeHandler(handler)

View File

@ -29,7 +29,7 @@ class LeaderboardPage
openPage()
{
this.leaderboardPage.hidden = false;
Engine.SetGlobalHotkey("cancel", this.onPressClose.bind(this));
Engine.SetGlobalHotkey("cancel", "Press", this.onPressClose.bind(this));
Engine.SendGetBoardList();
let playerName = this.leaderboardList.selectedPlayer();

View File

@ -17,10 +17,10 @@ class QuitButton
if (dialog)
{
Engine.SetGlobalHotkey("lobby", onPress);
Engine.SetGlobalHotkey("cancel", onPress);
Engine.SetGlobalHotkey("lobby", "Press", onPress);
Engine.SetGlobalHotkey("cancel", "Press", onPress);
let cancelHotkey = Engine.SetGlobalHotkey.bind(Engine, "cancel", onPress);
let cancelHotkey = Engine.SetGlobalHotkey.bind(Engine, "cancel", "Press", onPress);
leaderboardPage.registerClosePageHandler(cancelHotkey);
profilePage.registerClosePageHandler(cancelHotkey);
}

View File

@ -37,7 +37,7 @@ class ProfilePage
openPage()
{
this.profilePage.hidden = false;
Engine.SetGlobalHotkey("cancel", this.onPressClose.bind(this));
Engine.SetGlobalHotkey("cancel", "Press", this.onPressClose.bind(this));
}
onPressLookup()

View File

@ -85,7 +85,7 @@ class MainMenuItemHandler
{
let item = menuItems[i];
if (item.onPress && item.hotkey)
Engine.SetGlobalHotkey(item.hotkey, () => {
Engine.SetGlobalHotkey(item.hotkey, "Press", () => {
this.closeSubmenu();
item.onPress();
});

View File

@ -12,7 +12,7 @@ class RangeOverlayManager
for (let type of this.Types)
{
this.setEnabled(type, this.isEnabled(type));
Engine.SetGlobalHotkey(type.hotkey, this.toggle.bind(this, type));
Engine.SetGlobalHotkey(type.hotkey, "Press", this.toggle.bind(this, type));
}
registerConfigChangeHandler(this.onConfigChange.bind(this));

View File

@ -40,9 +40,9 @@ class Chat
registerPlayerAssignmentsChangeHandler(updater);
playerViewControl.registerViewedPlayerChangeHandler(updater);
Engine.SetGlobalHotkey("chat", this.openPage.bind(this));
Engine.SetGlobalHotkey("privatechat", this.openPage.bind(this));
Engine.SetGlobalHotkey("teamchat", () => { this.openPage(g_IsObserver ? "/observers" : "/allies"); });
Engine.SetGlobalHotkey("chat", "Press", this.openPage.bind(this));
Engine.SetGlobalHotkey("privatechat", "Press", this.openPage.bind(this));
Engine.SetGlobalHotkey("teamchat", "Press", () => { this.openPage(g_IsObserver ? "/observers" : "/allies"); });
}
/**
@ -81,7 +81,7 @@ class Chat
submitChat(text, command = "")
{
if (command.startsWith("/msg "))
Engine.SetGlobalHotkey("privatechat", () => { this.openPage(command); });
Engine.SetGlobalHotkey("privatechat", "Press", () => { this.openPage(command); });
let msg = command ? command + " " + text : text;

View File

@ -32,19 +32,19 @@
</object>
<object hotkey="session.kill">
<action on="Press">performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "delete");</action>
<action on="KeyDown">performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "delete");</action>
</object>
<object hotkey="session.unload">
<action on="Press">unloadAll();</action>
<action on="KeyDown">unloadAll();</action>
</object>
<object hotkey="session.stop">
<action on="Press">stopUnits(g_Selection.toList());</action>
<action on="KeyDown">stopUnits(g_Selection.toList());</action>
</object>
<object hotkey="session.backtowork">
<action on="Press">backToWork();</action>
<action on="KeyDown">backToWork();</action>
</object>
<object hotkey="session.batchtrain">
@ -54,15 +54,15 @@
<!-- Find idle warrior - TODO: Potentially move this to own UI button? -->
<object hotkey="selection.idlewarrior">
<action on="Press">findIdleUnit(g_MilitaryTypes);</action>
<action on="KeyDown">findIdleUnit(g_MilitaryTypes);</action>
</object>
<object hotkey="selection.idleunit">
<action on="Press">findIdleUnit(["!Domestic"]);</action>
<action on="KeyDown">findIdleUnit(["!Domestic"]);</action>
</object>
<object hotkey="selection.cancel">
<action on="Press">clearSelection();</action>
<action on="KeyDown">clearSelection();</action>
</object>
<object hotkey="session.showstatusbars">

View File

@ -2,36 +2,36 @@
<object>
<!-- queue first unit in the training queue -->
<object hotkey="session.queueunit.1">
<action on="Press">addTrainingByPosition(0);</action>
<action on="KeyDown">addTrainingByPosition(0);</action>
</object>
<!-- queue 2nd unit in the training queue -->
<object hotkey="session.queueunit.2">
<action on="Press">addTrainingByPosition(1);</action>
<action on="KeyDown">addTrainingByPosition(1);</action>
</object>
<!-- queue 3rd unit in the training queue -->
<object hotkey="session.queueunit.3">
<action on="Press">addTrainingByPosition(2);</action>
<action on="KeyDown">addTrainingByPosition(2);</action>
</object>
<!-- queue 4th unit in the training queue -->
<object hotkey="session.queueunit.4">
<action on="Press">addTrainingByPosition(3);</action>
<action on="KeyDown">addTrainingByPosition(3);</action>
</object>
<!-- queue 5th unit in the training queue -->
<object hotkey="session.queueunit.5">
<action on="Press">addTrainingByPosition(4);</action>
<action on="KeyDown">addTrainingByPosition(4);</action>
</object>
<!-- queue 6th unit in the training queue -->
<object hotkey="session.queueunit.6">
<action on="Press">addTrainingByPosition(5);</action>
<action on="KeyDown">addTrainingByPosition(5);</action>
</object>
<!-- queue 7th unit in the training queue -->
<object hotkey="session.queueunit.7">
<action on="Press">addTrainingByPosition(6);</action>
<action on="KeyDown">addTrainingByPosition(6);</action>
</object>
</object>

View File

@ -6,7 +6,8 @@ class MiniMapIdleWorkerButton
constructor(playerViewControl, idleClasses)
{
this.idleWorkerButton = Engine.GetGUIObjectByName("idleWorkerButton");
this.idleWorkerButton.onPress = this.onPress.bind(this);
this.idleWorkerButton.onKeyDown = this.onKeyDown.bind(this);
this.idleWorkerButton.onMouseLeftPress = this.onKeyDown.bind(this);
this.idleClasses = idleClasses;
registerHotkeyChangeHandler(this.onHotkeyChange.bind(this));
@ -30,7 +31,7 @@ class MiniMapIdleWorkerButton
});
}
onPress()
onKeyDown()
{
findIdleUnit(this.idleClasses);
}

View File

@ -18,8 +18,8 @@ class CivIcon
playerViewControl.registerViewedPlayerChangeHandler(this.rebuild.bind(this));
registerHotkeyChangeHandler(this.rebuild.bind(this));
Engine.SetGlobalHotkey("civinfo", () => this.openPage("page_civinfo.xml"));
Engine.SetGlobalHotkey("structree", () => this.openPage("page_structree.xml"));
Engine.SetGlobalHotkey("structree", "Press", this.openPage.bind(this, "page_structree.xml"));
Engine.SetGlobalHotkey("civinfo", "Press", this.openPage.bind(this, "page_civinfo.xml"));
}
onPress()

View File

@ -17,11 +17,11 @@
</object>
<object hotkey="tab.next">
<action on="Press">selectNextTab(1);</action>
<action on="KeyDown">selectNextTab(1);</action>
</object>
<object hotkey="tab.prev">
<action on="Press">selectNextTab(-1);</action>
<action on="KeyDown">selectNextTab(-1);</action>
</object>
<object type="image" sprite="ModernFade" name="fadeImage"/>

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -640,7 +640,19 @@ InReaction CCameraController::HandleEvent(const SDL_Event_* ev)
{
switch (ev->ev.type)
{
case SDL_HOTKEYPRESS:
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
if (hotkey == "camera.reset")
{
ResetCameraAngleZoom();
return IN_HANDLED;
}
return IN_PASS;
}
case SDL_HOTKEYDOWN:
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
// Mouse wheel must be treated using events instead of polling,
@ -698,11 +710,8 @@ InReaction CCameraController::HandleEvent(const SDL_Event_* ev)
m_ViewZoomSpeed /= m_ViewZoomSpeedModifier;
return IN_HANDLED;
}
else if (hotkey == "camera.reset")
{
ResetCameraAngleZoom();
return IN_HANDLED;
}
return IN_PASS;
}
}
return IN_PASS;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -378,10 +378,9 @@ InReaction CGameView::HandleEvent(const SDL_Event_* ev)
{
switch(ev->ev.type)
{
case SDL_HOTKEYDOWN:
case SDL_HOTKEYPRESS:
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
if (hotkey == "wireframe")
{
if (g_XmppClient && g_rankedGame == true)
@ -407,6 +406,7 @@ InReaction CGameView::HandleEvent(const SDL_Event_* ev)
return IN_HANDLED;
}
}
}
return m->CameraController->HandleEvent(ev);
}

View File

@ -50,6 +50,7 @@ const u32 MAX_OBJECT_DEPTH = 100; // Max number of nesting for GUI includes. Use
const CStr CGUI::EventNameLoad = "Load";
const CStr CGUI::EventNameTick = "Tick";
const CStr CGUI::EventNamePress = "Press";
const CStr CGUI::EventNameKeyDown = "KeyDown";
const CStr CGUI::EventNameRelease = "Release";
const CStr CGUI::EventNameMouseRightPress = "MouseRightPress";
const CStr CGUI::EventNameMouseLeftPress = "MouseLeftPress";
@ -86,11 +87,13 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
{
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<const char*>(ev->ev.user.data1);
if (m_GlobalHotkeys.find(hotkey) != m_GlobalHotkeys.end() && ev->ev.type == SDL_HOTKEYDOWN)
const CStr& eventName = ev->ev.type == SDL_HOTKEYPRESS ? EventNamePress : ev->ev.type == SDL_HOTKEYDOWN ? EventNameKeyDown : EventNameRelease;
if (m_GlobalHotkeys.find(hotkey) != m_GlobalHotkeys.end() && m_GlobalHotkeys[hotkey].find(eventName) != m_GlobalHotkeys[hotkey].end())
{
ret = IN_HANDLED;
@ -98,15 +101,17 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
JSAutoRequest rq(cx);
JS::RootedObject globalObj(cx, &GetGlobalObject().toObject());
JS::RootedValue result(cx);
JS_CallFunctionValue(cx, globalObj, m_GlobalHotkeys[hotkey], JS::HandleValueArray::empty(), &result);
JS_CallFunctionValue(cx, globalObj, m_GlobalHotkeys[hotkey][eventName], JS::HandleValueArray::empty(), &result);
}
std::map<CStr, std::vector<IGUIObject*> >::iterator it = m_HotkeyObjects.find(hotkey);
if (it != m_HotkeyObjects.end())
for (IGUIObject* const& obj : it->second)
{
if (ev->ev.type == SDL_HOTKEYDOWN)
if (ev->ev.type == SDL_HOTKEYPRESS)
ret = obj->SendEvent(GUIM_PRESSED, EventNamePress);
else if (ev->ev.type == SDL_HOTKEYDOWN)
ret = obj->SendEvent(GUIM_KEYDOWN, EventNameKeyDown);
else
ret = obj->SendEvent(GUIM_RELEASED, EventNameRelease);
}
@ -400,7 +405,7 @@ void CGUI::UnsetObjectHotkey(IGUIObject* pObject, const CStr& hotkeyTag)
assignment.end());
}
void CGUI::SetGlobalHotkey(const CStr& hotkeyTag, JS::HandleValue function)
void CGUI::SetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName, JS::HandleValue function)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
@ -411,19 +416,33 @@ void CGUI::SetGlobalHotkey(const CStr& hotkeyTag, JS::HandleValue function)
return;
}
// Only support "Press", "Keydown" and "Release" events.
if (eventName != EventNamePress && eventName != EventNameKeyDown && eventName != EventNameRelease)
{
JS_ReportError(cx, "Cannot assign a function to an unsupported event!");
return;
}
if (!function.isObject() || !JS_ObjectIsFunction(cx, &function.toObject()))
{
JS_ReportError(cx, "Cannot assign non-function value to global hotkey '%s'", hotkeyTag.c_str());
return;
}
UnsetGlobalHotkey(hotkeyTag);
m_GlobalHotkeys[hotkeyTag].init(cx, function);
UnsetGlobalHotkey(hotkeyTag, eventName);
m_GlobalHotkeys[hotkeyTag][eventName].init(cx, function);
}
void CGUI::UnsetGlobalHotkey(const CStr& hotkeyTag)
void CGUI::UnsetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName)
{
m_GlobalHotkeys.erase(hotkeyTag);
std::map<CStr, std::map<CStr, JS::PersistentRootedValue>>::iterator it = m_GlobalHotkeys.find(hotkeyTag);
if (it == m_GlobalHotkeys.end())
return;
m_GlobalHotkeys[hotkeyTag].erase(eventName);
if (m_GlobalHotkeys.count(hotkeyTag) == 0)
m_GlobalHotkeys.erase(it);
}
const SGUIScrollBarStyle* CGUI::GetScrollBarStyle(const CStr& style) const

View File

@ -136,8 +136,8 @@ public:
/**
* Allows the JS side to add or remove global hotkeys.
*/
void SetGlobalHotkey(const CStr& hotkeyTag, JS::HandleValue function);
void UnsetGlobalHotkey(const CStr& hotkeyTag);
void SetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName, JS::HandleValue function);
void UnsetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName);
/**
* Return the object which is an ancestor of every other GUI object.
@ -606,10 +606,11 @@ private:
std::map<CStr, std::vector<IGUIObject*> > m_HotkeyObjects;
/**
* Map from hotkey names to functions that are triggered if the hotkey is pressed.
* Contrary to object hotkeys, this allows for only one global function per hotkey name.
* Map from hotkey names to maps of eventNames to functions that are triggered
* when the hotkey goes through the event. Contrary to object hotkeys, this
* allows for only one global function per hotkey name per event type.
*/
std::map<CStr, JS::PersistentRootedValue> m_GlobalHotkeys;
std::map<CStr, std::map<CStr, JS::PersistentRootedValue>> m_GlobalHotkeys;
/**
* XML and JS can subscribe handlers to events identified by these names.
@ -619,6 +620,7 @@ private:
static const CStr EventNameLoad;
static const CStr EventNameTick;
static const CStr EventNamePress;
static const CStr EventNameKeyDown;
static const CStr EventNameRelease;
static const CStr EventNameMouseRightPress;
static const CStr EventNameMouseLeftPress;

View File

@ -43,6 +43,7 @@ enum EGUIMessageType
GUIM_MOUSE_WHEEL_DOWN,
GUIM_SETTINGS_UPDATED, // SGUIMessage.m_Value = name of setting
GUIM_PRESSED,
GUIM_KEYDOWN,
GUIM_RELEASED,
GUIM_DOUBLE_PRESSED,
GUIM_MOUSE_MOTION,

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -61,16 +61,16 @@ JS::Value JSI_GUIManager::GetGUIObjectByName(ScriptInterface::CxPrivate* pCxPriv
return JS::ObjectValue(*guiObj->GetJSObject());
}
void JSI_GUIManager::SetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, JS::HandleValue function)
void JSI_GUIManager::SetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, const std::string& eventName, JS::HandleValue function)
{
CGUI* guiPage = static_cast<CGUI*>(pCxPrivate->pCBData);
guiPage->SetGlobalHotkey(hotkeyTag, function);
guiPage->SetGlobalHotkey(hotkeyTag, eventName, function);
}
void JSI_GUIManager::UnsetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag)
void JSI_GUIManager::UnsetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, const std::string& eventName)
{
CGUI* guiPage = static_cast<CGUI*>(pCxPrivate->pCBData);
guiPage->UnsetGlobalHotkey(hotkeyTag);
guiPage->UnsetGlobalHotkey(hotkeyTag, eventName);
}
std::wstring JSI_GUIManager::SetCursor(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& name)
@ -99,8 +99,8 @@ void JSI_GUIManager::RegisterScriptFunctions(const ScriptInterface& scriptInterf
{
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, JS::HandleValue, &PushGuiPage>("PushGuiPage");
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &SwitchGuiPage>("SwitchGuiPage");
scriptInterface.RegisterFunction<void, std::string, JS::HandleValue, &SetGlobalHotkey>("SetGlobalHotkey");
scriptInterface.RegisterFunction<void, std::string, &UnsetGlobalHotkey>("UnsetGlobalHotkey");
scriptInterface.RegisterFunction<void, std::string, std::string, JS::HandleValue, &SetGlobalHotkey>("SetGlobalHotkey");
scriptInterface.RegisterFunction<void, std::string, std::string, &UnsetGlobalHotkey>("UnsetGlobalHotkey");
scriptInterface.RegisterFunction<void, JS::HandleValue, &PopGuiPage>("PopGuiPage");
scriptInterface.RegisterFunction<JS::Value, std::string, &GetGUIObjectByName>("GetGUIObjectByName");
scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor");

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -27,8 +27,8 @@ namespace JSI_GUIManager
void SwitchGuiPage(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name, JS::HandleValue initData);
void PopGuiPage(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue args);
JS::Value GetGUIObjectByName(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name);
void SetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, JS::HandleValue function);
void UnsetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag);
void SetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, const std::string& eventName, JS::HandleValue function);
void UnsetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, const std::string& eventName);
std::wstring SetCursor(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name);
void ResetCursor(ScriptInterface::CxPrivate* pCxPrivate);
bool TemplateExists(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -74,6 +74,7 @@ public:
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = SDL_KEYDOWN;
hotkeyNotification.ev.key.keysym.sym = SDLK_a;
hotkeyNotification.ev.key.repeat = 0;
// Init input and poll the event.
InitInput();
@ -100,6 +101,22 @@ public:
ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value);
TS_ASSERT_EQUALS(hotkey_pressed_value, true);
// We are listening to KeyDown events, so repeat shouldn't matter.
hotkeyNotification.ev.key.repeat = 1;
in_push_priority_event(&hotkeyNotification);
while (in_poll_event(&ev))
in_dispatch_event(&ev);
hotkey_pressed_value = false;
pageScriptInterface.GetProperty(global, "state_before", &js_hotkey_pressed_value);
ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value);
TS_ASSERT_EQUALS(hotkey_pressed_value, true);
hotkey_pressed_value = false;
pageScriptInterface.GetProperty(global, "state_after", &js_hotkey_pressed_value);
ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value);
TS_ASSERT_EQUALS(hotkey_pressed_value, true);
hotkeyNotification.ev.type = SDL_KEYUP;
in_push_priority_event(&hotkeyNotification);
while (in_poll_event(&ev))

View File

@ -171,7 +171,7 @@ static InReaction MainInputHandler(const SDL_Event_* ev)
QuitEngine();
break;
case SDL_HOTKEYDOWN:
case SDL_HOTKEYPRESS:
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
if (hotkey == "exit")
{

View File

@ -637,7 +637,7 @@ InReaction conInputHandler(const SDL_Event_* ev)
if (!g_Console)
return IN_PASS;
if ((int)ev->ev.type == SDL_HOTKEYDOWN)
if (static_cast<int>(ev->ev.type) == SDL_HOTKEYPRESS)
{
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -154,7 +154,7 @@ bool isNegated(const SKey& key)
InReaction HotkeyStateChange(const SDL_Event_* ev)
{
if (ev->ev.type == SDL_HOTKEYDOWN)
if (ev->ev.type == SDL_HOTKEYPRESS)
g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = true;
else if (ev->ev.type == SDL_HOTKEYUP)
g_HotkeyStatus[static_cast<const char*>(ev->ev.user.data1)] = false;
@ -216,6 +216,9 @@ InReaction HotkeyInputHandler(const SDL_Event_* ev)
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;
@ -259,7 +262,7 @@ InReaction HotkeyInputHandler(const SDL_Event_* ev)
// 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)
@ -306,10 +309,22 @@ InReaction HotkeyInputHandler(const SDL_Event_* ev)
for (size_t i = 0; i < closestMapNames.size(); ++i)
{
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = SDL_HOTKEYDOWN;
hotkeyNotification.ev.user.data1 = const_cast<char*>(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_ hotkeyPressNotification;
hotkeyPressNotification.ev.type = SDL_HOTKEYPRESS;
hotkeyPressNotification.ev.user.data1 = const_cast<char*>(closestMapNames[i]);
in_push_priority_event(&hotkeyPressNotification);
}
// 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_ hotkeyDownNotification;
hotkeyDownNotification.ev.type = SDL_HOTKEYDOWN;
hotkeyDownNotification.ev.user.data1 = const_cast<char*>(closestMapNames[i]);
in_push_priority_event(&hotkeyDownNotification);
}
// -- KEYUP SECTION --

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 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,10 @@
* 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 is triggered. While the key is
* kept down repeated SDL_HOTKEYDOWN events are triggered at an interval
* determined by the OS. 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 +39,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 uint SDL_HOTKEYPRESS = SDL_USEREVENT;
const uint SDL_HOTKEYDOWN = SDL_USEREVENT + 1;
const uint SDL_HOTKEYUP = SDL_USEREVENT + 2;
extern void LoadHotkeys();
extern void UnloadHotkeys();

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -336,7 +336,7 @@ InReaction CProfileViewer::Input(const SDL_Event_* ev)
}
break;
}
case SDL_HOTKEYDOWN:
case SDL_HOTKEYPRESS:
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
if( hotkey == "profile.toggle" )