/* Copyright 2021 flyinghead This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "mouse.h" #include "rend/gui.h" // Mouse buttons // bit 0: Button C // bit 1: Right button (B) // bit 2: Left button (A) // bit 3: Wheel button u8 mo_buttons[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; // Relative mouse coordinates [-512:511] float mo_x_delta[4]; float mo_y_delta[4]; float mo_wheel_delta[4]; std::mutex relPosMutex; // Absolute mouse coordinates // Range [0:639] [0:479] // but may be outside this range if the pointer is offscreen or outside the 4:3 window. s32 mo_x_abs[4]; s32 mo_y_abs[4]; // previous mouse coordinates for relative motion s32 mo_x_prev[4] = { -1, -1, -1, -1 }; s32 mo_y_prev[4] = { -1, -1, -1, -1 }; // last known screen/window size static s32 mo_width; static s32 mo_height; void Mouse::setAbsPos(int x, int y, int width, int height) { SetMousePosition(x, y, width, height, maple_port()); } void Mouse::setRelPos(float deltax, float deltay) { SetRelativeMousePosition(deltax, deltay, maple_port()); } void Mouse::setWheel(int delta) { std::lock_guard lock(relPosMutex); if (maple_port() >= 0 && maple_port() < (int)ARRAY_SIZE(mo_wheel_delta)) mo_wheel_delta[maple_port()] += delta; } void Mouse::setButton(Button button, bool pressed) { if (maple_port() >= 0 && maple_port() < (int)ARRAY_SIZE(mo_buttons)) { if (pressed) mo_buttons[maple_port()] &= ~(1 << (int)button); else mo_buttons[maple_port()] |= 1 << (int)button; } if ((gui_is_open() || gui_mouse_captured()) && !is_detecting_input()) // Don't register mouse clicks as gamepad presses when gui is open // This makes the gamepad presses to be handled first and the mouse position to be ignored return; gamepad_btn_input(button, pressed); } void SystemMouse::setAbsPos(int x, int y, int width, int height) { gui_set_mouse_position(x, y); Mouse::setAbsPos(x, y, width, height); } void SystemMouse::setButton(Button button, bool pressed) { int uiBtn = (int)button - 1; if (uiBtn < 2) uiBtn ^= 1; gui_set_mouse_button(uiBtn, pressed); Mouse::setButton(button, pressed); } void SystemMouse::setWheel(int delta) { gui_set_mouse_wheel(delta * 35); Mouse::setWheel(delta); } static void screenToNative(int& x, int& y, int width, int height) { float fx, fy; if (!config::Rotate90) { float scale = 480.f / height; fy = y * scale; scale /= config::ScreenStretching / 100.f; fx = (x - (width - 640.f / scale) / 2.f) * scale; } else { float scale = 640.f / width; fx = x * scale; scale /= config::ScreenStretching / 100.f; fy = (y - (height - 480.f / scale) / 2.f) * scale; } x = (int)std::round(fx); y = (int)std::round(fy); } void SetMousePosition(int x, int y, int width, int height, u32 mouseId) { if (mouseId >= ARRAY_SIZE(mo_x_abs)) return; mo_width = width; mo_height = height; if (config::Rotate90) { int t = y; y = x; x = height - 1 - t; std::swap(width, height); } screenToNative(x, y, width, height); mo_x_abs[mouseId] = x; mo_y_abs[mouseId] = y; if (mo_x_prev[mouseId] != -1) { std::lock_guard lock(relPosMutex); mo_x_delta[mouseId] += (float)(x - mo_x_prev[mouseId]) * config::MouseSensitivity / 100.f; mo_y_delta[mouseId] += (float)(y - mo_y_prev[mouseId]) * config::MouseSensitivity / 100.f; } mo_x_prev[mouseId] = x; mo_y_prev[mouseId] = y; } void SetRelativeMousePosition(float xrel, float yrel, u32 mouseId) { if (mouseId >= ARRAY_SIZE(mo_x_delta)) return; int width = mo_width; int height = mo_height; if (config::Rotate90) { std::swap(xrel, yrel); xrel = -xrel; std::swap(width, height); } float dx = xrel * config::MouseSensitivity / 100.f; float dy = yrel * config::MouseSensitivity / 100.f; { std::lock_guard lock(relPosMutex); mo_x_delta[mouseId] += dx; mo_y_delta[mouseId] += dy; } int minX = -width / 32; int minY = -height / 32; int maxX = width + width / 32; int maxY = height + height / 32; screenToNative(minX, minY, width, height); screenToNative(maxX, maxY, width, height); mo_x_abs[mouseId] += (int)std::round(dx); mo_y_abs[mouseId] += (int)std::round(dy); mo_x_abs[mouseId] = std::min(std::max(mo_x_abs[mouseId], minX), maxX); mo_y_abs[mouseId] = std::min(std::max(mo_y_abs[mouseId], minY), maxY); }