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
|
// Copyright 2010 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include "Common/HookableEvent.h"
#include "Common/Matrix.h"
#include "Common/WindowSystemInfo.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include "InputCommon/ControllerInterface/InputBackend.h"
// enable disable sources
#ifdef _WIN32
#define CIFACE_USE_WIN32
#endif
#ifdef HAVE_X11
#define CIFACE_USE_XLIB
#endif
#if defined(__APPLE__)
#define CIFACE_USE_OSX
#endif
#if defined(HAVE_LIBEVDEV) && defined(HAVE_LIBUDEV)
#define CIFACE_USE_EVDEV
#endif
#if defined(USE_PIPES)
#define CIFACE_USE_PIPES
#endif
#define CIFACE_USE_DUALSHOCKUDPCLIENT
#if defined(HAVE_SDL3)
#define CIFACE_USE_SDL
#endif
#if defined(HAVE_HIDAPI)
#define CIFACE_USE_STEAMDECK
#endif
namespace ciface
{
// A thread local "input channel" is maintained to handle the state of relative inputs.
// This allows simultaneous use of relative inputs across different input contexts.
// e.g. binding relative mouse movements to both GameCube controllers and FreeLook.
// These operate at different rates and processing one would break the other without separate state.
enum class InputChannel
{
Host,
SerialInterface,
Bluetooth,
FreeLook,
Count,
};
} // namespace ciface
//
// ControllerInterface
//
// Some crazy shit I made to control different device inputs and outputs
// from lots of different sources, hopefully more easily.
//
class ControllerInterface : public ciface::Core::DeviceContainer
{
public:
enum class WindowChangeReason
{
// Application is shutting down
Exit,
Other
};
enum class RefreshReason
{
// Only the window changed.
WindowChangeOnly,
// User requested, or any other internal reason (e.g. init).
// The window might have changed anyway.
Other
};
ControllerInterface() : m_is_init(false) {}
void Initialize(const WindowSystemInfo& wsi);
// Only call from one thread at a time.
void ChangeWindow(void* hwnd, WindowChangeReason reason = WindowChangeReason::Other);
// Can be called by any thread at any time (when initialized).
void RefreshDevices(RefreshReason reason = RefreshReason::Other);
void Shutdown();
bool AddDevice(std::shared_ptr<ciface::Core::Device> device);
// Removes all the devices the function returns true to.
// If all the devices shared ptrs need to be destroyed immediately,
// set force_devices_release to true.
void RemoveDevice(std::function<bool(const ciface::Core::Device*)> callback,
bool force_devices_release = false);
// This is mandatory to use on device populations functions that can be called concurrently by
// more than one thread, or that are called by a single other thread.
// Without this, our devices list might end up in a mixed state.
void PlatformPopulateDevices(const std::function<void()>& callback);
bool IsInit() const { return m_is_init; }
void UpdateInput();
// Set adjustment from the full render window aspect-ratio to the drawn aspect-ratio.
// Used to fit mouse cursor inputs to the relevant region of the render window.
void SetAspectRatioAdjustment(float);
// Calculated from the aspect-ratio adjustment.
// Inputs based on window coordinates should be multiplied by this.
Common::Vec2 GetWindowInputScale() const;
// Request that the mouse cursor should be centered in the render window at the next opportunity.
void SetMouseCenteringRequested(bool center);
bool IsMouseCenteringRequested() const;
[[nodiscard]] Common::EventHook
RegisterDevicesChangedCallback(Common::HookableEvent<>::CallbackType callback);
static void SetCurrentInputChannel(ciface::InputChannel);
static ciface::InputChannel GetCurrentInputChannel();
WindowSystemInfo GetWindowSystemInfo() const;
private:
void ClearDevices();
void InvokeDevicesChangedCallbacks();
Common::HookableEvent<> m_devices_changed_event;
mutable std::recursive_mutex m_devices_population_mutex;
std::atomic<bool> m_is_init;
// This is now always protected by m_devices_population_mutex, so
// it doesn't really need to be a counter or atomic anymore (it could be a raw bool),
// but we keep it so for simplicity, in case we changed the design.
std::atomic<int> m_populating_devices_counter;
WindowSystemInfo m_wsi;
std::atomic<float> m_aspect_ratio_adjustment = 1;
std::atomic<bool> m_requested_mouse_centering = false;
std::vector<std::unique_ptr<ciface::InputBackend>> m_input_backends;
};
namespace ciface
{
template <typename T>
class RelativeInputState
{
public:
void Update()
{
const auto channel = int(ControllerInterface::GetCurrentInputChannel());
m_value[channel] = m_delta[channel];
m_delta[channel] = {};
}
T GetValue() const
{
const auto channel = int(ControllerInterface::GetCurrentInputChannel());
return m_value[channel];
}
void Move(T delta)
{
for (auto& d : m_delta)
d += delta;
}
private:
std::array<T, int(InputChannel::Count)> m_value;
std::array<T, int(InputChannel::Count)> m_delta;
};
} // namespace ciface
extern ControllerInterface g_controller_interface;
|