feat(libSceKeyboard): implement the keyboard library
Keyboard input follows libSceMouse: the video out event loop forwards SDL key and focus events, whose scancodes are USB HID usages, and the library keeps the pressed keys, modifiers and lock LEDs in a 16-entry history for sceKeyboardRead and a snapshot for sceKeyboardReadState. sceKeyboardGetKey2Char translates the 101-key arrangement; the 106-key one throws and is listed as technical debt. KeyboardData holds 16 key codes (96 bytes), not 6. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bc51b337ad
commit
63501b6d73
|
|
@ -974,7 +974,7 @@ struct MouseData {
|
|||
std::uint8_t reserved[8];
|
||||
};
|
||||
|
||||
constexpr std::uint32_t KEYBOARD_MAX_KEYCODES = 6;
|
||||
constexpr std::uint32_t KEYBOARD_MAX_KEYCODES = 16;
|
||||
|
||||
struct KeyboardData {
|
||||
std::uint64_t timestamp;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
add_library(libSceKeyboard SHARED EXCLUDE_FROM_ALL
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/keyboard_impl.cpp
|
||||
)
|
||||
target_include_directories(libSceKeyboard PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
set_target_properties(libSceKeyboard PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(libSceKeyboard)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(keyboard_tests
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests/Keyboard.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../libSceVideoOut/src/KeyboardInput.cpp
|
||||
)
|
||||
target_include_directories(keyboard_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/3rdparty/SDL2/include)
|
||||
target_link_libraries(keyboard_tests PRIVATE libSceKeyboard libkernel libc)
|
||||
configure_windows_unwind(keyboard_tests)
|
||||
add_test(NAME keyboard_input COMMAND keyboard_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(keyboard_input PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceKeyboard>")
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1,54 +1,34 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libSceKeyboard/include/KeyboardState.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceKeyboardClose(int32_t handle) {
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKeyboardGetKey2Char(int32_t handle, int32_t arrange, uint32_t led, uint32_t modifier_key, uint16_t key_code, KeyboardCharData* char_data) {
|
||||
(void)handle;
|
||||
(void)arrange;
|
||||
(void)led;
|
||||
(void)modifier_key;
|
||||
(void)key_code;
|
||||
(void)char_data;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKeyboardInit(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return Keyboard::Initialize();
|
||||
}
|
||||
|
||||
int APS5_VABI sceKeyboardOpen(int user_id, int32_t type, int32_t index, const void* param) {
|
||||
(void)user_id;
|
||||
(void)type;
|
||||
(void)index;
|
||||
(void)param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
(void)param;
|
||||
return Keyboard::Open(user_id, type, index);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKeyboardClose(int32_t handle) {
|
||||
return Keyboard::Close(handle);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKeyboardRead(int32_t handle, KeyboardData* data, int32_t num) {
|
||||
(void)handle;
|
||||
(void)data;
|
||||
(void)num;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return Keyboard::Read(handle, data, num);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKeyboardReadState(int32_t handle, KeyboardData* data) {
|
||||
(void)handle;
|
||||
(void)data;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return Keyboard::ReadState(handle, data);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKeyboardGetKey2Char(int32_t handle, int32_t arrange, uint32_t led, uint32_t modifier_key, uint16_t key_code, KeyboardCharData* char_data) {
|
||||
return Keyboard::GetKey2Char(handle, arrange, led, modifier_key, key_code, char_data);
|
||||
}
|
||||
|
||||
void KeyboardPublishInput_nid_postfix(const KeyboardInputEvent& event) {
|
||||
Keyboard::Publish(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEKEYBOARD_KEYBOARDSTATE_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEKEYBOARD_KEYBOARDSTATE_HPP
|
||||
|
||||
#include "prx/libSceKeyboard/include/keyboard_structs.h"
|
||||
|
||||
struct KeyboardInputEvent {
|
||||
std::uint16_t keyCode = 0;
|
||||
bool pressed = false;
|
||||
std::uint32_t led = 0;
|
||||
bool resetKeys = false;
|
||||
bool connectionChange = false;
|
||||
bool connected = true;
|
||||
};
|
||||
|
||||
namespace Keyboard {
|
||||
int Initialize();
|
||||
int Open(int userId, std::int32_t type, std::int32_t index);
|
||||
int Close(std::int32_t handle);
|
||||
int Read(std::int32_t handle, KeyboardData* data, std::int32_t num);
|
||||
int ReadState(std::int32_t handle, KeyboardData* data);
|
||||
int GetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint32_t led, std::uint32_t modifierKey, std::uint16_t keyCode, KeyboardCharData* charData);
|
||||
void Publish(const KeyboardInputEvent& event);
|
||||
}
|
||||
|
||||
extern "C" void KeyboardPublishInput_nid_postfix(const KeyboardInputEvent& event);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEKEYBOARD_KEYBOARD_STRUCTS_H
|
||||
#define CORE_LIBS_PRX_LIBSCEKEYBOARD_KEYBOARD_STRUCTS_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
constexpr int KEYBOARD_OK = 0;
|
||||
constexpr int KEYBOARD_ERROR_INVALID_ARG = static_cast<int>(0x80da0001u);
|
||||
constexpr int KEYBOARD_ERROR_INVALID_HANDLE = static_cast<int>(0x80da0003u);
|
||||
constexpr int KEYBOARD_ERROR_ALREADY_OPENED = static_cast<int>(0x80da0004u);
|
||||
constexpr int KEYBOARD_ERROR_NOT_INITIALIZED = static_cast<int>(0x80da0005u);
|
||||
constexpr int KEYBOARD_MAX_DATA_NUM = 16;
|
||||
constexpr int KEYBOARD_HANDLE = 1;
|
||||
constexpr std::int32_t KEYBOARD_ARRANGEMENT_101 = 0;
|
||||
constexpr std::int32_t KEYBOARD_ARRANGEMENT_106 = 1;
|
||||
|
||||
constexpr std::uint32_t KEYBOARD_LED_NUM_LOCK = 1;
|
||||
constexpr std::uint32_t KEYBOARD_LED_CAPS_LOCK = 2;
|
||||
constexpr std::uint32_t KEYBOARD_LED_SCROLL_LOCK = 4;
|
||||
constexpr std::uint32_t KEYBOARD_MOD_LEFT_SHIFT = 2;
|
||||
constexpr std::uint32_t KEYBOARD_MOD_RIGHT_SHIFT = 32;
|
||||
|
||||
constexpr std::uint16_t KEYBOARD_KEY_LEFT_CTRL = 0xe0;
|
||||
constexpr std::uint16_t KEYBOARD_KEY_RIGHT_GUI = 0xe7;
|
||||
|
||||
static_assert(sizeof(KeyboardData) == 96);
|
||||
static_assert(alignof(KeyboardData) == 8);
|
||||
static_assert(offsetof(KeyboardData, timestamp) == 0);
|
||||
static_assert(offsetof(KeyboardData, intercepted) == 8);
|
||||
static_assert(offsetof(KeyboardData, connected) == 16);
|
||||
static_assert(offsetof(KeyboardData, length) == 20);
|
||||
static_assert(offsetof(KeyboardData, led) == 24);
|
||||
static_assert(offsetof(KeyboardData, modifier_key) == 28);
|
||||
static_assert(offsetof(KeyboardData, key_code) == 32);
|
||||
static_assert(offsetof(KeyboardData, reserve2) == 64);
|
||||
static_assert(sizeof(KeyboardCharData) == 20);
|
||||
static_assert(offsetof(KeyboardCharData, length) == 4);
|
||||
static_assert(offsetof(KeyboardCharData, char_code) == 8);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
#include "prx/libSceKeyboard/include/KeyboardState.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/Time/include/Time.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
std::mutex keyboardMutex;
|
||||
std::array<KeyboardData, KEYBOARD_MAX_DATA_NUM> history{};
|
||||
int head = 0;
|
||||
int count = 0;
|
||||
bool initialized = false;
|
||||
bool opened = false;
|
||||
bool connected = true;
|
||||
std::uint32_t led = 0;
|
||||
std::uint32_t modifiers = 0;
|
||||
std::array<std::uint16_t, KEYBOARD_MAX_KEYCODES> keys{};
|
||||
int keyCount = 0;
|
||||
|
||||
constexpr char US_NORMAL[] = "1234567890\n\0\b\t -=[]\\\0;'`,./";
|
||||
constexpr char US_SHIFTED[] = "!@#$%^&*()\n\0\b\t _+{}|\0:\"~<>?";
|
||||
constexpr char KEYPAD_DIGITS[] = "1234567890.";
|
||||
|
||||
KeyboardData snapshot() {
|
||||
KeyboardData data{};
|
||||
data.timestamp = sceKernelGetProcessTime();
|
||||
data.connected = connected;
|
||||
data.length = keyCount;
|
||||
data.led = led;
|
||||
data.modifier_key = modifiers;
|
||||
std::copy_n(keys.begin(), keyCount, data.key_code);
|
||||
return data;
|
||||
}
|
||||
|
||||
void enqueue(const KeyboardData& data) {
|
||||
if (count == KEYBOARD_MAX_DATA_NUM) {
|
||||
head = (head + 1) % KEYBOARD_MAX_DATA_NUM;
|
||||
--count;
|
||||
}
|
||||
history[(head + count) % KEYBOARD_MAX_DATA_NUM] = data;
|
||||
++count;
|
||||
}
|
||||
|
||||
void clearKeys() {
|
||||
modifiers = 0;
|
||||
keyCount = 0;
|
||||
}
|
||||
|
||||
bool updateKey(std::uint16_t keyCode, bool pressed) {
|
||||
if (keyCode >= KEYBOARD_KEY_LEFT_CTRL && keyCode <= KEYBOARD_KEY_RIGHT_GUI) {
|
||||
const std::uint32_t bit = 1u << (keyCode - KEYBOARD_KEY_LEFT_CTRL);
|
||||
const std::uint32_t updated = pressed ? modifiers | bit : modifiers & ~bit;
|
||||
if (updated == modifiers) return false;
|
||||
modifiers = updated;
|
||||
return true;
|
||||
}
|
||||
const auto end = keys.begin() + keyCount;
|
||||
const auto found = std::find(keys.begin(), end, keyCode);
|
||||
if (pressed) {
|
||||
if (found != end || keyCount == static_cast<int>(KEYBOARD_MAX_KEYCODES)) return false;
|
||||
keys[keyCount++] = keyCode;
|
||||
return true;
|
||||
}
|
||||
if (found == end) return false;
|
||||
std::copy(found + 1, end, found);
|
||||
--keyCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::uint16_t usCharacter(std::uint32_t ledState, std::uint32_t modifierKey, std::uint16_t keyCode) {
|
||||
const bool shift = (modifierKey & (KEYBOARD_MOD_LEFT_SHIFT | KEYBOARD_MOD_RIGHT_SHIFT)) != 0;
|
||||
if (keyCode >= 0x04 && keyCode <= 0x1d) {
|
||||
const bool upper = shift != ((ledState & KEYBOARD_LED_CAPS_LOCK) != 0);
|
||||
return static_cast<std::uint16_t>((upper ? 'A' : 'a') + keyCode - 0x04);
|
||||
}
|
||||
if (keyCode >= 0x1e && keyCode <= 0x38) {
|
||||
return static_cast<unsigned char>((shift ? US_SHIFTED : US_NORMAL)[keyCode - 0x1e]);
|
||||
}
|
||||
switch (keyCode) {
|
||||
case 0x54: return '/';
|
||||
case 0x55: return '*';
|
||||
case 0x56: return '-';
|
||||
case 0x57: return '+';
|
||||
case 0x58: return '\n';
|
||||
default: break;
|
||||
}
|
||||
if (keyCode >= 0x59 && keyCode <= 0x63 && (ledState & KEYBOARD_LED_NUM_LOCK) != 0) {
|
||||
return static_cast<unsigned char>(KEYPAD_DIGITS[keyCode - 0x59]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Keyboard {
|
||||
|
||||
int Initialize() {
|
||||
std::lock_guard lock(keyboardMutex);
|
||||
initialized = true;
|
||||
return KEYBOARD_OK;
|
||||
}
|
||||
|
||||
int Open(int userId, std::int32_t type, std::int32_t index) {
|
||||
std::lock_guard lock(keyboardMutex);
|
||||
if (!initialized) return KEYBOARD_ERROR_NOT_INITIALIZED;
|
||||
if (userId < 0 || type != 0 || index != 0) return KEYBOARD_ERROR_INVALID_ARG;
|
||||
if (opened) return KEYBOARD_ERROR_ALREADY_OPENED;
|
||||
opened = true;
|
||||
clearKeys();
|
||||
head = 0;
|
||||
count = 0;
|
||||
enqueue(snapshot());
|
||||
return KEYBOARD_HANDLE;
|
||||
}
|
||||
|
||||
int Close(std::int32_t handle) {
|
||||
std::lock_guard lock(keyboardMutex);
|
||||
if (!initialized) return KEYBOARD_ERROR_NOT_INITIALIZED;
|
||||
if (handle != KEYBOARD_HANDLE || !opened) return KEYBOARD_ERROR_INVALID_HANDLE;
|
||||
opened = false;
|
||||
clearKeys();
|
||||
head = 0;
|
||||
count = 0;
|
||||
return KEYBOARD_OK;
|
||||
}
|
||||
|
||||
int Read(std::int32_t handle, KeyboardData* data, std::int32_t num) {
|
||||
std::lock_guard lock(keyboardMutex);
|
||||
if (!initialized) return KEYBOARD_ERROR_NOT_INITIALIZED;
|
||||
if (handle != KEYBOARD_HANDLE || !opened) return KEYBOARD_ERROR_INVALID_HANDLE;
|
||||
if (data == nullptr || num <= 0 || num > KEYBOARD_MAX_DATA_NUM) return KEYBOARD_ERROR_INVALID_ARG;
|
||||
const int available = std::min(count, num);
|
||||
for (int i = 0; i < available; ++i) {
|
||||
data[i] = history[head];
|
||||
head = (head + 1) % KEYBOARD_MAX_DATA_NUM;
|
||||
}
|
||||
count -= available;
|
||||
return available;
|
||||
}
|
||||
|
||||
int ReadState(std::int32_t handle, KeyboardData* data) {
|
||||
std::lock_guard lock(keyboardMutex);
|
||||
if (!initialized) return KEYBOARD_ERROR_NOT_INITIALIZED;
|
||||
if (handle != KEYBOARD_HANDLE || !opened) return KEYBOARD_ERROR_INVALID_HANDLE;
|
||||
if (data == nullptr) return KEYBOARD_ERROR_INVALID_ARG;
|
||||
*data = snapshot();
|
||||
return KEYBOARD_OK;
|
||||
}
|
||||
|
||||
int GetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint32_t ledState, std::uint32_t modifierKey, std::uint16_t keyCode, KeyboardCharData* charData) {
|
||||
{
|
||||
std::lock_guard lock(keyboardMutex);
|
||||
if (!initialized) return KEYBOARD_ERROR_NOT_INITIALIZED;
|
||||
if (handle != KEYBOARD_HANDLE || !opened) return KEYBOARD_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
if (charData == nullptr) return KEYBOARD_ERROR_INVALID_ARG;
|
||||
if (arrange == KEYBOARD_ARRANGEMENT_106) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return KEYBOARD_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (arrange != KEYBOARD_ARRANGEMENT_101) return KEYBOARD_ERROR_INVALID_ARG;
|
||||
*charData = {};
|
||||
charData->char_code = usCharacter(ledState, modifierKey, keyCode);
|
||||
charData->processed = charData->char_code != 0;
|
||||
charData->length = charData->processed ? 1 : 0;
|
||||
return KEYBOARD_OK;
|
||||
}
|
||||
|
||||
void Publish(const KeyboardInputEvent& event) {
|
||||
std::lock_guard lock(keyboardMutex);
|
||||
if (event.connectionChange) {
|
||||
if (connected == event.connected) return;
|
||||
connected = event.connected;
|
||||
if (!connected) clearKeys();
|
||||
} else if (!connected) {
|
||||
return;
|
||||
}
|
||||
if (!opened) return;
|
||||
if (event.resetKeys) clearKeys();
|
||||
if (event.keyCode != 0) {
|
||||
const bool ledChanged = led != event.led;
|
||||
led = event.led;
|
||||
if (!updateKey(event.keyCode, event.pressed) && !ledChanged) return;
|
||||
}
|
||||
enqueue(snapshot());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
#include "prx/libSceKeyboard/include/keyboard_structs.h"
|
||||
#include "prx/libSceKeyboard/include/KeyboardState.hpp"
|
||||
#include "prx/libSceVideoOut/include/KeyboardInput.hpp"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_keyboard.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceKeyboardInit(void);
|
||||
int APS5_VABI sceKeyboardOpen(int, std::int32_t, std::int32_t, const void*);
|
||||
int APS5_VABI sceKeyboardClose(std::int32_t);
|
||||
int APS5_VABI sceKeyboardRead(std::int32_t, KeyboardData*, std::int32_t);
|
||||
int APS5_VABI sceKeyboardReadState(std::int32_t, KeyboardData*);
|
||||
int APS5_VABI sceKeyboardGetKey2Char(std::int32_t, std::int32_t, std::uint32_t, std::uint32_t, std::uint16_t, KeyboardCharData*);
|
||||
}
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
static SDL_Event KeyEvent(Uint32 type, SDL_Scancode scancode, Uint16 mod = 0, Uint8 repeat = 0) {
|
||||
SDL_Event event{};
|
||||
event.type = type;
|
||||
event.key.windowID = 7;
|
||||
event.key.repeat = repeat;
|
||||
event.key.keysym.scancode = scancode;
|
||||
event.key.keysym.mod = mod;
|
||||
return event;
|
||||
}
|
||||
|
||||
static std::uint16_t Char(std::uint32_t led, std::uint32_t modifierKey, std::uint16_t keyCode) {
|
||||
KeyboardCharData data{};
|
||||
Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, led, modifierKey, keyCode, &data) == KEYBOARD_OK);
|
||||
Require(data.processed == (data.char_code != 0) && data.length == (data.processed ? 1 : 0));
|
||||
return data.char_code;
|
||||
}
|
||||
|
||||
int main() {
|
||||
KeyboardData data[16]{};
|
||||
KeyboardCharData charData{};
|
||||
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_ERROR_NOT_INITIALIZED);
|
||||
Require(sceKeyboardInit() == KEYBOARD_OK);
|
||||
Require(sceKeyboardInit() == KEYBOARD_OK);
|
||||
Require(sceKeyboardOpen(1, 0, 1, nullptr) == KEYBOARD_ERROR_INVALID_ARG);
|
||||
Require(sceKeyboardOpen(1, 1, 0, nullptr) == KEYBOARD_ERROR_INVALID_ARG);
|
||||
Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, 0, 0, 0x04, &charData) == KEYBOARD_ERROR_INVALID_HANDLE);
|
||||
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_HANDLE);
|
||||
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_ERROR_ALREADY_OPENED);
|
||||
Require(sceKeyboardRead(42, data, 1) == KEYBOARD_ERROR_INVALID_HANDLE);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, nullptr, 1) == KEYBOARD_ERROR_INVALID_ARG);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 17) == KEYBOARD_ERROR_INVALID_ARG);
|
||||
Require(sceKeyboardReadState(KEYBOARD_HANDLE, nullptr) == KEYBOARD_ERROR_INVALID_ARG);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1);
|
||||
Require(data[0].connected && data[0].length == 0 && data[0].modifier_key == 0);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 0);
|
||||
|
||||
KeyboardInput input;
|
||||
input.HandleEvent(KeyEvent(SDL_KEYDOWN, SDL_SCANCODE_A), 8);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 0);
|
||||
input.HandleEvent(KeyEvent(SDL_KEYDOWN, SDL_SCANCODE_LSHIFT, KMOD_NUM), 7);
|
||||
input.HandleEvent(KeyEvent(SDL_KEYDOWN, SDL_SCANCODE_A, KMOD_NUM | KMOD_LSHIFT), 7);
|
||||
input.HandleEvent(KeyEvent(SDL_KEYDOWN, SDL_SCANCODE_A, KMOD_NUM | KMOD_LSHIFT, 1), 7);
|
||||
input.HandleEvent(KeyEvent(SDL_KEYDOWN, SDL_SCANCODE_RETURN, KMOD_NUM | KMOD_LSHIFT), 7);
|
||||
input.HandleEvent(KeyEvent(SDL_KEYUP, SDL_SCANCODE_A, KMOD_NUM | KMOD_LSHIFT), 7);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 16) == 4);
|
||||
Require(data[0].length == 0 && data[0].modifier_key == 2 && data[0].led == KEYBOARD_LED_NUM_LOCK);
|
||||
Require(data[1].length == 1 && data[1].key_code[0] == 0x04);
|
||||
Require(data[2].length == 2 && data[2].key_code[0] == 0x04 && data[2].key_code[1] == 0x28);
|
||||
Require(data[3].length == 1 && data[3].key_code[0] == 0x28 && data[3].modifier_key == 2);
|
||||
Require(data[0].timestamp <= data[3].timestamp);
|
||||
Require(sceKeyboardReadState(KEYBOARD_HANDLE, data) == KEYBOARD_OK);
|
||||
Require(data[0].connected && data[0].length == 1 && data[0].key_code[0] == 0x28 && data[0].modifier_key == 2);
|
||||
|
||||
input.HandleEvent(KeyEvent(SDL_KEYDOWN, SDL_SCANCODE_CAPSLOCK, KMOD_NUM | KMOD_CAPS | KMOD_LSHIFT), 7);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1);
|
||||
Require(data[0].led == (KEYBOARD_LED_NUM_LOCK | KEYBOARD_LED_CAPS_LOCK));
|
||||
|
||||
SDL_Event window{};
|
||||
window.type = SDL_WINDOWEVENT;
|
||||
window.window.windowID = 7;
|
||||
window.window.event = SDL_WINDOWEVENT_FOCUS_LOST;
|
||||
input.HandleEvent(window, 7);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1);
|
||||
Require(data[0].connected && data[0].length == 0 && data[0].modifier_key == 0);
|
||||
input.HandleEvent(KeyEvent(SDL_KEYDOWN, SDL_SCANCODE_B), 7);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 0);
|
||||
window.window.event = SDL_WINDOWEVENT_FOCUS_GAINED;
|
||||
input.HandleEvent(window, 7);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 0);
|
||||
window.window.event = SDL_WINDOWEVENT_CLOSE;
|
||||
input.HandleEvent(window, 7);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && !data[0].connected);
|
||||
window.window.event = SDL_WINDOWEVENT_FOCUS_GAINED;
|
||||
input.HandleEvent(window, 7);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && data[0].connected);
|
||||
|
||||
for (std::uint16_t key = 0x04; key < 0x04 + 20; ++key) {
|
||||
KeyboardInputEvent press{};
|
||||
press.keyCode = key;
|
||||
press.pressed = true;
|
||||
KeyboardPublishInput_nid_postfix(press);
|
||||
}
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 16) == 16);
|
||||
Require(data[15].length == 16 && data[15].key_code[15] == 0x13);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 0);
|
||||
|
||||
Require(Char(0, 0, 0x04) == 'a' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x1d) == 'Z');
|
||||
Require(Char(KEYBOARD_LED_CAPS_LOCK, 0, 0x04) == 'A' && Char(KEYBOARD_LED_CAPS_LOCK, KEYBOARD_MOD_RIGHT_SHIFT, 0x04) == 'a');
|
||||
Require(Char(KEYBOARD_LED_CAPS_LOCK, 0, 0x1e) == '1' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x1e) == '!');
|
||||
Require(Char(0, 0, 0x27) == '0' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x27) == ')');
|
||||
Require(Char(0, 0, 0x28) == '\n' && Char(0, 0, 0x2a) == '\b' && Char(0, 0, 0x2b) == '\t' && Char(0, 0, 0x2c) == ' ');
|
||||
Require(Char(0, 0, 0x29) == 0 && Char(0, 0, 0x32) == 0 && Char(0, 0, 0x3a) == 0 && Char(0, 0, 0xe1) == 0);
|
||||
Require(Char(0, 0, 0x31) == '\\' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x31) == '|');
|
||||
Require(Char(0, 0, 0x34) == '\'' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x34) == '"');
|
||||
Require(Char(0, 0, 0x38) == '/' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x38) == '?');
|
||||
Require(Char(0, 0, 0x59) == 0 && Char(KEYBOARD_LED_NUM_LOCK, 0, 0x59) == '1');
|
||||
Require(Char(KEYBOARD_LED_NUM_LOCK, 0, 0x62) == '0' && Char(KEYBOARD_LED_NUM_LOCK, 0, 0x63) == '.');
|
||||
Require(Char(0, 0, 0x54) == '/' && Char(0, 0, 0x57) == '+' && Char(0, 0, 0x58) == '\n');
|
||||
Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, 0, 0, 0x04, nullptr) == KEYBOARD_ERROR_INVALID_ARG);
|
||||
Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, 2, 0, 0, 0x04, &charData) == KEYBOARD_ERROR_INVALID_ARG);
|
||||
|
||||
Require(sceKeyboardClose(KEYBOARD_HANDLE) == KEYBOARD_OK);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == KEYBOARD_ERROR_INVALID_HANDLE);
|
||||
Require(sceKeyboardReadState(KEYBOARD_HANDLE, data) == KEYBOARD_ERROR_INVALID_HANDLE);
|
||||
Require(sceKeyboardClose(KEYBOARD_HANDLE) == KEYBOARD_ERROR_INVALID_HANDLE);
|
||||
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_HANDLE);
|
||||
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && data[0].length == 0);
|
||||
Require(sceKeyboardClose(KEYBOARD_HANDLE) == KEYBOARD_OK);
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ add_library(libSceVideoOut SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/DisplayWindow.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/PadInput.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/MouseInput.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/KeyboardInput.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputMapping.cpp
|
||||
)
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ target_include_directories(libSceVideoOut PRIVATE
|
|||
${CMAKE_SOURCE_DIR}/3rdparty/SDL2/include
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/Vulkan-Headers/include
|
||||
)
|
||||
target_link_libraries(libSceVideoOut PRIVATE libSceAgcDriver libScePad libSceMouse libc libkernel SDL2-static)
|
||||
target_link_libraries(libSceVideoOut PRIVATE libSceAgcDriver libScePad libSceMouse libSceKeyboard libc libkernel SDL2-static)
|
||||
if(WIN32)
|
||||
target_link_libraries(libSceVideoOut PRIVATE comctl32)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEVIDEOOUT_KEYBOARDINPUT_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEVIDEOOUT_KEYBOARDINPUT_HPP
|
||||
|
||||
#include "SDL_events.h"
|
||||
|
||||
class KeyboardInput {
|
||||
public:
|
||||
void HandleEvent(const SDL_Event& event, unsigned windowId);
|
||||
|
||||
private:
|
||||
bool focused = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#include "prx/libSceVideoOut/include/KeyboardInput.hpp"
|
||||
#include "prx/libSceKeyboard/include/KeyboardState.hpp"
|
||||
|
||||
#include "SDL_keyboard.h"
|
||||
|
||||
void KeyboardInput::HandleEvent(const SDL_Event& event, unsigned windowId) {
|
||||
KeyboardInputEvent input{};
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
if (event.key.windowID != windowId || !focused || event.key.repeat != 0) return;
|
||||
if (event.key.keysym.scancode <= SDL_SCANCODE_UNKNOWN || event.key.keysym.scancode > SDL_SCANCODE_RGUI) return;
|
||||
input.keyCode = static_cast<std::uint16_t>(event.key.keysym.scancode);
|
||||
input.pressed = event.type == SDL_KEYDOWN;
|
||||
if ((event.key.keysym.mod & KMOD_NUM) != 0) input.led |= KEYBOARD_LED_NUM_LOCK;
|
||||
if ((event.key.keysym.mod & KMOD_CAPS) != 0) input.led |= KEYBOARD_LED_CAPS_LOCK;
|
||||
if ((event.key.keysym.mod & KMOD_SCROLL) != 0) input.led |= KEYBOARD_LED_SCROLL_LOCK;
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (event.window.windowID != windowId) return;
|
||||
if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
|
||||
focused = false;
|
||||
input.resetKeys = true;
|
||||
} else if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
|
||||
focused = true;
|
||||
input.connectionChange = true;
|
||||
} else if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
|
||||
focused = false;
|
||||
input.connectionChange = true;
|
||||
input.connected = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
KeyboardPublishInput_nid_postfix(input);
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include "SDL_vulkan.h"
|
||||
#include "prx/libSceVideoOut/include/PadInput.hpp"
|
||||
#include "prx/libSceVideoOut/include/MouseInput.hpp"
|
||||
#include "prx/libSceVideoOut/include/KeyboardInput.hpp"
|
||||
#include "prx/libScePad/include/PadState.hpp"
|
||||
#include "prx/libkernel/Equeue/Equeue.hpp"
|
||||
#include "prx/libkernel/Time/include/Time.hpp"
|
||||
|
|
@ -466,6 +467,7 @@ void VideoOutDriver::presentLoop(std::stop_token token) {
|
|||
try {
|
||||
PadInput padInput;
|
||||
MouseInput mouseInput;
|
||||
KeyboardInput keyboardInput;
|
||||
while (!token.stop_requested()) {
|
||||
{
|
||||
std::unique_lock lock(flipQueue->mutex);
|
||||
|
|
@ -485,7 +487,10 @@ void VideoOutDriver::presentLoop(std::stop_token token) {
|
|||
throw ProcessShutdown{};
|
||||
}
|
||||
padInput.HandleEvent(event, window);
|
||||
if (window.Handle() != nullptr) mouseInput.HandleEvent(event, SDL_GetWindowID(window.Handle()));
|
||||
if (window.Handle() != nullptr) {
|
||||
mouseInput.HandleEvent(event, SDL_GetWindowID(window.Handle()));
|
||||
keyboardInput.HandleEvent(event, SDL_GetWindowID(window.Handle()));
|
||||
}
|
||||
}
|
||||
padInput.Update();
|
||||
if (current) {
|
||||
|
|
|
|||
|
|
@ -42,4 +42,5 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- The Linux placement of the `--to-intel` stubs in the [ELF patcher](../../core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp) is covered only by a synthetic test.
|
||||
- The libc [SSE4a trap emulation](../../core/libs/prx/libc/src/specifics/windows/Sse4aEmulation.hpp) on Windows is superseded by `--to-intel` and remains only until the relinked title has been verified without it.
|
||||
- `--to-intel` guest module trampolines are covered only by a synthetic relinker test; no game title has been verified with them on Linux or Windows.
|
||||
- [sceKeyboardGetKey2Char](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) (libSceKeyboard) translates only the 101-key (US) arrangement and throws for the 106-key (Japanese) one; Ctrl and Alt do not change the character.
|
||||
- [libScePlayerInvitationDialog](../../core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp) simulates dialog completion without displaying UI or sending invitations; its parameter ABI remains unverified.
|
||||
|
|
|
|||
Loading…
Reference in New Issue