mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
Merge pull request #12958 from zaryabzip/feat/mouse-absolute-position
feat(libSceMouse): implement the absolute position calls as firmware 13.42 does
This commit is contained in:
10 files changed
+537
-19
No files matched your search
@@ -7,6 +7,18 @@ target_include_directories(libSceMouse PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
set_target_properties(libSceMouse PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(libSceMouse)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(mouse_absolute_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/MouseAbsolute.cpp)
|
||||
target_include_directories(mouse_absolute_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(mouse_absolute_tests PRIVATE libSceMouse libkernel libc ${CMAKE_DL_LIBS})
|
||||
configure_windows_unwind(mouse_absolute_tests)
|
||||
add_test(NAME mouse_absolute_position COMMAND mouse_absolute_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(mouse_absolute_position PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceMouse>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(mouse_tests
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests/Mouse.cpp
|
||||
|
||||
@@ -82,19 +82,16 @@ int APS5_VABI sceMouseSetProcessPrivilege() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceMouseReadAbsolutePosition() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceMouseReadAbsolutePosition(std::int32_t handle, MouseAbsoluteData* data, std::int32_t num) {
|
||||
return Mouse::ReadAbsolutePosition(handle, data, num);
|
||||
}
|
||||
|
||||
int APS5_VABI sceMouseSetAbsoluteHighPrecisionMode() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceMouseSetAbsoluteHighPrecisionMode(std::int32_t handle, std::int32_t mode) {
|
||||
return Mouse::SetAbsoluteHighPrecisionMode(handle, mode);
|
||||
}
|
||||
|
||||
int APS5_VABI sceMouseSetAbsoluteVirtualScreenSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceMouseSetAbsoluteVirtualScreenSize(std::int32_t handle, std::int32_t size) {
|
||||
return Mouse::SetAbsoluteVirtualScreenSize(handle, size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libSceMouse/include/mouse_structs.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -19,8 +20,8 @@ int APS5_VABI sceMouseMbusInit();
|
||||
int APS5_VABI sceMouseSetHandType();
|
||||
int APS5_VABI sceMouseSetPointerSpeed();
|
||||
int APS5_VABI sceMouseSetProcessPrivilege();
|
||||
int APS5_VABI sceMouseReadAbsolutePosition();
|
||||
int APS5_VABI sceMouseSetAbsoluteHighPrecisionMode();
|
||||
int APS5_VABI sceMouseSetAbsoluteVirtualScreenSize();
|
||||
int APS5_VABI sceMouseReadAbsolutePosition(std::int32_t handle, MouseAbsoluteData* data, std::int32_t num);
|
||||
int APS5_VABI sceMouseSetAbsoluteHighPrecisionMode(std::int32_t handle, std::int32_t mode);
|
||||
int APS5_VABI sceMouseSetAbsoluteVirtualScreenSize(std::int32_t handle, std::int32_t size);
|
||||
|
||||
}
|
||||
@@ -6,6 +6,8 @@
|
||||
struct MouseInputEvent {
|
||||
std::int32_t x = 0;
|
||||
std::int32_t y = 0;
|
||||
std::int32_t countsX = 0;
|
||||
std::int32_t countsY = 0;
|
||||
std::int32_t wheel = 0;
|
||||
std::int32_t tilt = 0;
|
||||
std::uint32_t button = 0;
|
||||
@@ -22,6 +24,9 @@ int Open(int userId, std::int32_t type, std::int32_t index, const MouseOpenParam
|
||||
int Close(std::int32_t handle);
|
||||
int Read(std::int32_t handle, MouseData* data, std::int32_t num);
|
||||
int ResetAbsolutePosition(std::int32_t handle, std::uint16_t x, std::uint16_t y);
|
||||
int SetAbsoluteVirtualScreenSize(std::int32_t handle, std::int32_t size);
|
||||
int SetAbsoluteHighPrecisionMode(std::int32_t handle, std::int32_t mode);
|
||||
int ReadAbsolutePosition(std::int32_t handle, MouseAbsoluteData* data, std::int32_t num);
|
||||
void Publish(const MouseInputEvent& event);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,18 @@ struct MouseOpenParam {
|
||||
std::uint8_t reserved[7];
|
||||
};
|
||||
|
||||
struct MouseAbsoluteData {
|
||||
std::uint64_t timestamp;
|
||||
std::uint8_t connected;
|
||||
std::uint8_t padding[3];
|
||||
std::uint32_t buttons;
|
||||
std::uint16_t x;
|
||||
std::uint16_t y;
|
||||
std::int32_t wheel;
|
||||
std::int32_t tilt;
|
||||
std::uint8_t reserved[12];
|
||||
};
|
||||
|
||||
constexpr int MOUSE_OK = 0;
|
||||
constexpr int MOUSE_ERROR_INVALID_ARG = static_cast<int>(0x80df0001u);
|
||||
constexpr int MOUSE_ERROR_INVALID_HANDLE = static_cast<int>(0x80df0003u);
|
||||
@@ -20,6 +32,13 @@ constexpr int MOUSE_HANDLE = 1;
|
||||
constexpr std::uint8_t MOUSE_OPEN_PARAM_MERGED = 1;
|
||||
|
||||
static_assert(sizeof(MouseOpenParam) == 8);
|
||||
static_assert(sizeof(MouseAbsoluteData) == 40);
|
||||
static_assert(offsetof(MouseAbsoluteData, connected) == 8);
|
||||
static_assert(offsetof(MouseAbsoluteData, buttons) == 12);
|
||||
static_assert(offsetof(MouseAbsoluteData, x) == 16);
|
||||
static_assert(offsetof(MouseAbsoluteData, y) == 18);
|
||||
static_assert(offsetof(MouseAbsoluteData, wheel) == 20);
|
||||
static_assert(offsetof(MouseAbsoluteData, tilt) == 24);
|
||||
static_assert(sizeof(MouseData) == 40);
|
||||
static_assert(alignof(MouseData) == 8);
|
||||
static_assert(offsetof(MouseData, timestamp) == 0);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
@@ -14,6 +16,74 @@ bool initialized = false;
|
||||
bool opened = false;
|
||||
bool connected = true;
|
||||
std::uint32_t buttons = 0;
|
||||
constexpr std::array<std::int64_t, 4> SCREEN_WIDTHS{1280, 1920, 2560, 3840};
|
||||
constexpr std::array<std::int64_t, 4> SCREEN_HEIGHTS{720, 1080, 1440, 2160};
|
||||
constexpr std::array<float, 5> CURVE_SPEEDS{0.0f, 0.4300079345703125f, 1.25f, 3.8600006103515625f, 40.0f};
|
||||
constexpr std::array<float, 5> CURVE_MOVES{0.0f, 1.0702667236328125f, 4.140625f, 18.984375f, 443.75f};
|
||||
struct AbsoluteSample {
|
||||
std::uint64_t timestamp;
|
||||
std::uint32_t buttons;
|
||||
std::int32_t x;
|
||||
std::int32_t y;
|
||||
std::int32_t wheel;
|
||||
std::int32_t tilt;
|
||||
bool connected;
|
||||
};
|
||||
|
||||
std::array<AbsoluteSample, MOUSE_MAX_DATA_NUM> absoluteHistory{};
|
||||
int absoluteHead = 0;
|
||||
int absoluteCount = 0;
|
||||
std::uint32_t screenSize = 1;
|
||||
std::int64_t absoluteX = 0;
|
||||
std::int64_t absoluteY = 0;
|
||||
std::uint8_t highPrecision = 0;
|
||||
float fractionX = 0.0f;
|
||||
float fractionY = 0.0f;
|
||||
|
||||
float curve(float speed) {
|
||||
std::size_t segment = 1;
|
||||
while (segment + 1 < CURVE_SPEEDS.size() && speed > CURVE_SPEEDS[segment]) ++segment;
|
||||
const float lowSpeed = CURVE_SPEEDS[segment - 1];
|
||||
const float lowMove = CURVE_MOVES[segment - 1];
|
||||
return lowMove + (speed - lowSpeed) * (CURVE_MOVES[segment] - lowMove) / (CURVE_SPEEDS[segment] - lowSpeed);
|
||||
}
|
||||
|
||||
std::int64_t step(float& fraction, float delta, float scale) {
|
||||
const float total = delta * scale + fraction;
|
||||
const float whole = std::trunc(total);
|
||||
fraction = total - whole;
|
||||
return static_cast<std::int64_t>(whole);
|
||||
}
|
||||
|
||||
void moveAbsolute(std::int32_t dx, std::int32_t dy) {
|
||||
std::int64_t moveX = dx;
|
||||
std::int64_t moveY = dy;
|
||||
if (highPrecision != 0) {
|
||||
moveX = 0;
|
||||
moveY = 0;
|
||||
const float length = std::sqrt(static_cast<float>(dx) * static_cast<float>(dx) + static_cast<float>(dy) * static_cast<float>(dy));
|
||||
if (length != 0.0f) {
|
||||
const float scale = curve(length * 120.0f / 420.0f) * 96.0f / 120.0f / length;
|
||||
moveX = step(fractionX, static_cast<float>(dx), scale);
|
||||
moveY = step(fractionY, static_cast<float>(dy), scale);
|
||||
}
|
||||
}
|
||||
absoluteX = std::clamp<std::int64_t>(absoluteX + moveX, 0, SCREEN_WIDTHS[screenSize] - 1);
|
||||
absoluteY = std::clamp<std::int64_t>(absoluteY + moveY, 0, SCREEN_HEIGHTS[screenSize] - 1);
|
||||
}
|
||||
|
||||
void enqueueAbsolute(const AbsoluteSample& sample) {
|
||||
if (absoluteCount == MOUSE_MAX_DATA_NUM) {
|
||||
absoluteHead = (absoluteHead + 1) % MOUSE_MAX_DATA_NUM;
|
||||
--absoluteCount;
|
||||
}
|
||||
absoluteHistory[(absoluteHead + absoluteCount) % MOUSE_MAX_DATA_NUM] = sample;
|
||||
++absoluteCount;
|
||||
}
|
||||
|
||||
bool sharesRecord(const AbsoluteSample& first, const AbsoluteSample& second) {
|
||||
return first.connected && second.connected && first.buttons == second.buttons;
|
||||
}
|
||||
|
||||
void enqueue(const MouseData& data) {
|
||||
if (count == MOUSE_MAX_DATA_NUM) {
|
||||
@@ -23,6 +93,24 @@ void enqueue(const MouseData& data) {
|
||||
history[(head + count) % MOUSE_MAX_DATA_NUM] = data;
|
||||
++count;
|
||||
}
|
||||
|
||||
bool motionOnly(const MouseData& data) {
|
||||
return data.wheel == 0 && data.tilt == 0 && (data.x_axis != 0 || data.y_axis != 0);
|
||||
}
|
||||
|
||||
std::int32_t saturatedSum(std::int32_t a, std::int32_t b) {
|
||||
return static_cast<std::int32_t>(std::clamp<std::int64_t>(static_cast<std::int64_t>(a) + b, INT32_MIN, INT32_MAX));
|
||||
}
|
||||
|
||||
bool mergeMotion(const MouseData& data) {
|
||||
if (count == 0 || !motionOnly(data)) return false;
|
||||
MouseData& tail = history[(head + count - 1) % MOUSE_MAX_DATA_NUM];
|
||||
if (!motionOnly(tail) || tail.buttons != data.buttons || tail.connected != data.connected) return false;
|
||||
tail.timestamp = data.timestamp;
|
||||
tail.x_axis = saturatedSum(tail.x_axis, data.x_axis);
|
||||
tail.y_axis = saturatedSum(tail.y_axis, data.y_axis);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Mouse {
|
||||
@@ -50,10 +138,19 @@ int Open(int userId, std::int32_t type, std::int32_t index, const MouseOpenParam
|
||||
buttons = 0;
|
||||
head = 0;
|
||||
count = 0;
|
||||
screenSize = 1;
|
||||
absoluteX = 0;
|
||||
absoluteY = 0;
|
||||
highPrecision = 0;
|
||||
fractionX = 0.0f;
|
||||
fractionY = 0.0f;
|
||||
MouseData initial{};
|
||||
initial.timestamp = sceKernelGetProcessTime();
|
||||
initial.connected = connected;
|
||||
enqueue(initial);
|
||||
absoluteHead = 0;
|
||||
absoluteCount = 0;
|
||||
enqueueAbsolute(AbsoluteSample{initial.timestamp, 0, 0, 0, 0, 0, connected});
|
||||
return MOUSE_HANDLE;
|
||||
}
|
||||
|
||||
@@ -65,6 +162,8 @@ int Close(std::int32_t handle) {
|
||||
buttons = 0;
|
||||
head = 0;
|
||||
count = 0;
|
||||
absoluteHead = 0;
|
||||
absoluteCount = 0;
|
||||
return MOUSE_OK;
|
||||
}
|
||||
|
||||
@@ -82,11 +181,87 @@ int Read(std::int32_t handle, MouseData* data, std::int32_t num) {
|
||||
return available;
|
||||
}
|
||||
|
||||
int SetAbsoluteVirtualScreenSize(std::int32_t handle, std::int32_t size) {
|
||||
std::lock_guard lock(mouseMutex);
|
||||
if (!initialized) return MOUSE_ERROR_NOT_INITIALIZED;
|
||||
const auto next = static_cast<std::uint32_t>(size);
|
||||
if (next >= SCREEN_WIDTHS.size()) return MOUSE_ERROR_INVALID_ARG;
|
||||
if (handle != MOUSE_HANDLE || !opened) return MOUSE_ERROR_INVALID_HANDLE;
|
||||
absoluteX = absoluteX * SCREEN_WIDTHS[next] / SCREEN_WIDTHS[screenSize];
|
||||
absoluteY = absoluteY * SCREEN_HEIGHTS[next] / SCREEN_HEIGHTS[screenSize];
|
||||
screenSize = next;
|
||||
return MOUSE_OK;
|
||||
}
|
||||
|
||||
int SetAbsoluteHighPrecisionMode(std::int32_t handle, std::int32_t mode) {
|
||||
std::lock_guard lock(mouseMutex);
|
||||
if (!initialized) return MOUSE_ERROR_NOT_INITIALIZED;
|
||||
if (handle != MOUSE_HANDLE || !opened) return MOUSE_ERROR_INVALID_HANDLE;
|
||||
highPrecision = static_cast<std::uint8_t>(mode);
|
||||
return MOUSE_OK;
|
||||
}
|
||||
|
||||
int ReadAbsolutePosition(std::int32_t handle, MouseAbsoluteData* data, std::int32_t num) {
|
||||
std::lock_guard lock(mouseMutex);
|
||||
if (!initialized) return MOUSE_ERROR_NOT_INITIALIZED;
|
||||
if (data == nullptr || num <= 0 || num > MOUSE_MAX_DATA_NUM) return MOUSE_ERROR_INVALID_ARG;
|
||||
if (handle != MOUSE_HANDLE || !opened) return MOUSE_ERROR_INVALID_HANDLE;
|
||||
if (!connected) {
|
||||
data[0] = MouseAbsoluteData{};
|
||||
return 1;
|
||||
}
|
||||
std::array<AbsoluteSample, MOUSE_MAX_DATA_NUM> samples{};
|
||||
std::array<bool, MOUSE_MAX_DATA_NUM> endsRecord{};
|
||||
const int pending = absoluteCount;
|
||||
for (int i = 0; i < pending; ++i) {
|
||||
samples[i] = absoluteHistory[(absoluteHead + i) % MOUSE_MAX_DATA_NUM];
|
||||
endsRecord[i] = true;
|
||||
}
|
||||
absoluteHead = 0;
|
||||
absoluteCount = 0;
|
||||
int records = pending;
|
||||
for (int i = 0; i + 1 < pending && records > num; ++i) {
|
||||
if (!sharesRecord(samples[i], samples[i + 1])) continue;
|
||||
endsRecord[i] = false;
|
||||
--records;
|
||||
}
|
||||
int first = pending;
|
||||
for (int kept = 0; first > 0 && kept < num;) {
|
||||
--first;
|
||||
if (endsRecord[first]) ++kept;
|
||||
while (first > 0 && !endsRecord[first - 1]) --first;
|
||||
}
|
||||
int written = 0;
|
||||
std::int32_t wheel = 0;
|
||||
std::int32_t tilt = 0;
|
||||
for (int i = first; i < pending; ++i) {
|
||||
const AbsoluteSample& sample = samples[i];
|
||||
if (!sample.connected) continue;
|
||||
moveAbsolute(sample.x, sample.y);
|
||||
wheel += sample.wheel;
|
||||
tilt += sample.tilt;
|
||||
if (!endsRecord[i]) continue;
|
||||
MouseAbsoluteData& record = data[written++];
|
||||
record.timestamp = sample.timestamp;
|
||||
record.connected = 1;
|
||||
record.buttons = sample.buttons;
|
||||
record.x = static_cast<std::uint16_t>(absoluteX);
|
||||
record.y = static_cast<std::uint16_t>(absoluteY);
|
||||
record.wheel = wheel;
|
||||
record.tilt = tilt;
|
||||
wheel = 0;
|
||||
tilt = 0;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
int ResetAbsolutePosition(std::int32_t handle, std::uint16_t x, std::uint16_t y) {
|
||||
std::lock_guard lock(mouseMutex);
|
||||
if (!initialized) return MOUSE_ERROR_NOT_INITIALIZED;
|
||||
if (handle != MOUSE_HANDLE || !opened) return MOUSE_ERROR_INVALID_HANDLE;
|
||||
if (x >= 1920 || y >= 1080) return MOUSE_ERROR_INVALID_ARG;
|
||||
if (x >= SCREEN_WIDTHS[screenSize] || y >= SCREEN_HEIGHTS[screenSize]) return MOUSE_ERROR_INVALID_ARG;
|
||||
absoluteX = x;
|
||||
absoluteY = y;
|
||||
return MOUSE_OK;
|
||||
}
|
||||
|
||||
@@ -100,19 +275,26 @@ void Publish(const MouseInputEvent& event) {
|
||||
return;
|
||||
}
|
||||
if (!opened) return;
|
||||
const bool countsGiven = event.countsX != 0 || event.countsY != 0;
|
||||
const bool noReport = event.button == 0 && !event.resetButtons && !event.connectionChange && event.wheel == 0 && event.tilt == 0;
|
||||
if (noReport && event.x == 0 && event.y == 0 && !countsGiven) return;
|
||||
if (event.resetButtons) buttons = 0;
|
||||
if (event.button != 0) {
|
||||
if (event.pressed) buttons |= event.button;
|
||||
else buttons &= ~event.button;
|
||||
}
|
||||
const std::uint64_t timestamp = sceKernelGetProcessTime();
|
||||
enqueueAbsolute(AbsoluteSample{timestamp, buttons, countsGiven ? event.countsX : event.x, countsGiven ? event.countsY : event.y, event.wheel, event.tilt, connected});
|
||||
if (noReport && event.x == 0 && event.y == 0) return;
|
||||
MouseData data{};
|
||||
data.timestamp = sceKernelGetProcessTime();
|
||||
data.timestamp = timestamp;
|
||||
data.connected = connected;
|
||||
data.buttons = buttons;
|
||||
data.x_axis = event.x;
|
||||
data.y_axis = event.y;
|
||||
data.wheel = event.wheel;
|
||||
data.tilt = event.tilt;
|
||||
if (event.button == 0 && !event.resetButtons && mergeMotion(data)) return;
|
||||
enqueue(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_mouse.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
|
||||
extern "C" {
|
||||
bool MouseIsOpen_nid_no_patch();
|
||||
@@ -14,6 +16,18 @@ bool MouseIsOpen_nid_no_patch();
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
struct AbsoluteRecord {
|
||||
std::uint64_t timestamp;
|
||||
std::uint8_t connected;
|
||||
std::uint8_t padding[3];
|
||||
std::uint32_t buttons;
|
||||
std::uint16_t x;
|
||||
std::uint16_t y;
|
||||
std::int32_t wheel;
|
||||
std::int32_t tilt;
|
||||
std::uint8_t reserved[12];
|
||||
};
|
||||
|
||||
int main() {
|
||||
MouseData data[64]{};
|
||||
Require(!MouseIsOpen_nid_no_patch());
|
||||
@@ -97,13 +111,87 @@ int main() {
|
||||
input.HandleEvent(event, 7);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 1) == 1 && data[0].connected);
|
||||
|
||||
for (int i = 0; i < 70; ++i) {
|
||||
for (int i = 0; i < 300; ++i) {
|
||||
MouseInputEvent motion{};
|
||||
motion.x = i;
|
||||
motion.x = -1;
|
||||
motion.y = i % 2;
|
||||
MousePublishInput_nid_no_patch(motion);
|
||||
}
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 8) == 1);
|
||||
Require(data[0].x_axis == -300 && data[0].y_axis == 150 && data[0].buttons == 0);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 1) == 0);
|
||||
|
||||
MouseInputEvent still{};
|
||||
MousePublishInput_nid_no_patch(still);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 1) == 0);
|
||||
for (int i = 0; i < 300; ++i) {
|
||||
MouseInputEvent alternating{};
|
||||
alternating.x = i % 2;
|
||||
MousePublishInput_nid_no_patch(alternating);
|
||||
}
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 8) == 1);
|
||||
Require(data[0].x_axis == 150 && data[0].y_axis == 0);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 1) == 0);
|
||||
|
||||
MouseInputEvent zero{};
|
||||
MouseInputEvent two{};
|
||||
two.x = 2;
|
||||
MouseInputEvent three{};
|
||||
three.x = 3;
|
||||
MouseInputEvent down{};
|
||||
down.button = 1;
|
||||
down.pressed = true;
|
||||
MouseInputEvent up{};
|
||||
up.button = 1;
|
||||
for (const MouseInputEvent& report : {two, zero, down, zero, three, zero, up, zero}) MousePublishInput_nid_no_patch(report);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 64) == 4);
|
||||
Require(data[0].x_axis == 2 && data[0].buttons == 0);
|
||||
Require(data[1].x_axis == 0 && data[1].buttons == 1);
|
||||
Require(data[2].x_axis == 3 && data[2].buttons == 1);
|
||||
Require(data[3].x_axis == 0 && data[3].buttons == 0);
|
||||
|
||||
MouseInputEvent step{};
|
||||
step.x = 3;
|
||||
MousePublishInput_nid_no_patch(step);
|
||||
MousePublishInput_nid_no_patch(step);
|
||||
MouseInputEvent press{};
|
||||
press.button = 1;
|
||||
press.pressed = true;
|
||||
MousePublishInput_nid_no_patch(press);
|
||||
MousePublishInput_nid_no_patch(step);
|
||||
MousePublishInput_nid_no_patch(step);
|
||||
MouseInputEvent wheel{};
|
||||
wheel.wheel = 1;
|
||||
MousePublishInput_nid_no_patch(wheel);
|
||||
MousePublishInput_nid_no_patch(step);
|
||||
MouseInputEvent release{};
|
||||
release.button = 1;
|
||||
MousePublishInput_nid_no_patch(release);
|
||||
MousePublishInput_nid_no_patch(step);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 64) == 7);
|
||||
Require(data[0].x_axis == 6 && data[0].buttons == 0);
|
||||
Require(data[1].x_axis == 0 && data[1].buttons == 1);
|
||||
Require(data[2].x_axis == 6 && data[2].buttons == 1);
|
||||
Require(data[3].wheel == 1 && data[3].x_axis == 0 && data[3].buttons == 1);
|
||||
Require(data[4].x_axis == 3 && data[4].wheel == 0 && data[4].buttons == 1);
|
||||
Require(data[5].x_axis == 0 && data[5].buttons == 0);
|
||||
Require(data[6].x_axis == 3 && data[6].buttons == 0);
|
||||
Require(data[2].timestamp >= data[1].timestamp && data[6].timestamp >= data[5].timestamp);
|
||||
|
||||
MouseInputEvent large{};
|
||||
large.x = INT32_MAX;
|
||||
MousePublishInput_nid_no_patch(large);
|
||||
MousePublishInput_nid_no_patch(large);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 8) == 1 && data[0].x_axis == INT32_MAX);
|
||||
|
||||
for (int i = 0; i < 70; ++i) {
|
||||
MouseInputEvent click{};
|
||||
click.button = 1;
|
||||
click.pressed = (i % 2) == 0;
|
||||
MousePublishInput_nid_no_patch(click);
|
||||
}
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 64) == 64);
|
||||
Require(data[0].x_axis == 6 && data[63].x_axis == 69);
|
||||
Require(data[0].buttons == 1 && data[63].buttons == 0 && data[62].buttons == 1);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 1) == 0);
|
||||
Require(sceMouseClose(MOUSE_HANDLE) == MOUSE_OK);
|
||||
Require(!MouseIsOpen_nid_no_patch());
|
||||
@@ -119,4 +207,22 @@ int main() {
|
||||
Require(sceMouseClose(MOUSE_HANDLE) == MOUSE_OK);
|
||||
Require(!MouseIsOpen_nid_no_patch());
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 959, 539) == MOUSE_ERROR_INVALID_HANDLE);
|
||||
|
||||
using ReadAbsolute = int (APS5_VABI*)(std::int32_t, AbsoluteRecord*, std::int32_t);
|
||||
const auto readAbsolute = reinterpret_cast<ReadAbsolute>(reinterpret_cast<void (*)()>(&sceMouseReadAbsolutePosition));
|
||||
AbsoluteRecord absolute[MOUSE_MAX_DATA_NUM]{};
|
||||
Require(sceMouseOpen(1, 0, 0, nullptr) == MOUSE_HANDLE);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 64) == 1);
|
||||
Require(readAbsolute(MOUSE_HANDLE, absolute, MOUSE_MAX_DATA_NUM) == 1);
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK);
|
||||
input.SetPresentation(3840, 2160, 1920, 1080);
|
||||
event = {};
|
||||
event.type = SDL_MOUSEMOTION;
|
||||
event.motion.windowID = 7;
|
||||
event.motion.xrel = 10;
|
||||
event.motion.yrel = 5;
|
||||
input.HandleEvent(event, 7);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, data, 1) == 1 && data[0].x_axis == 20 && data[0].y_axis == 10);
|
||||
Require(readAbsolute(MOUSE_HANDLE, absolute, 1) == 1 && absolute[0].x == 110 && absolute[0].y == 105);
|
||||
Require(sceMouseClose(MOUSE_HANDLE) == MOUSE_OK);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
#include "prx/libSceMouse/include/Api.hpp"
|
||||
#include "prx/libSceMouse/include/MouseState.hpp"
|
||||
#include "prx/libSceMouse/include/mouse_structs.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct AbsoluteRecord {
|
||||
std::uint64_t timestamp;
|
||||
std::uint8_t connected;
|
||||
std::uint8_t padding[3];
|
||||
std::uint32_t buttons;
|
||||
std::uint16_t x;
|
||||
std::uint16_t y;
|
||||
std::int32_t wheel;
|
||||
std::int32_t tilt;
|
||||
std::uint8_t reserved[12];
|
||||
};
|
||||
static_assert(sizeof(AbsoluteRecord) == 40);
|
||||
|
||||
using SetValueFn = int (APS5_VABI*)(std::int32_t, std::int32_t);
|
||||
using ReadAbsoluteFn = int (APS5_VABI*)(std::int32_t, AbsoluteRecord*, std::int32_t);
|
||||
|
||||
template <typename Function>
|
||||
Function Resolve(const char* name) {
|
||||
#ifdef _WIN32
|
||||
void* symbol = reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA("libSceMouse.prx"), name));
|
||||
#else
|
||||
void* symbol = dlsym(RTLD_DEFAULT, name);
|
||||
#endif
|
||||
if (!symbol) {
|
||||
std::fprintf(stderr, "%s is not exported by libSceMouse\n", name);
|
||||
std::abort();
|
||||
}
|
||||
return reinterpret_cast<Function>(symbol);
|
||||
}
|
||||
|
||||
void Require(bool value, const char* what) {
|
||||
if (value) return;
|
||||
std::fprintf(stderr, "mouse absolute position: %s\n", what);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void Motion(std::int32_t x, std::int32_t y) {
|
||||
MouseInputEvent event{};
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
MousePublishInput_nid_no_patch(event);
|
||||
}
|
||||
|
||||
void Button(std::uint32_t button, bool pressed) {
|
||||
MouseInputEvent event{};
|
||||
event.button = button;
|
||||
event.pressed = pressed;
|
||||
MousePublishInput_nid_no_patch(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
const auto setSize = Resolve<SetValueFn>("sceMouseSetAbsoluteVirtualScreenSize");
|
||||
const auto setMode = Resolve<SetValueFn>("sceMouseSetAbsoluteHighPrecisionMode");
|
||||
const auto readAbsolute = Resolve<ReadAbsoluteFn>("sceMouseReadAbsolutePosition");
|
||||
AbsoluteRecord records[64];
|
||||
|
||||
Require(setSize(MOUSE_HANDLE, 9) == MOUSE_ERROR_NOT_INITIALIZED, "the screen size before initialization did not return NOT_INITIALIZED first");
|
||||
Require(setMode(MOUSE_HANDLE, 1) == MOUSE_ERROR_NOT_INITIALIZED, "the precision mode before initialization did not return NOT_INITIALIZED");
|
||||
Require(readAbsolute(MOUSE_HANDLE, nullptr, 0) == MOUSE_ERROR_NOT_INITIALIZED, "a read before initialization did not return NOT_INITIALIZED first");
|
||||
Require(sceMouseInit() == MOUSE_OK, "initialization failed");
|
||||
Require(setSize(MOUSE_HANDLE, 4) == MOUSE_ERROR_INVALID_ARG && setSize(MOUSE_HANDLE, -1) == MOUSE_ERROR_INVALID_ARG, "a size past 3 was not refused before the handle");
|
||||
Require(setSize(MOUSE_HANDLE, 1) == MOUSE_ERROR_INVALID_HANDLE, "the screen size of a closed handle was set");
|
||||
Require(setMode(MOUSE_HANDLE, 1) == MOUSE_ERROR_INVALID_HANDLE, "the precision mode of a closed handle was set");
|
||||
Require(readAbsolute(MOUSE_HANDLE, nullptr, 1) == MOUSE_ERROR_INVALID_ARG && readAbsolute(MOUSE_HANDLE, records, 0) == MOUSE_ERROR_INVALID_ARG && readAbsolute(MOUSE_HANDLE, records, 65) == MOUSE_ERROR_INVALID_ARG, "a bad read was not refused before the handle");
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == MOUSE_ERROR_INVALID_HANDLE, "a closed handle was read");
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 5000, 0) == MOUSE_ERROR_INVALID_HANDLE, "a reset with a bad handle and a bad position did not return INVALID_HANDLE");
|
||||
|
||||
Require(sceMouseOpen(1, 0, 0, nullptr) == MOUSE_HANDLE, "open failed");
|
||||
std::memset(records, 0xA5, sizeof(records));
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 64) == 1, "the sample queued at open was not read");
|
||||
Require(records[0].connected == 1 && records[0].x == 0 && records[0].y == 0, "the position does not start at the top-left corner");
|
||||
Require(records[0].padding[0] == 0xA5 && records[0].padding[2] == 0xA5 && records[0].reserved[0] == 0xA5 && records[0].reserved[11] == 0xA5, "bytes the console leaves alone were written");
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 0, "a read with no samples returned a record");
|
||||
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 1920, 0) == MOUSE_ERROR_INVALID_ARG, "x 1920 was taken at 1920x1080");
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 1000, 500) == MOUSE_OK, "the reset failed");
|
||||
Require(setSize(MOUSE_HANDLE, 3) == MOUSE_OK, "the size 3840x2160 was refused");
|
||||
Button(1, true);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 2000 && records[0].y == 1000 && records[0].buttons == 1, "the position was not scaled to 3840x2160");
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 3839, 2159) == MOUSE_OK, "the corner of 3840x2160 was refused");
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 2000, 1000) == MOUSE_OK, "the reset at 3840x2160 failed");
|
||||
Require(setSize(MOUSE_HANDLE, 0) == MOUSE_OK, "the size 1280x720 was refused");
|
||||
Button(1, false);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 666 && records[0].y == 333 && records[0].buttons == 0, "the position was not scaled to 1280x720 with truncation");
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 1280, 0) == MOUSE_ERROR_INVALID_ARG, "x 1280 was taken at 1280x720");
|
||||
|
||||
Require(setSize(MOUSE_HANDLE, 1) == MOUSE_OK && sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "returning to 1920x1080 failed");
|
||||
Motion(5, -3);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 105 && records[0].y == 97, "motion did not move the position one for one");
|
||||
Motion(-5000, 5000);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 0 && records[0].y == 1079, "the position was not clamped to the screen");
|
||||
MouseInputEvent wheel{};
|
||||
wheel.wheel = 2;
|
||||
wheel.tilt = -1;
|
||||
MousePublishInput_nid_no_patch(wheel);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].wheel == 2 && records[0].tilt == -1, "the wheel and tilt were not copied");
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "the reset before the merge checks failed");
|
||||
Motion(1, 0);
|
||||
Motion(2, 0);
|
||||
Motion(4, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 3) == 3 && records[0].x == 101 && records[1].x == 103 && records[2].x == 107, "three samples read with num 3 did not give one record each");
|
||||
Motion(1, 0);
|
||||
Motion(2, 0);
|
||||
Motion(4, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 114, "three samples read with num 1 did not share one record with all their motion");
|
||||
|
||||
Require(setMode(MOUSE_HANDLE, 0x100) == MOUSE_OK && sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "the precision mode was refused");
|
||||
Motion(1, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 101, "mode 0x100 did not keep its low byte 0, which is off");
|
||||
Require(setMode(MOUSE_HANDLE, 1) == MOUSE_OK && sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "turning high precision on failed");
|
||||
Motion(1, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 100, "a delta of 1 moved a whole pixel in high precision");
|
||||
Motion(1, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 101, "the kept fraction was not added to the next delta");
|
||||
Motion(10, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 111, "a delta of 10 did not follow the curve");
|
||||
Motion(100, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 359 && records[0].y == 100, "a delta of 100 did not follow the curve");
|
||||
|
||||
MouseData relative[64];
|
||||
const auto reopen = [&](const char* what) {
|
||||
Require(sceMouseClose(MOUSE_HANDLE) == MOUSE_OK && sceMouseOpen(1, 0, 0, nullptr) == MOUSE_HANDLE, what);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 64) == 1 && sceMouseRead(MOUSE_HANDLE, relative, 64) == 1, what);
|
||||
};
|
||||
reopen("reopening for the queued precision check failed");
|
||||
Require(setMode(MOUSE_HANDLE, 1) == MOUSE_OK && sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "turning high precision on failed");
|
||||
for (int i = 0; i < 10; ++i) Motion(1, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 105, "ten queued deltas of 1 did not each go through the curve");
|
||||
reopen("reopening for the incremental precision check failed");
|
||||
Require(setMode(MOUSE_HANDLE, 1) == MOUSE_OK && sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "turning high precision on failed");
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Motion(1, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1, "a delta read on its own gave no record");
|
||||
}
|
||||
Require(records[0].x == 105, "ten deltas of 1 read one at a time did not end where the queued ones did");
|
||||
|
||||
Require(setMode(MOUSE_HANDLE, 0) == MOUSE_OK && sceMouseResetAbsolutePosition(MOUSE_HANDLE, 0, 100) == MOUSE_OK, "the edge reset failed");
|
||||
Motion(-5, 0);
|
||||
Motion(5, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 5, "queued motion at the left edge was not clamped per sample");
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 0, 100) == MOUSE_OK, "the edge reset failed");
|
||||
Motion(-5, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 0, "motion past the left edge was not clamped");
|
||||
Motion(5, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 5, "motion read one at a time at the edge did not end where the queued motion did");
|
||||
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "the reset before the size check failed");
|
||||
Motion(10, 0);
|
||||
Require(setSize(MOUSE_HANDLE, 3) == MOUSE_OK && readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 210 && records[0].y == 200, "a queued sample was not applied at the size in force when it was read");
|
||||
Require(setSize(MOUSE_HANDLE, 1) == MOUSE_OK && sceMouseResetAbsolutePosition(MOUSE_HANDLE, 100, 100) == MOUSE_OK, "returning to 1920x1080 failed");
|
||||
Motion(10, 0);
|
||||
Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 50, 50) == MOUSE_OK && readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 60 && records[0].y == 50, "a queued sample did not move the position a later reset set");
|
||||
|
||||
Require(sceMouseRead(MOUSE_HANDLE, relative, 64) >= 0, "draining sceMouseRead failed");
|
||||
Motion(7, 0);
|
||||
Require(sceMouseRead(MOUSE_HANDLE, relative, 1) == 1 && relative[0].x_axis == 7, "sceMouseRead did not get the motion");
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 67, "a relative read took the sample from the absolute reads");
|
||||
Motion(3, 0);
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == 1 && records[0].x == 70, "the absolute read missed the motion");
|
||||
Require(sceMouseRead(MOUSE_HANDLE, relative, 1) == 1 && relative[0].x_axis == 3, "an absolute read took the sample from sceMouseRead");
|
||||
|
||||
MouseInputEvent disconnect{};
|
||||
disconnect.connectionChange = true;
|
||||
disconnect.connected = false;
|
||||
MousePublishInput_nid_no_patch(disconnect);
|
||||
std::memset(records, 0xA5, sizeof(records));
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 4) == 1, "a disconnected mouse did not return one record");
|
||||
const AbsoluteRecord zero{};
|
||||
Require(std::memcmp(&records[0], &zero, sizeof(zero)) == 0 && records[1].connected == 0xA5, "a disconnected mouse did not return one zeroed record");
|
||||
|
||||
Require(sceMouseClose(MOUSE_HANDLE) == MOUSE_OK, "close failed");
|
||||
Require(readAbsolute(MOUSE_HANDLE, records, 1) == MOUSE_ERROR_INVALID_HANDLE, "a closed handle was read");
|
||||
std::puts("mouse absolute position tests passed");
|
||||
return 0;
|
||||
}
|
||||
@@ -14,6 +14,8 @@ void MouseInput::HandleEvent(const SDL_Event& event, unsigned windowId) {
|
||||
if (event.motion.windowID != windowId || !focused || ModalDialogCapturesInput_nid_postfix()) return;
|
||||
input.x = scaled(event.motion.xrel, scaleX, remainderX);
|
||||
input.y = scaled(event.motion.yrel, scaleY, remainderY);
|
||||
input.countsX = event.motion.xrel;
|
||||
input.countsY = event.motion.yrel;
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
- [sceMouseDisconnectAll, sceMouseVirtualDeviceAddDevice, sceMouseVirtualDeviceDeleteDevice and sceMouseVirtualDeviceGetCursor](../../../core/libs/prx/libSceMouse/InventoryExports.cpp) - the names and NIDs are recorded in the [13.60 export inventory](https://github.com/xdCloudy/PS5-13.60-Function-Stub-Library/blob/e495d3cf24aea17cefda59decdfda9e3e4e3e5ff/data/firmware-13.60/libscemouse-acdd5b5e23.jsonl). Signatures and behavior remain unknown; the parameterless `int` declarations only resolve imports and every call throws. Not measured on a console.
|
||||
|
||||
- [sceMouseConnectPort, sceMouseDebugGetDeviceId, sceMouseDeviceOpen, sceMouseDisconnectDevice, sceMouseDisconnectPort, sceMouseGetDeviceInfo, sceMouseMbusInit, sceMouseSetHandType, sceMouseSetPointerSpeed and sceMouseSetProcessPrivilege](../../../core/libs/prx/libSceMouse/Export.cpp) (libSceMouse) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMouse.c); the no-argument prototypes follow [shadPS4's PS4 stubs](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/mouse/mouse.cpp#L34-L146), not measured on a console. These declarations have no parameters solely to resolve known imports and make no ABI claim.
|
||||
- [sceMouseReadAbsolutePosition, sceMouseSetAbsoluteHighPrecisionMode and sceMouseSetAbsoluteVirtualScreenSize](../../../core/libs/prx/libSceMouse/Export.cpp) (libSceMouse) - the names match unresolved NIDs reported by a title, but their signatures and behavior are not verified. These no-argument declarations only resolve imports; the exports throw if called.
|
||||
|
||||
### Functional
|
||||
|
||||
- [sceMouseResetAbsolutePosition](../../../core/libs/prx/libSceMouse/Export.cpp) (libSceMouse) - the position is not stored because the host reports relative motion only. Native input widths and validation are confirmed, including the default 1920 by 1080 bounds; other native coordinate modes and absolute reports remain unimplemented
|
||||
- [Publish](../../../core/libs/prx/libSceMouse/src/mouse_impl.cpp) (libSceMouse) - consecutive motion-only reports with the same buttons are summed into the newest unread entry, so a title that reads a few entries per frame gets current motion instead of a full queue of old reports. Reports with no motion and no button, wheel, tilt or connection change are dropped. Per-report granularity and the console's own report rate are not reproduced
|
||||
|
||||
- [sceMouseResetAbsolutePosition, sceMouseSetAbsoluteVirtualScreenSize, sceMouseSetAbsoluteHighPrecisionMode, sceMouseReadAbsolutePosition](../../../core/libs/prx/libSceMouse/src/mouse_impl.cpp) (libSceMouse) - follow firmware 13.42 [run natively](https://github.com/boykopovar/AnyPS5/issues/11477#issuecomment-6502712241) with the driver stubbed: checks and order, screen sizes and rescale, the mode byte, the curve with its fraction, the 40-byte record. Host choices, after [XatiX's title-tested version](https://github.com/boykopovar/AnyPS5/issues/11477#issuecomment-6503782633): each host event is one sample with unscaled counts, in its own queue, so `sceMouseRead`'s motion merging does not feed the curve. A read takes all pending samples; past `num`, same-button neighbours share a record, each still moving the pointer, and the last `num` records are kept. Speed is taken as 4, the hand swap and flagged bit never apply, and a disconnected mouse stands in for a zero driver status
|
||||
Reference in new issue
Block a user