/* Copyright (c) 2017-2026 Hans-Kristian Arntzen * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "input.hpp" #include "event.hpp" #include "muglm/muglm_impl.hpp" #include "logging.hpp" #include #include using namespace Util; namespace Granite { const char *joypad_key_to_tag(JoypadKey key) { #define D(k) case JoypadKey::k: return #k switch (key) { D(Left); D(Right); D(Up); D(Down); D(LeftShoulder); D(RightShoulder); D(West); D(East); D(North); D(South); D(LeftThumb); D(RightThumb); D(Mode); D(Start); D(Select); default: return "Unknown"; } #undef D } const char *joypad_axis_to_tag(JoypadAxis axis) { #define D(k) case JoypadAxis::k: return #k switch (axis) { D(LeftX); D(LeftY); D(RightX); D(RightY); D(LeftTrigger); D(RightTrigger); default: return "Unknown"; } #undef D } void InputTracker::orientation_event(quat rot) { OrientationEvent event(rot); if (handler) handler->dispatch(event); } void InputTracker::on_touch_down(unsigned id, float x, float y) { if (touch.active_pointers >= TouchCount) { LOGE("Touch pointer overflow!\n"); return; } unsigned index = touch.active_pointers++; auto &pointer = touch.pointers[index]; pointer.id = id; pointer.start_x = x; pointer.start_y = y; pointer.last_x = x; pointer.last_y = y; pointer.x = x; pointer.y = y; TouchDownEvent event(index, id, x, y, touch.width, touch.height); if (handler) handler->dispatch(event); } void InputTracker::dispatch_touch_gesture() { TouchGestureEvent event(touch); if (handler) handler->dispatch(event); } void InputTracker::on_touch_move(unsigned id, float x, float y) { auto &pointers = touch.pointers; auto itr = std::find_if(std::begin(pointers), std::begin(pointers) + touch.active_pointers, [id](const TouchState::Pointer &pointer) { return pointer.id == id; }); if (itr == std::end(pointers)) { LOGE("Could not find pointer!\n"); return; } itr->x = x; itr->y = y; } void InputTracker::on_touch_up(unsigned id, float x, float y) { auto &pointers = touch.pointers; auto itr = std::find_if(std::begin(pointers), std::begin(pointers) + touch.active_pointers, [id](const TouchState::Pointer &pointer) { return pointer.id == id; }); if (itr == std::end(pointers)) { LOGE("Could not find pointer!\n"); return; } auto index = itr - std::begin(pointers); TouchUpEvent event(itr->id, x, y, itr->start_x, itr->start_y, touch.width, touch.height); if (handler) handler->dispatch(event); memmove(&pointers[index], &pointers[index + 1], (TouchCount - (index + 1)) * sizeof(TouchState::Pointer)); touch.active_pointers--; } void InputTracker::joypad_key_state(unsigned index, JoypadKey key, JoypadKeyState state) { if (index >= Joypads) return; assert(active_joypads & (1u << index)); auto &joy = joypads[index]; unsigned key_index = Util::ecast(key); unsigned key_mask = 1u << key_index; if (state == JoypadKeyState::Pressed) { if ((joy.button_mask & key_mask) == 0) { JoypadButtonEvent event(index, key, state); if (handler) handler->dispatch(event); } joy.button_mask |= key_mask; } else if (state == JoypadKeyState::Released) { if ((joy.button_mask & key_mask) != 0) { JoypadButtonEvent event(index, key, state); if (handler) handler->dispatch(event); } joy.button_mask &= ~key_mask; } } void JoypadState::snap_deadzone(float deadzone) { memcpy(snapped_axis, raw_axis, sizeof(raw_axis)); static const JoypadAxis fused_axes[2][2] = { { JoypadAxis::LeftX, JoypadAxis::LeftY }, { JoypadAxis::RightX, JoypadAxis::RightY }, }; for (auto &fused : fused_axes) { if (std::abs(raw_axis[int(fused[0])]) < deadzone && std::abs(raw_axis[int(fused[1])]) < deadzone) for (auto &axis : fused) snapped_axis[int(axis)] = 0.0f; } } void InputTracker::joyaxis_state(unsigned index, JoypadAxis axis, float value) { if (index >= Joypads) return; assert(active_joypads & (1u << index)); auto &joy = joypads[index]; unsigned axis_index = Util::ecast(axis); auto &a = joy.raw_axis[axis_index]; if (a != value) { JoypadAxisEvent event(index, axis, value); if (handler) handler->dispatch(event); } a = value; } void InputTracker::key_event(Key key, KeyState state) { if (state == KeyState::Released) key_state &= ~(1ull << ecast(key)); else if (state == KeyState::Pressed) key_state |= 1ull << ecast(key); KeyboardEvent event(key, state); if (handler) handler->dispatch(event); } void InputTracker::mouse_button_event(Granite::MouseButton button, bool pressed) { mouse_button_event(button, last_mouse_x, last_mouse_y, pressed); } void InputTracker::mouse_button_event(MouseButton button, double x, double y, bool pressed) { if (pressed) mouse_button_state |= 1ull << ecast(button); else mouse_button_state &= ~(1ull << ecast(button)); if (mouse_active) { last_mouse_x = x; last_mouse_y = y; } MouseButtonEvent event(button, x, y, pressed); if (handler) handler->dispatch(event); } void InputTracker::mouse_move_event_relative(double x, double y) { x *= mouse_speed_x; y *= mouse_speed_y; if (mouse_active) { last_mouse_x += x; last_mouse_y += y; last_mouse_x = clamp(last_mouse_x, mouse_relative_range_x, mouse_relative_range_x + mouse_relative_range_width); last_mouse_y = clamp(last_mouse_y, mouse_relative_range_y, mouse_relative_range_y + mouse_relative_range_height); MouseMoveEvent event(x, y, last_mouse_x, last_mouse_y, key_state, mouse_button_state); if (handler) handler->dispatch(event); } } void InputTracker::mouse_move_event_absolute(double x, double y) { if (mouse_active) { double delta_x = x - last_mouse_x; double delta_y = y - last_mouse_y; last_mouse_x = x; last_mouse_y = y; MouseMoveEvent event(delta_x, delta_y, x, y, key_state, mouse_button_state); if (handler) handler->dispatch(event); } } void InputTracker::mouse_move_event_absolute_normalized(double x, double y) { mouse_move_event_absolute(x * double(touch.width), y * double(touch.height)); } void InputTracker::mouse_button_event_normalized(MouseButton button, double x, double y, bool pressed) { mouse_button_event(button, x * double(touch.width), y * double(touch.height), pressed); } void InputTracker::mouse_enter(double x, double y) { mouse_active = true; last_mouse_x = x; last_mouse_y = y; } void InputTracker::mouse_leave() { mouse_active = false; } void InputTracker::dispatch_current_state(double delta_time, InputTrackerHandler *override_handler) { if (!override_handler) override_handler = handler; if (override_handler) { for (auto &pad : joypads) pad.snap_deadzone(axis_deadzone); override_handler->dispatch(JoypadStateEvent{active_joypads, joypads, Joypads, delta_time}); override_handler->dispatch(InputStateEvent{last_mouse_x, last_mouse_y, delta_time, key_state, mouse_button_state, mouse_active}); } } int InputTracker::find_vacant_joypad_index() const { for (int i = 0; i < Joypads; i++) { if ((active_joypads & (1 << i)) == 0) return i; } return -1; } void InputTracker::enable_joypad(unsigned index, uint32_t vid, uint32_t pid) { if (index >= Joypads) return; if (active_joypads & (1u << index)) return; active_joypads |= 1u << index; joypads[index] = {}; joypads[index].vid = vid; joypads[index].pid = pid; JoypadConnectionEvent event(index, true, vid, pid); if (handler) handler->dispatch(event); } void InputTracker::disable_joypad(unsigned index, uint32_t vid, uint32_t pid) { if (index >= Joypads) return; if ((active_joypads & (1u << index)) == 0) return; active_joypads &= ~(1u << index); joypads[index] = {}; JoypadConnectionEvent event(index, false, vid, pid); if (handler) handler->dispatch(event); } std::mutex &InputTracker::get_lock() { return dispatch_lock; } }