feat: Add mouse wheel support for game input passthrough

- Implement mouse wheel event handling in SDL2 backend (emu_window_sdl2)
- Add wheel event routing from GRenderWindow to input subsystem
- Add debugging logs to trace mouse wheel events through input chain
- Support mouse wheel input for mods like Ultracam when games are running (WIP)

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-09-14 15:18:38 +10:00
parent 206d778782
commit 56d8bd7dd3
8 changed files with 34 additions and 0 deletions

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
@@ -732,6 +733,7 @@ void GRenderWindow::ConstrainMouse() {
void GRenderWindow::wheelEvent(QWheelEvent* event) {
const int x = event->angleDelta().x();
const int y = event->angleDelta().y();
LOG_DEBUG(Frontend, "GRenderWindow wheel event: x={}, y={}", x, y);
input_subsystem->GetMouse()->MouseWheelChange(x, y);
}

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL.h>
@@ -77,6 +78,11 @@ void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
}
void EmuWindow_SDL2::OnMouseWheel(s32 x, s32 y) {
input_subsystem->GetMouse()->MouseWheelChange(x, y);
LOG_DEBUG(Frontend, "SDL2 Mouse wheel event: x={}, y={}", x, y);
}
void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
}
@@ -202,6 +208,9 @@ void EmuWindow_SDL2::WaitEvent() {
OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
}
break;
case SDL_MOUSEWHEEL:
OnMouseWheel(event.wheel.x, event.wheel.y);
break;
case SDL_FINGERDOWN:
OnFingerDown(event.tfinger.x, event.tfinger.y,
static_cast<std::size_t>(event.tfinger.touchId));

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-FileCopyrightText: 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -52,6 +53,9 @@ protected:
/// Called by WaitEvent when the mouse moves.
void OnMouseMotion(s32 x, s32 y);
/// Called by WaitEvent when the mouse wheel is scrolled
void OnMouseWheel(s32 x, s32 y);
/// Called by WaitEvent when a finger starts touching the touchscreen
void OnFingerDown(float x, float y, std::size_t id);

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
@@ -374,6 +375,7 @@ void EmulatedDevices::SetMouseWheel(const Common::Input::CallbackStatus& callbac
const auto analog_value = TransformToAnalog(callback);
device_status.mouse_wheel_values[index] = analog_value;
LOG_DEBUG(Input, "EmulatedDevices::SetMouseWheel: index={}, value={}", index, analog_value.value);
if (is_configuring) {
device_status.mouse_wheel_state = {};
@@ -455,6 +457,11 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const {
return device_status.mouse_wheel_state;
}
void EmulatedDevices::ResetMouseWheel() {
std::scoped_lock lock{mutex};
device_status.mouse_wheel_state = {};
}
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {

View File

@@ -135,6 +135,9 @@ public:
/// Returns the latest mouse wheel change
AnalogStickState GetMouseWheel() const;
/// Resets the mouse wheel state (should be called each frame)
void ResetMouseWheel();
/**
* Adds a callback to the list of events
* @param update_callback InterfaceUpdateCallback that will be triggered

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
@@ -55,6 +56,9 @@ void DebugMouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
last_mouse_wheel_state = mouse_wheel_state;
// Reset mouse wheel state after reading to ensure delta values work correctly
emulated_devices->ResetMouseWheel();
next_state.button = mouse_button_state;
}

View File

@@ -55,6 +55,9 @@ void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
last_mouse_wheel_state = mouse_wheel_state;
// Reset mouse wheel state after reading to ensure delta values work correctly
emulated_devices->ResetMouseWheel();
next_state.button = mouse_button_state;
}

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
@@ -235,6 +236,7 @@ void Mouse::MouseWheelChange(int x, int y) {
wheel_position.x += x;
wheel_position.y += y;
last_motion_change.z += static_cast<f32>(y);
LOG_DEBUG(Input, "Mouse wheel change: x={}, y={}, wheel_pos=({}, {})", x, y, wheel_position.x, wheel_position.y);
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
}