Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions core/input/input_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,60 @@ bool InputEvent::is_action_type() const {
return false;
}

bool InputEvent::assign_device_index(int p_device) {
if (device_index.end() != device_index.find(p_device)) {
return false;
}
device_index.insert(p_device, true);
for (const auto &[old_device, active] : device_index) {
if (!active) {
device_index.erase(old_device);
break;
}
}
return true;
}

bool InputEvent::remove_device_index(int p_device) {
if (device_index.end() == device_index.find(p_device)) {
return false;
}
device_index[p_device] = false;
return true;
}

void InputEvent::filter_active_devices(Vector<int> p_active_devices) {
for (const auto &[current_dev, active] : device_index) {
if (!active) {
continue;
}
bool found = false;
for (const auto &new_dev : p_active_devices) {
found = (new_dev == current_dev);
if (found) {
break;
}
}
if (!found) {
InputEvent::remove_device_index(current_dev);
}
}
}

int InputEvent::get_device_index(int p_device) {
return device_index.get_index(p_device);
}

bool InputEvent::set_device_index(int p_device) {
InputEvent::assign_device_index(p_device);
int index = InputEvent::get_device_index(p_device);
if (-1 == index) {
return false;
}
this->set_device(index);
return true;
}

void InputEvent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device);
ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device);
Expand Down
10 changes: 9 additions & 1 deletion core/input/input_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "core/math/transform_2d.h"
#include "core/os/keyboard.h"
#include "core/string/ustring.h"
#include "core/templates/a_hash_map.h"
#include "core/typedefs.h"

