Merge pull request #8395 from Robyt3/Client-Input-Event-Refactoring

Refactor client input event handling
This commit is contained in:
Dennis Felsing 2024-06-05 05:26:23 +00:00 committed by GitHub
commit 5eda5e9fe3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 101 additions and 103 deletions

View file

@ -33,19 +33,25 @@
// for platform specific features that aren't available or are broken in SDL // for platform specific features that aren't available or are broken in SDL
#include <SDL_syswm.h> #include <SDL_syswm.h>
void CInput::AddEvent(char *pText, int Key, int Flags) void CInput::AddKeyEvent(int Key, int Flags)
{ {
if(m_NumEvents != INPUT_BUFFER_SIZE) dbg_assert((Flags & (FLAG_PRESS | FLAG_RELEASE)) != 0 && (Flags & ~(FLAG_PRESS | FLAG_RELEASE)) == 0, "Flags invalid");
{ CEvent Event;
m_aInputEvents[m_NumEvents].m_Key = Key; Event.m_Key = Key;
m_aInputEvents[m_NumEvents].m_Flags = Flags; Event.m_Flags = Flags;
if(pText == nullptr) Event.m_aText[0] = '\0';
m_aInputEvents[m_NumEvents].m_aText[0] = '\0'; Event.m_InputCount = m_InputCounter;
else m_vInputEvents.emplace_back(Event);
str_copy(m_aInputEvents[m_NumEvents].m_aText, pText); }
m_aInputEvents[m_NumEvents].m_InputCount = m_InputCounter;
m_NumEvents++; void CInput::AddTextEvent(const char *pText)
} {
CEvent Event;
Event.m_Key = KEY_UNKNOWN;
Event.m_Flags = FLAG_TEXT;
str_copy(Event.m_aText, pText);
Event.m_InputCount = m_InputCounter;
m_vInputEvents.emplace_back(Event);
} }
CInput::CInput() CInput::CInput()
@ -53,6 +59,7 @@ CInput::CInput()
mem_zero(m_aInputCount, sizeof(m_aInputCount)); mem_zero(m_aInputCount, sizeof(m_aInputCount));
mem_zero(m_aInputState, sizeof(m_aInputState)); mem_zero(m_aInputState, sizeof(m_aInputState));
m_vInputEvents.reserve(32);
m_LastUpdate = 0; m_LastUpdate = 0;
m_UpdateTime = 0.0f; m_UpdateTime = 0.0f;
@ -60,8 +67,6 @@ CInput::CInput()
m_InputGrabbed = false; m_InputGrabbed = false;
m_MouseDoubleClick = false; m_MouseDoubleClick = false;
m_NumEvents = 0;
m_MouseFocus = true; m_MouseFocus = true;
m_pClipboardText = nullptr; m_pClipboardText = nullptr;
@ -342,11 +347,29 @@ void CInput::StopTextInput()
m_vCandidates.clear(); m_vCandidates.clear();
} }
void CInput::ConsumeEvents(std::function<void(const CEvent &Event)> Consumer) const
{
for(const CEvent &Event : m_vInputEvents)
{
// Only propagate valid input events
if(Event.m_InputCount == m_InputCounter)
{
Consumer(Event);
}
}
}
void CInput::Clear() void CInput::Clear()
{ {
mem_zero(m_aInputState, sizeof(m_aInputState)); mem_zero(m_aInputState, sizeof(m_aInputState));
mem_zero(m_aInputCount, sizeof(m_aInputCount)); mem_zero(m_aInputCount, sizeof(m_aInputCount));
m_NumEvents = 0; m_vInputEvents.clear();
m_MouseDoubleClick = false;
}
float CInput::GetUpdateTime() const
{
return m_UpdateTime;
} }
bool CInput::KeyState(int Key) const bool CInput::KeyState(int Key) const
@ -424,24 +447,24 @@ void CInput::HandleJoystickAxisMotionEvent(const SDL_JoyAxisEvent &Event)
{ {
m_aInputState[LeftKey] = true; m_aInputState[LeftKey] = true;
m_aInputCount[LeftKey] = m_InputCounter; m_aInputCount[LeftKey] = m_InputCounter;
AddEvent(nullptr, LeftKey, IInput::FLAG_PRESS); AddKeyEvent(LeftKey, IInput::FLAG_PRESS);
} }
else if(Event.value > SDL_JOYSTICK_AXIS_MIN * DeadZone && m_aInputState[LeftKey]) else if(Event.value > SDL_JOYSTICK_AXIS_MIN * DeadZone && m_aInputState[LeftKey])
{ {
m_aInputState[LeftKey] = false; m_aInputState[LeftKey] = false;
AddEvent(nullptr, LeftKey, IInput::FLAG_RELEASE); AddKeyEvent(LeftKey, IInput::FLAG_RELEASE);
} }
if(Event.value >= SDL_JOYSTICK_AXIS_MAX * DeadZone && !m_aInputState[RightKey]) if(Event.value >= SDL_JOYSTICK_AXIS_MAX * DeadZone && !m_aInputState[RightKey])
{ {
m_aInputState[RightKey] = true; m_aInputState[RightKey] = true;
m_aInputCount[RightKey] = m_InputCounter; m_aInputCount[RightKey] = m_InputCounter;
AddEvent(nullptr, RightKey, IInput::FLAG_PRESS); AddKeyEvent(RightKey, IInput::FLAG_PRESS);
} }
else if(Event.value < SDL_JOYSTICK_AXIS_MAX * DeadZone && m_aInputState[RightKey]) else if(Event.value < SDL_JOYSTICK_AXIS_MAX * DeadZone && m_aInputState[RightKey])
{ {
m_aInputState[RightKey] = false; m_aInputState[RightKey] = false;
AddEvent(nullptr, RightKey, IInput::FLAG_RELEASE); AddKeyEvent(RightKey, IInput::FLAG_RELEASE);
} }
} }
@ -461,12 +484,12 @@ void CInput::HandleJoystickButtonEvent(const SDL_JoyButtonEvent &Event)
{ {
m_aInputState[Key] = true; m_aInputState[Key] = true;
m_aInputCount[Key] = m_InputCounter; m_aInputCount[Key] = m_InputCounter;
AddEvent(nullptr, Key, IInput::FLAG_PRESS); AddKeyEvent(Key, IInput::FLAG_PRESS);
} }
else if(Event.type == SDL_JOYBUTTONUP) else if(Event.type == SDL_JOYBUTTONUP)
{ {
m_aInputState[Key] = false; m_aInputState[Key] = false;
AddEvent(nullptr, Key, IInput::FLAG_RELEASE); AddKeyEvent(Key, IInput::FLAG_RELEASE);
} }
} }
@ -488,7 +511,7 @@ void CInput::HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event)
if(Key != HatKeys[0] && Key != HatKeys[1] && m_aInputState[Key]) if(Key != HatKeys[0] && Key != HatKeys[1] && m_aInputState[Key])
{ {
m_aInputState[Key] = false; m_aInputState[Key] = false;
AddEvent(nullptr, Key, IInput::FLAG_RELEASE); AddKeyEvent(Key, IInput::FLAG_RELEASE);
} }
} }
@ -498,7 +521,7 @@ void CInput::HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event)
{ {
m_aInputState[CurrentKey] = true; m_aInputState[CurrentKey] = true;
m_aInputCount[CurrentKey] = m_InputCounter; m_aInputCount[CurrentKey] = m_InputCounter;
AddEvent(nullptr, CurrentKey, IInput::FLAG_PRESS); AddKeyEvent(CurrentKey, IInput::FLAG_PRESS);
} }
} }
} }
@ -582,8 +605,8 @@ int CInput::Update()
} }
m_LastUpdate = Now; m_LastUpdate = Now;
// keep the counter between 1..0xFFFF, 0 means not pressed // keep the counter between 1..0xFFFFFFFF, 0 means not pressed
m_InputCounter = (m_InputCounter % 0xFFFF) + 1; m_InputCounter = (m_InputCounter % std::numeric_limits<decltype(m_InputCounter)>::max()) + 1;
// Ensure that we have the latest keyboard, mouse and joystick state // Ensure that we have the latest keyboard, mouse and joystick state
SDL_PumpEvents(); SDL_PumpEvents();
@ -621,7 +644,7 @@ int CInput::Update()
for(int i = 0; i < Event.edit.start; i++) for(int i = 0; i < Event.edit.start; i++)
m_CompositionCursor = str_utf8_forward(m_aComposition, m_CompositionCursor); m_CompositionCursor = str_utf8_forward(m_aComposition, m_CompositionCursor);
// Event.edit.length is currently unused on Windows and will always be 0, so we don't support selecting composition text // Event.edit.length is currently unused on Windows and will always be 0, so we don't support selecting composition text
AddEvent(nullptr, KEY_UNKNOWN, IInput::FLAG_TEXT); AddTextEvent("");
} }
else else
{ {
@ -636,7 +659,7 @@ int CInput::Update()
m_aComposition[0] = '\0'; m_aComposition[0] = '\0';
m_CompositionLength = COMP_LENGTH_INACTIVE; m_CompositionLength = COMP_LENGTH_INACTIVE;
m_CompositionCursor = 0; m_CompositionCursor = 0;
AddEvent(Event.text.text, KEY_UNKNOWN, IInput::FLAG_TEXT); AddTextEvent(Event.text.text);
break; break;
// handle keys // handle keys
@ -806,7 +829,7 @@ int CInput::Update()
m_aInputState[Scancode] = 1; m_aInputState[Scancode] = 1;
m_aInputCount[Scancode] = m_InputCounter; m_aInputCount[Scancode] = m_InputCounter;
} }
AddEvent(nullptr, Scancode, Action); AddKeyEvent(Scancode, Action);
} }
} }

