-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDekiInputSystem.cpp
More file actions
219 lines (184 loc) · 6.4 KB
/
Copy pathDekiInputSystem.cpp
File metadata and controls
219 lines (184 loc) · 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include "DekiInputSystem.h"
#include "InputCollider.h"
#include "DekiEngine.h"
#include "DekiObject.h"
#include "Prefab.h"
#include "deki-rendering/CameraComponent.h"
#include "DekiInput.h" // now local to this module
#include "providers/IDekiRenderSystem.h"
#include <map>
#include <memory>
namespace
{
// Keyboard state fed by DispatchKey() rather than a physical device driver.
// Registered with DekiInput on first injected key so IsKeyPressed() aggregates
// injected keys exactly like keys from a real driver (SDL3 on the desktop
// simulator). Owned by DekiInput once registered.
class InjectedKeyboard : public IDekiInput
{
public:
bool Initialize() override { return true; }
void Shutdown() override { m_Keys.clear(); }
void Update() override {}
void RegisterEventCallback(const InputEventCallback& callback) override { m_Callback = callback; }
bool IsInitialized() const override { return true; }
bool GetPointerPosition(int32_t*, int32_t*) const override { return false; }
bool IsKeyPressed(uint32_t key) const override
{
auto it = m_Keys.find(key);
return it != m_Keys.end() && it->second;
}
void SetKey(uint32_t key, bool down)
{
m_Keys[key] = down;
if (m_Callback)
{
InputEvent event{};
event.type = down ? InputEventType::KEY_DOWN : InputEventType::KEY_UP;
event.key = key;
event.pressed = down;
m_Callback(event);
}
}
private:
std::map<uint32_t, bool> m_Keys;
InputEventCallback m_Callback;
};
constexpr const char* kInjectedKeyboardName = "Injected";
} // namespace
DekiInputSystem::DekiInputSystem()
{
}
DekiInputSystem::~DekiInputSystem()
{
Shutdown();
}
void DekiInputSystem::Initialize()
{
if (m_Initialized)
return;
// Register callback on DekiInput to receive input events
DekiInput::RegisterEventCallback([this](const InputEvent& event) {
OnInputEvent(event);
});
m_Initialized = true;
}
void DekiInputSystem::Shutdown()
{
// Clear callbacks BEFORE DLL unload — the std::function objects in
// DekiInput::global_callbacks hold lambdas whose code lives
// in this DLL. After FreeLibrary, those function pointers are stale
// and any operation on them (move, copy, destroy) will crash.
DekiInput::ClearEventCallbacks();
// Tear down active input drivers (mouse / keyboard / touch). DekiEngine
// reaches this through the interface so it doesn't need to link to the
// static DekiInput symbol in this module DLL.
DekiInput::Shutdown();
m_Initialized = false;
}
void DekiInputSystem::Update()
{
DekiInput::Update();
}
void DekiInputSystem::DispatchKey(uint32_t key, bool down)
{
auto* driver = DekiInput::GetInput(kInjectedKeyboardName);
if (!driver)
{
// Registered on demand so the driver only exists for hosts that
// actually inject keys; DekiInput owns it from here on and clears it
// on DekiInput::Shutdown (looked up by name each call, never cached).
DekiInput::SetInput(std::make_unique<InjectedKeyboard>(), kInjectedKeyboardName);
driver = DekiInput::GetInput(kInjectedKeyboardName);
if (!driver)
return;
}
static_cast<InjectedKeyboard*>(driver)->SetKey(key, down);
}
bool DekiInputSystem::ShouldExit() const
{
return DekiInput::ShouldExit();
}
void DekiInputSystem::OnInputEvent(const InputEvent& event)
{
DekiEngine& engine = DekiEngine::GetInstance();
if (!engine.IsInitialized())
return;
Prefab* prefab = engine.GetRootPrefab();
if (!prefab)
return;
bool isDown = (event.type == InputEventType::MOUSE_BUTTON_DOWN);
bool isMove = (event.type == InputEventType::MOUSE_MOVE);
bool isUp = (event.type == InputEventType::MOUSE_BUTTON_UP);
if (!isDown && !isMove && !isUp)
return;
// Find camera for screen-to-world coordinate conversion (search recursively)
CameraComponent* cam = nullptr;
std::function<CameraComponent*(DekiObject*)> findCamera = [&](DekiObject* obj) -> CameraComponent* {
CameraComponent* c = obj->GetComponent<CameraComponent>();
if (c) return c;
for (auto* child : obj->GetChildren())
{
c = findCamera(child);
if (c) return c;
}
return nullptr;
};
for (DekiObject* obj : prefab->GetObjects())
{
cam = findCamera(obj);
if (cam) break;
}
float worldX = static_cast<float>(event.x);
float worldY = static_cast<float>(event.y);
if (cam && engine.GetRenderSystem())
{
cam->ScreenToWorld(static_cast<float>(event.x), static_cast<float>(event.y),
engine.GetRenderSystem()->GetScreenWidth(),
engine.GetRenderSystem()->GetScreenHeight(),
worldX, worldY);
}
DispatchInput(prefab, worldX, worldY, isDown, isMove, isUp);
}
void DekiInputSystem::DispatchInput(Prefab* prefab, float x, float y,
bool down, bool move, bool up)
{
if (!prefab)
return;
for (DekiObject* obj : prefab->GetObjects())
{
DispatchToObject(obj, x, y, down, move, up);
}
}
bool DekiInputSystem::DispatchToObject(DekiObject* obj, float x, float y,
bool down, bool move, bool up)
{
if (!obj)
return false;
// Phase 1: Recurse to children FIRST (deepest child gets priority)
// All siblings are dispatched so they can track hover state (pointer_exit).
bool childConsumed = false;
for (DekiObject* child : obj->GetChildren())
{
if (DispatchToObject(child, x, y, down, move, up))
childConsumed = true;
}
// Phase 2: Process this object's InputCollider
for (DekiComponent* comp : obj->GetComponents())
{
if (comp->getType() == InputCollider::StaticType ||
comp->getBaseType() == InputCollider::StaticType)
{
auto* collider = static_cast<InputCollider*>(comp);
// Process if: no child consumed, OR this is a non-consuming collider (e.g. scroll)
if (!childConsumed || !collider->consume_input)
{
bool handled = collider->ProcessInput(x, y, down, move, up);
if (handled && collider->consume_input)
return true;
}
break;
}
}
return childConsumed;
}