/**
Expand All @@ -52,11 +53,12 @@ class Shortcut;
class InputEvent : public Resource {
GDCLASS(InputEvent, Resource);

int device = 0;
int device = -1; // ALL_DEVICES

protected:
bool canceled = false;
bool pressed = false;
static inline AHashMap<int, bool> device_index; // bool is to mark a device ID as inactive, available for overwriting
Copy link
Contributor

@Nintorch Nintorch Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the new functionality in this PR should be inside of Input class instead of InputEvent, since joypads are also registered inside Input.


static void _bind_methods();

Expand Down Expand Up @@ -90,6 +92,12 @@ class InputEvent : public Resource {
virtual bool accumulate(const Ref<InputEvent> &p_event) { return false; }

virtual InputEventType get_type() const { return InputEventType::INVALID; }

static bool assign_device_index(int p_device);
static bool remove_device_index(int p_device);
static void filter_active_devices(Vector<int> p_active_devices);
static int get_device_index(int p_device);
bool set_device_index(int p_device);
};

class InputEventFromWindow : public InputEvent {
Expand Down
20 changes: 19 additions & 1 deletion core/input/input_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re
return nullptr;
}

Ref<InputEvent> InputMap::_normalize_event(const Ref<InputEvent> &p_event) const {
ERR_FAIL_COND_V(p_event.is_null(), nullptr);
// Normalize for key events if they are raw
Ref<InputEventKey> k = p_event;
// Raw input IDs are HANDLE, which use 32 bit HANDLE even for 64 bit Windows
// So raw input's device (HANDLE casted to int64_t) is > 0
if (k.is_valid() && (k->get_device() > 0)) {
// Create a normalized event with device = ALL_DEVICES for InpuMap
Ref<InputEventKey> k_norm = k->duplicate();
k_norm->set_device(ALL_DEVICES);
return k_norm;
}
return p_event;
}

bool InputMap::has_action(const StringName &p_action) const {
return input_map.has(p_action);
}
Expand Down Expand Up @@ -203,7 +218,10 @@ void InputMap::action_add_event(const StringName &p_action, RequiredParam<InputE
return; // Already added.
}

input_map[p_action].inputs.push_back(p_event);
// Normalize events first
Ref<InputEvent> p_event_norm = _normalize_event(p_event);

input_map[p_action].inputs.push_back(p_event_norm);
}

bool InputMap::action_has_event(const StringName &p_action, RequiredParam<InputEvent> rp_event) {
Expand Down
1 change: 1 addition & 0 deletions core/input/input_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class InputMap : public Object {
HashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;

List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const;
Ref<InputEvent> _normalize_event(const Ref<InputEvent> &p_event) const;

TypedArray<InputEvent> _action_get_events(const StringName &p_action);

Expand Down
1 change: 1 addition & 0 deletions core/templates/a_hash_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ template class AHashMap<String, int>;
template class AHashMap<StringName, StringName>;
template class AHashMap<StringName, Variant>;
template class AHashMap<StringName, int>;
template class AHashMap<int, bool>;
1 change: 1 addition & 0 deletions core/templates/a_hash_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -729,3 +729,4 @@ extern template class AHashMap<String, int>;
extern template class AHashMap<StringName, StringName>;
extern template class AHashMap<StringName, Variant>;
extern template class AHashMap<StringName, int>;
extern template class AHashMap<int, bool>;
2 changes: 1 addition & 1 deletion doc/classes/InputEvent.xml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
</method>
</methods>
<members>
<member name="device" type="int" setter="set_device" getter="get_device" default="0">
<member name="device" type="int" setter="set_device" getter="get_device" default="-1">
The event's device ID.
[b]Note:[/b] [member device] can be negative for special use cases that don't refer to devices physically present on the system. See [constant DEVICE_ID_EMULATION].
</member>
Expand Down
2 changes: 2 additions & 0 deletions platform/linuxbsd/wayland/wayland_thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ Ref<InputEventKey> WaylandThread::_seat_state_get_key_event(SeatState *p_ss, xkb

event->set_pressed(p_pressed);
event->set_keycode(keycode);
event->set_device_index(p_ss->wl_seat_name);
event->set_physical_keycode(physical_keycode);
event->set_location(key_location);

Expand Down Expand Up @@ -739,6 +740,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}

void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) {
InputEventKey::remove_device_index(name);
RegistryState *registry = (RegistryState *)data;
ERR_FAIL_NULL(registry);

Expand Down
28 changes: 25 additions & 3 deletions platform/linuxbsd/x11/display_server_x11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,14 @@ bool DisplayServerX11::_refresh_device_info() {
int dev_count;
XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count);

// Check which devices no longer remains to filter
Vector<int> new_active_devices;
for (int i = 0; i < dev_count; ++i) {
XIDeviceInfo *dev = &info[i];
new_active_devices.append(dev->deviceid);
}
InputEvent::filter_active_devices(new_active_devices);

for (int i = 0; i < dev_count; i++) {
XIDeviceInfo *dev = &info[i];
if (!dev->enabled) {
Expand Down Expand Up @@ -3796,7 +3804,7 @@ void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<Inp
state->set_meta_pressed((p_x11_state & Mod4Mask));
}

void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo) {
void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, int p_device_id, bool p_echo) {
WindowData &wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
Expand Down Expand Up @@ -3918,6 +3926,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
k->set_pressed(keypress);

k->set_keycode(keycode);
k->set_device_index(p_device_id);
k->set_physical_keycode(physical_keycode);
if (!keysym.is_empty()) {
k->set_key_label(fix_key_label(keysym[0], keycode));
Expand All @@ -3933,6 +3942,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
if (k->get_keycode() == Key::BACKTAB) {
//make it consistent across platforms.
k->set_keycode(Key::TAB);
k->set_device_index(p_device_id);
k->set_physical_keycode(Key::TAB);
k->set_shift_pressed(true);
}
Expand Down Expand Up @@ -3998,6 +4008,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
k->set_pressed(keypress);

k->set_keycode(keycode);
k->set_device_index(p_device_id);
k->set_physical_keycode(physical_keycode);
if (!keysym.is_empty()) {
k->set_key_label(fix_key_label(keysym[0], keycode));
Expand All @@ -4015,6 +4026,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
if (k->get_keycode() == Key::BACKTAB) {
//make it consistent across platforms.
k->set_keycode(Key::TAB);
k->set_device_index(p_device_id);
k->set_physical_keycode(Key::TAB);
k->set_shift_pressed(true);
}
Expand Down Expand Up @@ -4116,7 +4128,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
if (rk == keysym_keycode) {
// Consume to next event.
++p_event_index;
_handle_key_event(p_window, (XKeyEvent *)&peek_event, p_events, p_event_index, true);
_handle_key_event(p_window, (XKeyEvent *)&peek_event, p_events, p_event_index, p_device_id, true);
return; //ignore current, echo next
}
}
Expand All @@ -4136,6 +4148,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
}

k->set_keycode(keycode);
k->set_device_index(p_device_id);
k->set_physical_keycode((Key)physical_keycode);
if (!keysym.is_empty()) {
k->set_key_label(fix_key_label(keysym[0], keycode));
Expand All @@ -4153,6 +4166,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
if (k->get_keycode() == Key::BACKTAB) {
//make it consistent across platforms.
k->set_keycode(Key::TAB);
k->set_device_index(p_device_id);
k->set_physical_keycode(Key::TAB);
k->set_shift_pressed(true);
}
Expand Down Expand Up @@ -4803,6 +4817,7 @@ void DisplayServerX11::process_events() {

bool ime_window_event = false;
WindowID window_id = MAIN_WINDOW_ID;
static int current_keyboard_id;

// Assign the event to the relevant window
for (const KeyValue<WindowID, WindowData> &E : windows) {
Expand All @@ -4825,6 +4840,11 @@ void DisplayServerX11::process_events() {
case XI_DeviceChanged: {
_refresh_device_info();
} break;
case XI_RawKeyPress:
case XI_RawKeyRelease: {
XIRawEvent *raw_event = (XIRawEvent *)event_data;
current_keyboard_id = raw_event->sourceid;
} break;
case XI_RawMotion: {
if (ime_window_event || ignore_events) {
break;
Expand Down Expand Up @@ -5477,7 +5497,7 @@ void DisplayServerX11::process_events() {

// key event is a little complex, so
// it will be handled in its own function.
_handle_key_event(window_id, &event.xkey, events, event_index);
_handle_key_event(window_id, &event.xkey, events, event_index, current_keyboard_id);
} break;

case SelectionNotify:
Expand Down Expand Up @@ -7217,6 +7237,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
all_master_event_mask.mask = all_master_mask_data;
XISetMask(all_master_event_mask.mask, XI_DeviceChanged);
XISetMask(all_master_event_mask.mask, XI_RawMotion);
XISetMask(all_master_event_mask.mask, XI_RawKeyPress);
XISetMask(all_master_event_mask.mask, XI_RawKeyRelease);
XISelectEvents(x11_display, DefaultRootWindow(x11_display), &all_master_event_mask, 1);
}

Expand Down
2 changes: 1 addition & 1 deletion platform/linuxbsd/x11/display_server_x11.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ class DisplayServerX11 : public DisplayServer {

Point2i center;

void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, int p_device_id, bool p_echo = false);

Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) const;
void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;
Expand Down
Loading