View file

@ -92,14 +92,17 @@ private:
std::vector<std::string> m_vCandidates; std::vector<std::string> m_vCandidates;
int m_CandidateSelectedIndex; int m_CandidateSelectedIndex;
void AddEvent(char *pText, int Key, int Flags); // events
void Clear() override; std::vector<CEvent> m_vInputEvents;
bool IsEventValid(const CEvent &Event) const override { return Event.m_InputCount == m_InputCounter; } int64_t m_LastUpdate;
float m_UpdateTime;
void AddKeyEvent(int Key, int Flags);
void AddTextEvent(const char *pText);
// quick access to input // quick access to input
unsigned short m_aInputCount[g_MaxKeys]; // tw-KEY uint32_t m_aInputCount[g_MaxKeys];
unsigned char m_aInputState[g_MaxKeys]; // SDL_SCANCODE unsigned char m_aInputState[g_MaxKeys];
int m_InputCounter; uint32_t m_InputCounter;
void UpdateMouseState(); void UpdateMouseState();
void UpdateJoystickState(); void UpdateJoystickState();
@ -122,6 +125,10 @@ public:
int Update() override; int Update() override;
void Shutdown() override; void Shutdown() override;
void ConsumeEvents(std::function<void(const CEvent &Event)> Consumer) const override;
void Clear() override;
float GetUpdateTime() const override;
bool ModifierIsPressed() const override { return KeyState(KEY_LCTRL) || KeyState(KEY_RCTRL) || KeyState(KEY_LGUI) || KeyState(KEY_RGUI); } bool ModifierIsPressed() const override { return KeyState(KEY_LCTRL) || KeyState(KEY_RCTRL) || KeyState(KEY_LGUI) || KeyState(KEY_RGUI); }
bool ShiftIsPressed() const override { return KeyState(KEY_LSHIFT) || KeyState(KEY_RSHIFT); } bool ShiftIsPressed() const override { return KeyState(KEY_LSHIFT) || KeyState(KEY_RSHIFT); }
bool AltIsPressed() const override { return KeyState(KEY_LALT) || KeyState(KEY_RALT); } bool AltIsPressed() const override { return KeyState(KEY_LALT) || KeyState(KEY_RALT); }

