fix(macos): Mouse input broken in-game (#2550)

Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
This commit is contained in:
Vithorio Polten 2024-06-12 18:26:02 -03:00 committed by GitHub
commit 02ddbefd44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 305 additions and 72 deletions

View file

@ -672,6 +672,18 @@ namespace platf {
input_t
input();
/**
* @brief Gets the current mouse position on screen
* @param input The input_t instance to use.
* @return util::point_t (x, y)
*
* EXAMPLES:
* ```cpp
* auto [x, y] = get_mouse_loc(input);
* ```
*/
util::point_t
get_mouse_loc(input_t &input);
void
move_mouse(input_t &input, int deltaX, int deltaY);
void

View file

@ -1098,6 +1098,36 @@ namespace platf {
#endif
}
util::point_t
get_mouse_loc(input_t &input) {
#ifdef SUNSHINE_BUILD_X11
Display *xdisplay = ((input_raw_t *) input.get())->display;
if (!xdisplay) {
return util::point_t {};
}
Window root, root_return, child_return;
root = DefaultRootWindow(xdisplay);
int root_x, root_y;
int win_x, win_y;
unsigned int mask_return;
if (XQueryPointer(xdisplay, root, &root_return, &child_return, &root_x, &root_y, &win_x, &win_y, &mask_return)) {
BOOST_LOG(debug)
<< "Pointer is at:"sv << std::endl
<< " x: " << root_x << std::endl
<< " y: " << root_y << std::endl;
return util::point_t { (double) root_x, (double) root_y };
}
else {
BOOST_LOG(debug) << "Unable to query x11 pointer"sv << std::endl;
}
#else
BOOST_LOG(debug) << "Unable to query wayland pointer"sv << std::endl;
#endif
return util::point_t {};
}
/**
* @brief Absolute mouse move.
* @param input The input_t instance to use.

View file

@ -2,6 +2,8 @@
* @file src/platform/macos/input.cpp
* @brief todo
*/
#include "src/input.h"
#import <Carbon/Carbon.h>
#include <chrono>
#include <mach/mach.h>
@ -10,6 +12,11 @@
#include "src/platform/common.h"
#include "src/utility.h"
#include <ApplicationServices/ApplicationServices.h>
#include <CoreFoundation/CoreFoundation.h>
#include <iostream>
#include <thread>
/**
* @brief Delay for a double click, in milliseconds.
* @todo Make this configurable.
@ -315,85 +322,108 @@ const KeyCodeMap kKeyCodesMap[] = {
}
// returns current mouse location:
inline CGPoint
util::point_t
get_mouse_loc(input_t &input) {
return CGEventGetLocation(((macos_input_t *) input.get())->mouse_event);
// Creating a new event every time to avoid any reuse risk
const auto macos_input = static_cast<macos_input_t *>(input.get());
const auto snapshot_event = CGEventCreate(macos_input->source);
const auto current = CGEventGetLocation(snapshot_event);
CFRelease(snapshot_event);
return util::point_t {
current.x,
current.y
};
}
void
post_mouse(input_t &input, CGMouseButton button, CGEventType type, CGPoint location, int click_count) {
BOOST_LOG(debug) << "mouse_event: "sv << button << ", type: "sv << type << ", location:"sv << location.x << ":"sv << location.y << " click_count: "sv << click_count;
post_mouse(
input_t &input,
const CGMouseButton button,
const CGEventType type,
const util::point_t raw_location,
const util::point_t previous_location,
const int click_count) {
BOOST_LOG(debug) << "mouse_event: "sv << button << ", type: "sv << type << ", location:"sv << raw_location.x << ":"sv << raw_location.y << " click_count: "sv << click_count;
auto macos_input = (macos_input_t *) input.get();
auto display = macos_input->display;
auto event = macos_input->mouse_event;
const auto macos_input = static_cast<macos_input_t *>(input.get());
const auto display = macos_input->display;
const auto event = macos_input->mouse_event;
// get display bounds for current display
CGRect display_bounds = CGDisplayBounds(display);
const CGRect display_bounds = CGDisplayBounds(display);
// limit mouse to current display bounds
location.x = std::clamp(location.x, display_bounds.origin.x, display_bounds.origin.x + display_bounds.size.width - 1);
location.y = std::clamp(location.y, display_bounds.origin.y, display_bounds.origin.y + display_bounds.size.height - 1);
const auto location = CGPoint {
std::clamp(raw_location.x, display_bounds.origin.x, display_bounds.origin.x + display_bounds.size.width - 1),
std::clamp(raw_location.y, display_bounds.origin.y, display_bounds.origin.y + display_bounds.size.height - 1)
};
CGEventSetType(event, type);
CGEventSetLocation(event, location);
CGEventSetIntegerValueField(event, kCGMouseEventButtonNumber, button);
CGEventSetIntegerValueField(event, kCGMouseEventClickState, click_count);
CGEventPost(kCGHIDEventTap, event);
// Include deltas so some 3D applications can consume changes (game cameras, etc)
const double deltaX = raw_location.x - previous_location.x;
const double deltaY = raw_location.y - previous_location.y;
CGEventSetDoubleValueField(event, kCGMouseEventDeltaX, deltaX);
CGEventSetDoubleValueField(event, kCGMouseEventDeltaY, deltaY);
// For why this is here, see:
// https://stackoverflow.com/questions/15194409/simulated-mouseevent-not-working-properly-osx
CGWarpMouseCursorPosition(location);
CGEventPost(kCGHIDEventTap, event);
}
inline CGEventType
event_type_mouse(input_t &input) {
auto macos_input = ((macos_input_t *) input.get());
const auto macos_input = static_cast<macos_input_t *>(input.get());
if (macos_input->mouse_down[0]) {
return kCGEventLeftMouseDragged;
}
else if (macos_input->mouse_down[1]) {
if (macos_input->mouse_down[1]) {
return kCGEventOtherMouseDragged;
}
else if (macos_input->mouse_down[2]) {
if (macos_input->mouse_down[2]) {
return kCGEventRightMouseDragged;
}
else {
return kCGEventMouseMoved;
}
return kCGEventMouseMoved;
}
void
move_mouse(input_t &input, int deltaX, int deltaY) {
auto current = get_mouse_loc(input);
move_mouse(
input_t &input,
const int deltaX,
const int deltaY) {
const auto current = get_mouse_loc(input);
CGPoint location = CGPointMake(current.x + deltaX, current.y + deltaY);
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, 0);
const auto location = util::point_t { current.x + deltaX, current.y + deltaY };
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, current, 0);
}
void
abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
auto macos_input = static_cast<macos_input_t *>(input.get());
auto scaling = macos_input->displayScaling;
auto display = macos_input->display;
abs_mouse(
input_t &input,
const touch_port_t &touch_port,
const float x,
const float y) {
const auto macos_input = static_cast<macos_input_t *>(input.get());
const auto scaling = macos_input->displayScaling;
const auto display = macos_input->display;
CGPoint location = CGPointMake(x * scaling, y * scaling);
auto location = util::point_t { x * scaling, y * scaling };
CGRect display_bounds = CGDisplayBounds(display);
// in order to get the correct mouse location for capturing display , we need to add the display bounds to the location
location.x += display_bounds.origin.x;
location.y += display_bounds.origin.y;
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, 0);
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, get_mouse_loc(input), 0);
}
void
button_mouse(input_t &input, int button, bool release) {
button_mouse(input_t &input, const int button, const bool release) {
CGMouseButton mac_button;
CGEventType event;
auto mouse = ((macos_input_t *) input.get());
const auto macos_input = static_cast<macos_input_t *>(input.get());
switch (button) {
case 1:
@ -413,22 +443,24 @@ const KeyCodeMap kKeyCodesMap[] = {
return;
}
mouse->mouse_down[mac_button] = !release;
macos_input->mouse_down[mac_button] = !release;
// if the last mouse down was less than MULTICLICK_DELAY_MS, we send a double click event
auto now = std::chrono::steady_clock::now();
if (now < mouse->last_mouse_event[mac_button][release] + MULTICLICK_DELAY_MS) {
post_mouse(input, mac_button, event, get_mouse_loc(input), 2);
const auto now = std::chrono::steady_clock::now();
const auto mouse_position = get_mouse_loc(input);
if (now < macos_input->last_mouse_event[mac_button][release] + MULTICLICK_DELAY_MS) {
post_mouse(input, mac_button, event, mouse_position, mouse_position, 2);
}
else {
post_mouse(input, mac_button, event, get_mouse_loc(input), 1);
post_mouse(input, mac_button, event, mouse_position, mouse_position, 1);
}
mouse->last_mouse_event[mac_button][release] = now;
macos_input->last_mouse_event[mac_button][release] = now;
}
void
scroll(input_t &input, int high_res_distance) {
scroll(input_t &input, const int high_res_distance) {
CGEventRef upEvent = CGEventCreateScrollWheelEvent(
nullptr,
kCGScrollEventUnitLine,
@ -509,7 +541,7 @@ const KeyCodeMap kKeyCodesMap[] = {
input() {
input_t result { new macos_input_t() };
auto macos_input = (macos_input_t *) result.get();
const auto macos_input = static_cast<macos_input_t *>(result.get());
// Default to main display
macos_input->display = CGMainDisplayID();
@ -534,7 +566,7 @@ const KeyCodeMap kKeyCodesMap[] = {
}
// Input coordinates are based on the virtual resolution not the physical, so we need the scaling factor
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(macos_input->display);
const CGDisplayModeRef mode = CGDisplayCopyDisplayMode(macos_input->display);
macos_input->displayScaling = ((CGFloat) CGDisplayPixelsWide(macos_input->display)) / ((CGFloat) CGDisplayModeGetPixelWidth(mode));
CFRelease(mode);
@ -555,7 +587,7 @@ const KeyCodeMap kKeyCodesMap[] = {
void
freeInput(void *p) {
auto *input = (macos_input_t *) p;
const auto *input = static_cast<macos_input_t *>(p);
CFRelease(input->source);
CFRelease(input->kb_event);

View file

@ -542,6 +542,21 @@ namespace platf {
send_input(i);
}
util::point_t
get_mouse_loc(input_t &input) {
throw std::runtime_error("not implemented yet, has to pass tests");
// TODO: Tests are failing, something wrong here?
POINT p;
if (!GetCursorPos(&p)) {
return util::point_t { 0.0, 0.0 };
}
return util::point_t {
(double) p.x,
(double) p.y
};
}
void
button_mouse(input_t &input, int button, bool release) {
INPUT i {};

View file

@ -9,6 +9,7 @@
#include <memory>
#include <mutex>
#include <optional>
#include <ostream>
#include <string>
#include <string_view>
#include <type_traits>
@ -940,6 +941,16 @@ namespace util {
return std::string_view((const char *) &data, sizeof(T));
}
struct point_t {
double x;
double y;
friend std::ostream &
operator<<(std::ostream &os, const point_t &p) {
return (os << "Point(x: " << p.x << ", y: " << p.y << ")");
}
};
namespace endian {
template <class T = void>
struct endianness {