diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c15e02ad..61927681b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -494,6 +494,9 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/razor_cpu/razor_cpu.h src/core/libraries/mouse/mouse.cpp src/core/libraries/mouse/mouse.h + src/core/libraries/mouse/mouse_error.h + src/core/libraries/mouse/sdl_mouse.cpp + src/core/libraries/mouse/sdl_mouse.h src/core/libraries/web_browser_dialog/webbrowserdialog.cpp src/core/libraries/web_browser_dialog/webbrowserdialog.h src/core/libraries/font/font.cpp diff --git a/src/common/ring_buffer_queue.h b/src/common/ring_buffer_queue.h new file mode 100644 index 000000000..00c4dcc91 --- /dev/null +++ b/src/common/ring_buffer_queue.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +template +class RingBufferQueue { +public: + RingBufferQueue(u64 size) : m_storage(size) {} + + void Push(T item) { + const u64 index = (m_begin + m_size) % m_storage.size(); + m_storage[index] = std::move(item); + if (m_size < m_storage.size()) { + m_size += 1; + } else { + m_begin = (m_begin + 1) % m_storage.size(); + } + } + + std::optional Pop() { + if (m_size == 0) { + return {}; + } + const u64 index = m_begin; + m_begin = (m_begin + 1) % m_storage.size(); + m_size -= 1; + return std::move(m_storage[index]); + } + + std::optional Peek() { + if (m_size == 0) { + return {}; + } + return m_storage[m_begin]; + } + +private: + u64 m_begin = 0; + u64 m_size = 0; + std::vector m_storage; +}; \ No newline at end of file diff --git a/src/core/emulator_settings.h b/src/core/emulator_settings.h index 8d3125fdb..85448f10c 100644 --- a/src/core/emulator_settings.h +++ b/src/core/emulator_settings.h @@ -296,6 +296,7 @@ struct InputSettings { Setting ime_url_mail_short_panel{false}; // specific Setting is_circle_enter{false}; // specific Setting camera_id{-1}; + Setting use_mice_as_mice{false}; std::vector GetOverrideableFields() const { return std::vector{ @@ -312,7 +313,8 @@ struct InputSettings { make_override("ime_url_mail_short_panel", &InputSettings::ime_url_mail_short_panel), make_override("is_circle_enter", &InputSettings::is_circle_enter), - make_override("camera_id", &InputSettings::camera_id)}; + make_override("camera_id", &InputSettings::camera_id), + make_override("use_mice_as_mice", &InputSettings::use_mice_as_mice)}; } }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings, cursor_state, cursor_hide_timeout, @@ -320,7 +322,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings, cursor_state, cursor_hide_time motion_controls_enabled, use_unified_input_config, default_controller_id, background_controller_input, ime_accessibility_enabled, ime_url_mail_short_panel, camera_id, - is_circle_enter) + is_circle_enter, use_mice_as_mice) // ------------------------------- // Audio settings // ------------------------------- @@ -683,6 +685,7 @@ public: SETTING_FORWARD_BOOL(m_input, UseUnifiedInputConfig, use_unified_input_config) SETTING_FORWARD(m_input, CameraId, camera_id) SETTING_FORWARD_BOOL(m_input, CircleEnter, is_circle_enter) + SETTING_FORWARD_BOOL(m_input, MiceUsedAsMice, use_mice_as_mice) // Vulkan settings SETTING_FORWARD(m_vulkan, GpuId, gpu_id) diff --git a/src/core/libraries/mouse/mouse.cpp b/src/core/libraries/mouse/mouse.cpp index b2799107b..822459c5d 100644 --- a/src/core/libraries/mouse/mouse.cpp +++ b/src/core/libraries/mouse/mouse.cpp @@ -1,15 +1,26 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later // Generated By moduleGenerator +#include "SDL3/SDL_mouse.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/user_settings.h" #include "mouse.h" +#include "mouse_error.h" +#include "sdl_window.h" + +extern Frontend::WindowSDL* g_window; namespace Libraries::Mouse { -int PS4_SYSV_ABI sceMouseClose() { +RingBufferQueue mouse_states[2]{{64}, {64}}; +s32 mouse_handles[2]{-1, -1}; +s32 mouse_sdl_handles[2]{-1, -1}; +bool g_lib_init = false, g_is_merged_mode = false; + +int PS4_SYSV_ABI sceMouseClose(s32 handle) { LOG_ERROR(Lib_Mouse, "(STUBBED) called"); return ORBIS_OK; } @@ -45,7 +56,16 @@ int PS4_SYSV_ABI sceMouseGetDeviceInfo() { } int PS4_SYSV_ABI sceMouseInit() { - LOG_ERROR(Lib_Mouse, "(STUBBED) called"); + SDL_WarpMouseInWindow(g_window->GetSDLWindow(), 1, 1); + SDL_SetWindowRelativeMouseMode(g_window->GetSDLWindow(), true); + int micecount = 0; + auto micelist = SDL_GetMice(&micecount); + LOG_INFO(Input, "{} mice are currently connected", micecount); + for (int i = 0; i < micecount; i++) { + LOG_INFO(Lib_Mouse, "{}: {} id: {}", i, SDL_GetMouseNameForID(micelist[i]), micelist[i]); + } + SDL_free(micelist); + g_lib_init = true; return ORBIS_OK; } @@ -54,14 +74,47 @@ int PS4_SYSV_ABI sceMouseMbusInit() { return ORBIS_OK; } -int PS4_SYSV_ABI sceMouseOpen() { - LOG_ERROR(Lib_Mouse, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceMouseOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, OrbisMouseOpenParam* pParam) { + LOG_WARNING(Lib_Mouse, "(DUMMY) called, uid: {}, type: {}, index: {}", userId, type, index); + auto u = UserManagement.GetUserByID(userId); + if (!u || !pParam || (u8)pParam->flag > 1 || index < 0 || index > 1) { + LOG_ERROR(Lib_Mouse, "invalid argument"); + return ORBIS_MOUSE_ERROR_INVALID_ARG; + } + if (pParam->flag == MouseOpenBehaviour::Merged && index != 0) { + LOG_ERROR(Lib_Mouse, "Only one mouse can be opened in merged mode!"); + return ORBIS_MOUSE_ERROR_ALREADY_OPENED; + } + g_is_merged_mode = pParam->flag == MouseOpenBehaviour::Merged; + if (mouse_handles[index] != -1) { + LOG_ERROR(Lib_Mouse, "already opened"); + return ORBIS_MOUSE_ERROR_ALREADY_OPENED; + } + mouse_handles[index] = index; + LOG_INFO(Lib_Mouse, "out handle: {}", mouse_handles[index]); + return mouse_handles[index]; } -int PS4_SYSV_ABI sceMouseRead() { - LOG_DEBUG(Lib_Mouse, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceMouseRead(s32 handle, OrbisMouseData* pData, s32 num) { + LOG_DEBUG(Lib_Mouse, "(DUMMY) called, h: {}, n: {}", handle, num); + if (!pData || num > 64) { + return ORBIS_MOUSE_ERROR_INVALID_ARG; + } + if (mouse_handles[0] != handle && mouse_handles[1] != handle) { + return ORBIS_MOUSE_ERROR_INVALID_HANDLE; + } + u64 m = mouse_handles[0] == handle ? 0 : 1; + int i = 0; + for (; i < num; i++) { + std::optional st = mouse_states[m].Pop(); + if (!st) { + break; + } + pData[i] = *st; + pData[i].connected = mouse_sdl_handles[m] != -1; + } + return i; } int PS4_SYSV_ABI sceMouseSetHandType() { diff --git a/src/core/libraries/mouse/mouse.h b/src/core/libraries/mouse/mouse.h index 192343434..b85cd6f59 100644 --- a/src/core/libraries/mouse/mouse.h +++ b/src/core/libraries/mouse/mouse.h @@ -1,8 +1,10 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "common/types.h" + +#include "common/ring_buffer_queue.h" +#include "core/libraries/system/userservice.h" namespace Core::Loader { class SymbolsResolver; @@ -10,20 +12,47 @@ class SymbolsResolver; namespace Libraries::Mouse { -int PS4_SYSV_ABI sceMouseClose(); -int PS4_SYSV_ABI sceMouseConnectPort(); -int PS4_SYSV_ABI sceMouseDebugGetDeviceId(); -int PS4_SYSV_ABI sceMouseDeviceOpen(); -int PS4_SYSV_ABI sceMouseDisconnectDevice(); -int PS4_SYSV_ABI sceMouseDisconnectPort(); -int PS4_SYSV_ABI sceMouseGetDeviceInfo(); -int PS4_SYSV_ABI sceMouseInit(); -int PS4_SYSV_ABI sceMouseMbusInit(); -int PS4_SYSV_ABI sceMouseOpen(); -int PS4_SYSV_ABI sceMouseRead(); -int PS4_SYSV_ABI sceMouseSetHandType(); -int PS4_SYSV_ABI sceMouseSetPointerSpeed(); -int PS4_SYSV_ABI sceMouseSetProcessPrivilege(); +struct OrbisMouseData { + u64 timestamp; + bool connected; + u32 buttons; + s32 x_axis; + s32 y_axis; + s32 wheel; + s32 tilt; + u8 reserve[8]; +}; + +enum class MouseOpenBehaviour : u8 { + Normal = 0, + Merged = 1, +}; + +struct OrbisMouseOpenParam { + MouseOpenBehaviour flag; + u8 reserve[7]; +}; + +extern RingBufferQueue mouse_states[2]; +extern s32 mouse_handles[2]; +extern s32 mouse_sdl_handles[2]; +extern bool g_lib_init, g_is_merged_mode; + +s32 PS4_SYSV_ABI sceMouseClose(s32 handle); +s32 PS4_SYSV_ABI sceMouseConnectPort(); +s32 PS4_SYSV_ABI sceMouseDebugGetDeviceId(); +s32 PS4_SYSV_ABI sceMouseDeviceOpen(); +s32 PS4_SYSV_ABI sceMouseDisconnectDevice(); +s32 PS4_SYSV_ABI sceMouseDisconnectPort(); +s32 PS4_SYSV_ABI sceMouseGetDeviceInfo(); +s32 PS4_SYSV_ABI sceMouseInit(); +s32 PS4_SYSV_ABI sceMouseMbusInit(); +s32 PS4_SYSV_ABI sceMouseOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, OrbisMouseOpenParam* pParam); +s32 PS4_SYSV_ABI sceMouseRead(s32 handle, OrbisMouseData* pData, s32 num); +s32 PS4_SYSV_ABI sceMouseSetHandType(); +s32 PS4_SYSV_ABI sceMouseSetPointerSpeed(); +s32 PS4_SYSV_ABI sceMouseSetProcessPrivilege(); void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Mouse \ No newline at end of file diff --git a/src/core/libraries/mouse/mouse_error.h b/src/core/libraries/mouse/mouse_error.h new file mode 100644 index 000000000..101bb00d6 --- /dev/null +++ b/src/core/libraries/mouse/mouse_error.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include "common/types.h" + +constexpr s32 ORBIS_MOUSE_ERROR_INVALID_ARG = 0x80DF0001; +constexpr s32 ORBIS_MOUSE_ERROR_INVALID_HANDLE = 0x80DF0003; +constexpr s32 ORBIS_MOUSE_ERROR_ALREADY_OPENED = 0x80DF0004; +constexpr s32 ORBIS_MOUSE_ERROR_NOT_INITIALIZED = 0x80DF0005; +constexpr s32 ORBIS_MOUSE_ERROR_FATAL = 0x80DF00FF; \ No newline at end of file diff --git a/src/core/libraries/mouse/sdl_mouse.cpp b/src/core/libraries/mouse/sdl_mouse.cpp new file mode 100644 index 000000000..4d737599d --- /dev/null +++ b/src/core/libraries/mouse/sdl_mouse.cpp @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/emulator_settings.h" +#include "core/libraries/kernel/time.h" +#include "sdl_mouse.h" + +namespace Libraries::Mouse { + +static u64 GetIndexFromSdlHandle(SDL_MouseID id) { + return g_is_merged_mode ? 0 + : mouse_sdl_handles[0] == id ? 0 + : mouse_sdl_handles[1] == id ? 1 + : 2; +} + +constexpr u32 OrbisButtonFromSDL(Uint8 button) { + switch (button) { + case SDL_BUTTON_LEFT: + return 1u << 0; + case SDL_BUTTON_RIGHT: + return 1u << 1; + case SDL_BUTTON_MIDDLE: + return 1u << 2; + case SDL_BUTTON_X1: + return 1u << 3; + case SDL_BUTTON_X2: + return 1u << 4; + default: + return 0; + } +} + +bool PushSDLEvent(SDL_Event const& e) { + static OrbisMouseData current_state[2]{}; + if (!EmulatorSettings.IsMiceUsedAsMice()) { + return false; + } + if (!g_lib_init) { + return false; + } + switch (e.type) { + default: + return false; + case SDL_EVENT_MOUSE_REMOVED: { + LOG_INFO(Lib_Mouse, "Mouse removed, id: {}", e.mdevice.which); + if (g_is_merged_mode) { + break; + } + u64 index = GetIndexFromSdlHandle(e.mdevice.which); + if (index == 2) { + return false; + } + mouse_sdl_handles[index] = -1; + break; + } + case SDL_EVENT_MOUSE_MOTION: { + u64 index = GetIndexFromSdlHandle(e.motion.which); + if (index == 2) { + index = GetIndexFromSdlHandle(-1); + if (index < 2) + mouse_sdl_handles[index] = e.motion.which; + else + return false; + } + auto& s = current_state[index]; + s.x_axis = (s32)e.motion.xrel; + s.y_axis = (s32)e.motion.yrel; + s.wheel = 0; + s.timestamp = Libraries::Kernel::sceKernelGetProcessTime(); + + mouse_states[index].Push(s); + break; + } + case SDL_EVENT_MOUSE_BUTTON_DOWN: { + u64 index = GetIndexFromSdlHandle(e.button.which); + if (index >= 2) + return false; + + auto& s = current_state[index]; + s.x_axis = 0; + s.y_axis = 0; + s.wheel = 0; + s.buttons |= OrbisButtonFromSDL(e.button.button); + s.timestamp = Libraries::Kernel::sceKernelGetProcessTime(); + + mouse_states[index].Push(s); + break; + } + case SDL_EVENT_MOUSE_BUTTON_UP: { + u64 index = GetIndexFromSdlHandle(e.button.which); + if (index >= 2) + return false; + + auto& s = current_state[index]; + s.x_axis = 0; + s.y_axis = 0; + s.wheel = 0; + s.buttons &= ~OrbisButtonFromSDL(e.button.button); + s.timestamp = Libraries::Kernel::sceKernelGetProcessTime(); + + mouse_states[index].Push(s); + break; + } + case SDL_EVENT_MOUSE_WHEEL: { + u64 index = GetIndexFromSdlHandle(e.wheel.which); + if (index >= 2) + return false; + + auto& s = current_state[index]; + s.x_axis = 0; + s.y_axis = 0; + s.wheel = e.wheel.y; + s.tilt = e.wheel.x; + s.timestamp = Libraries::Kernel::sceKernelGetProcessTime(); + + mouse_states[index].Push(s); + break; + } + } + return true; +} +} // namespace Libraries::Mouse diff --git a/src/core/libraries/mouse/sdl_mouse.h b/src/core/libraries/mouse/sdl_mouse.h new file mode 100644 index 000000000..7608370bb --- /dev/null +++ b/src/core/libraries/mouse/sdl_mouse.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "SDL3/SDL_events.h" +#include "common/types.h" +#include "mouse.h" + +namespace Libraries::Mouse { +bool PushSDLEvent(SDL_Event const& e); +} \ No newline at end of file diff --git a/src/input/controller.h b/src/input/controller.h index 2a2e13524..ec6663053 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -10,7 +10,7 @@ #include #include "SDL3/SDL_joystick.h" #include "common/assert.h" -#include "common/types.h" +#include "common/ring_buffer_queue.h" #include "core/libraries/pad/pad.h" #include "core/libraries/system/userservice.h" @@ -83,37 +83,6 @@ inline int GetAxis(int min, int max, int value) { return (v < 0 ? 0 : (v > 255 ? 255 : v)); } -template -class RingBufferQueue { -public: - RingBufferQueue(size_t size) : m_storage(size) {} - - void Push(T item) { - const size_t index = (m_begin + m_size) % m_storage.size(); - m_storage[index] = std::move(item); - if (m_size < m_storage.size()) { - m_size += 1; - } else { - m_begin = (m_begin + 1) % m_storage.size(); - } - } - - std::optional Pop() { - if (m_size == 0) { - return {}; - } - const size_t index = m_begin; - m_begin = (m_begin + 1) % m_storage.size(); - m_size -= 1; - return std::move(m_storage[index]); - } - -private: - size_t m_begin = 0; - size_t m_size = 0; - std::vector m_storage; -}; - class GameController { friend class GameControllers; diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index db04f9912..b909796c4 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -32,6 +32,8 @@ #ifdef __APPLE__ #include #endif +#include +#include "core/libraries/mouse/sdl_mouse.h" CMRC_DECLARE(res); @@ -217,6 +219,10 @@ void WindowSDL::WaitEvent() { return; } + if (Libraries::Mouse::PushSDLEvent(event)) { + return; + } + if (ImGui::Core::ProcessEvent(&event)) { return; }