View file

@ -4,7 +4,11 @@
#define ENGINE_INPUT_H #define ENGINE_INPUT_H
#include "kernel.h" #include "kernel.h"
#include <base/system.h>
#include <base/types.h>
#include <cstdint>
#include <functional>
const int g_MaxKeys = 512; const int g_MaxKeys = 512;
extern const char g_aaKeyStrings[g_MaxKeys][20]; extern const char g_aaKeyStrings[g_MaxKeys][20];
@ -23,23 +27,10 @@ public:
public: public:
int m_Flags; int m_Flags;
int m_Key; int m_Key;
uint32_t m_InputCount;
char m_aText[INPUT_TEXT_SIZE]; char m_aText[INPUT_TEXT_SIZE];
int m_InputCount;
}; };
protected:
enum
{
INPUT_BUFFER_SIZE = 32
};
// quick access to events
size_t m_NumEvents;
CEvent m_aInputEvents[INPUT_BUFFER_SIZE];
int64_t m_LastUpdate;
float m_UpdateTime;
public:
enum enum
{ {
FLAG_PRESS = 1 << 0, FLAG_PRESS = 1 << 0,
@ -60,19 +51,14 @@ public:
}; };
// events // events
size_t NumEvents() const { return m_NumEvents; } virtual void ConsumeEvents(std::function<void(const CEvent &Event)> Consumer) const = 0;
virtual bool IsEventValid(const CEvent &Event) const = 0; virtual void Clear() = 0;
const CEvent &GetEvent(size_t Index) const
{
dbg_assert(Index < m_NumEvents, "Index invalid");
return m_aInputEvents[Index];
}
/** /**
* @return Rolling average of the time in seconds between * @return Rolling average of the time in seconds between
* calls of the Update function. * calls of the Update function.
*/ */
float GetUpdateTime() const { return m_UpdateTime; } virtual float GetUpdateTime() const = 0;
// keys // keys
virtual bool ModifierIsPressed() const = 0; virtual bool ModifierIsPressed() const = 0;
@ -81,7 +67,6 @@ public:
virtual bool KeyIsPressed(int Key) const = 0; virtual bool KeyIsPressed(int Key) const = 0;
virtual bool KeyPress(int Key, bool CheckCounter = false) const = 0; virtual bool KeyPress(int Key, bool CheckCounter = false) const = 0;
const char *KeyName(int Key) const { return (Key >= 0 && Key < g_MaxKeys) ? g_aaKeyStrings[Key] : g_aaKeyStrings[0]; } const char *KeyName(int Key) const { return (Key >= 0 && Key < g_MaxKeys) ? g_aaKeyStrings[Key] : g_aaKeyStrings[0]; }
virtual void Clear() = 0;
// joystick // joystick
class IJoystick class IJoystick

View file

@ -2,6 +2,8 @@
#include "gameclient.h" #include "gameclient.h"
#include <base/system.h>
class IKernel *CComponent::Kernel() const { return m_pClient->Kernel(); } class IKernel *CComponent::Kernel() const { return m_pClient->Kernel(); }
class IEngine *CComponent::Engine() const { return m_pClient->Engine(); } class IEngine *CComponent::Engine() const { return m_pClient->Engine(); }
class IGraphics *CComponent::Graphics() const { return m_pClient->Graphics(); } class IGraphics *CComponent::Graphics() const { return m_pClient->Graphics(); }
@ -27,6 +29,15 @@ class IUpdater *CComponent::Updater() const
} }
#endif #endif
int64_t CComponent::time() const
{
#if defined(CONF_VIDEORECORDER)
return IVideo::Current() ? IVideo::Time() : time_get();
#else
return time_get();
#endif
}
float CComponent::LocalTime() const float CComponent::LocalTime() const
{ {
#if defined(CONF_VIDEORECORDER) #if defined(CONF_VIDEORECORDER)

View file

@ -104,25 +104,12 @@ protected:
class IUpdater *Updater() const; class IUpdater *Updater() const;
#endif #endif
#if defined(CONF_VIDEORECORDER)
/** /**
* Gets the current time. * Gets the current time.
* @see time_get() * @see time_get()
*/ */
int64_t time() const int64_t time() const;
{
return IVideo::Current() ? IVideo::Time() : time_get();
}
#else
/**
* Gets the current time.
* @see time_get()
*/
int64_t time() const
{
return time_get();
}
#endif
/** /**
* Gets the local time. * Gets the local time.
*/ */

View file

@ -2,6 +2,8 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "voting.h" #include "voting.h"
#include <base/system.h>
#include <engine/shared/config.h> #include <engine/shared/config.h>
#include <engine/textrender.h> #include <engine/textrender.h>
@ -139,6 +141,11 @@ void CVoting::Vote(int v)
Client()->SendPackMsgActive(&Msg, MSGFLAG_VITAL); Client()->SendPackMsgActive(&Msg, MSGFLAG_VITAL);
} }
int CVoting::SecondsLeft() const
{
return (m_Closetime - time()) / time_freq();
}
CVoting::CVoting() CVoting::CVoting()
{ {
ClearOptions(); ClearOptions();

View file

@ -57,8 +57,8 @@ public:
void Vote(int v); // -1 = no, 1 = yes void Vote(int v); // -1 = no, 1 = yes
int SecondsLeft() { return (m_Closetime - time()) / time_freq(); } int SecondsLeft() const;
bool IsVoting() { return m_Closetime != 0; } bool IsVoting() const { return m_Closetime != 0; }
int TakenChoice() const { return m_Voted; } int TakenChoice() const { return m_Voted; }
const char *VoteDescription() const { return m_aDescription; } const char *VoteDescription() const { return m_aDescription; }
const char *VoteReason() const { return m_aReason; } const char *VoteReason() const { return m_aReason; }

View file

@ -385,18 +385,13 @@ void CGameClient::OnUpdate()
} }
// handle key presses // handle key presses
for(size_t i = 0; i < Input()->NumEvents(); i++) Input()->ConsumeEvents([&](const IInput::CEvent &Event) {
{
const IInput::CEvent &Event = Input()->GetEvent(i);
if(!Input()->IsEventValid(Event))
continue;
for(auto &pComponent : m_vpInput) for(auto &pComponent : m_vpInput)
{ {
if(pComponent->OnInput(Event)) if(pComponent->OnInput(Event))
break; break;
} }
} });
if(g_Config.m_ClSubTickAiming && m_Binds.m_MouseOnAction) if(g_Config.m_ClSubTickAiming && m_Binds.m_MouseOnAction)
{ {

View file

@ -8482,17 +8482,6 @@ void CEditor::OnMouseMove(float MouseX, float MouseY)
Ui()->MapScreen(); Ui()->MapScreen();
} }
void CEditor::DispatchInputEvents()
{
for(size_t i = 0; i < Input()->NumEvents(); i++)
{
const IInput::CEvent &Event = Input()->GetEvent(i);
if(!Input()->IsEventValid(Event))
continue;
Ui()->OnInput(Event);
}
}
void CEditor::HandleAutosave() void CEditor::HandleAutosave()
{ {
const float Time = Client()->GlobalTime(); const float Time = Client()->GlobalTime();
@ -8624,21 +8613,16 @@ void CEditor::OnUpdate()
m_pContainerPannedLast = m_pContainerPanned; m_pContainerPannedLast = m_pContainerPanned;
// handle key presses // handle key presses
for(size_t i = 0; i < Input()->NumEvents(); i++) Input()->ConsumeEvents([&](const IInput::CEvent &Event) {
{
const IInput::CEvent &Event = Input()->GetEvent(i);
if(!Input()->IsEventValid(Event))
continue;
for(CEditorComponent &Component : m_vComponents) for(CEditorComponent &Component : m_vComponents)
{ {
if(Component.OnInput(Event)) if(Component.OnInput(Event))
break; return;
} }
} Ui()->OnInput(Event);
});
HandleCursorMovement(); HandleCursorMovement();
DispatchInputEvents();
HandleAutosave(); HandleAutosave();
HandleWriterFinishJobs(); HandleWriterFinishJobs();

View file

@ -465,7 +465,6 @@ public:
void HandleCursorMovement(); void HandleCursorMovement();
void OnMouseMove(float MouseX, float MouseY); void OnMouseMove(float MouseX, float MouseY);
void DispatchInputEvents();
void HandleAutosave(); void HandleAutosave();
bool PerformAutosave(); bool PerformAutosave();
void HandleWriterFinishJobs(); void HandleWriterFinishJobs();