mirror of
https://github.com/EmiiBytee/np2kai-ps4.git
synced 2026-10-06 11:00:16 +02:00
280 lines
9.2 KiB
C++
280 lines
9.2 KiB
C++
// teclado usb, copiando el orden del ejemplo samples/keyboard de openorbis:
|
|
// sceSysmoduleLoadModule(0x0106)
|
|
// sceKeyboardInit()
|
|
// sceKeyboardOpen(usuario, 0, 0, nullptr)
|
|
// sceKeyboardReadState(handle, &data) <- una vez por frame
|
|
//
|
|
// cuidado con esto: ReadState no devuelve una cola de eventos, devuelve las
|
|
// teclas que estan pulsadas AHORA. asi que me guardo como estaban antes y
|
|
// comparo, y de ahi salen los "pulsada" y "soltada"
|
|
|
|
#include "ps4_keyboard.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include <orbis/Keyboard.h>
|
|
|
|
// lo declaro a mano en vez de incluir Sysmodule.h porque el nombre del enum
|
|
// cambia entre versiones de openorbis y se me rompia la compilacion
|
|
extern "C" int sceSysmoduleLoadModule(int moduleId);
|
|
static constexpr int PS4_SYSMODULE_KEYBOARD = 0x0106;
|
|
|
|
namespace {
|
|
|
|
// los usages caben en un byte. si llega algo mas grande es cosa rara del
|
|
// teclado y lo ignoro, no me fio de usarlo como indice
|
|
constexpr unsigned USAGE_TABLE_SIZE = 256;
|
|
|
|
bool s_sysmoduleLoaded = false;
|
|
bool s_libraryInitialized = false;
|
|
int s_handle = -1;
|
|
int s_userId = -1;
|
|
int s_sysmoduleResult = -1;
|
|
int s_initResult = -1;
|
|
int s_openResult = -1;
|
|
int s_lastReadResult = 0;
|
|
unsigned s_openAttempts = 0;
|
|
|
|
bool s_pressed[USAGE_TABLE_SIZE] {};
|
|
std::uint16_t s_modifiers = 0;
|
|
std::uint16_t s_locks = 0;
|
|
|
|
void resetPressedState()
|
|
{
|
|
std::memset(s_pressed, 0, sizeof(s_pressed));
|
|
s_modifiers = 0;
|
|
s_locks = 0;
|
|
}
|
|
|
|
// los modificadores vienen en data.mods, pero algunos teclados ademas los
|
|
// meten en la lista de teclas. junto las dos cosas para no contarlos dos veces
|
|
struct ModifierUsage {
|
|
std::uint16_t bit;
|
|
std::uint16_t usage;
|
|
};
|
|
|
|
constexpr ModifierUsage MODIFIER_USAGES[] {
|
|
{PS4_KBD_MOD_LEFT_CTRL, 0xE0},
|
|
{PS4_KBD_MOD_LEFT_SHIFT, 0xE1},
|
|
{PS4_KBD_MOD_LEFT_ALT, 0xE2},
|
|
{PS4_KBD_MOD_LEFT_META, 0xE3},
|
|
{PS4_KBD_MOD_RIGHT_CTRL, 0xE4},
|
|
{PS4_KBD_MOD_RIGHT_SHIFT, 0xE5},
|
|
{PS4_KBD_MOD_RIGHT_ALT, 0xE6},
|
|
{PS4_KBD_MOD_RIGHT_META, 0xE7}
|
|
};
|
|
|
|
// esto es el plan b por si sceKeyboardGetKey2Char no contesta. solo me importa
|
|
// el ascii: al pc-98 le llega el usage, el caracter es de adorno para libretro.
|
|
// la distribucion que asumo aqui es la de un teclado US, con otra saldran mal
|
|
// algunos simbolos
|
|
char asciiFallback(std::uint16_t usage, bool shift, bool caps)
|
|
{
|
|
if (usage >= 0x04 && usage <= 0x1D) {
|
|
const bool upper = shift != caps;
|
|
return static_cast<char>((upper ? 'A' : 'a') + (usage - 0x04));
|
|
}
|
|
if (usage >= 0x1E && usage <= 0x26) {
|
|
static const char shifted[] = "!@#$%^&*(";
|
|
return shift ? shifted[usage - 0x1E]
|
|
: static_cast<char>('1' + (usage - 0x1E));
|
|
}
|
|
|
|
switch (usage) {
|
|
case 0x27: return shift ? ')' : '0';
|
|
case 0x28: return '\n';
|
|
case 0x2A: return '\b';
|
|
case 0x2B: return '\t';
|
|
case 0x2C: return ' ';
|
|
case 0x2D: return shift ? '_' : '-';
|
|
case 0x2E: return shift ? '+' : '=';
|
|
case 0x2F: return shift ? '{' : '[';
|
|
case 0x30: return shift ? '}' : ']';
|
|
case 0x31: return shift ? '|' : '\\';
|
|
case 0x33: return shift ? ':' : ';';
|
|
case 0x34: return shift ? '"' : '\'';
|
|
case 0x35: return shift ? '~' : '`';
|
|
case 0x36: return shift ? '<' : ',';
|
|
case 0x37: return shift ? '>' : '.';
|
|
case 0x38: return shift ? '?' : '/';
|
|
case 0x54: return '/';
|
|
case 0x55: return '*';
|
|
case 0x56: return '-';
|
|
case 0x57: return '+';
|
|
case 0x58: return '\n';
|
|
case 0x63: return '.';
|
|
default: break;
|
|
}
|
|
|
|
if (usage >= 0x59 && usage <= 0x61) {
|
|
return static_cast<char>('1' + (usage - 0x59));
|
|
}
|
|
if (usage == 0x62) return '0';
|
|
return 0;
|
|
}
|
|
|
|
bool ensureSysmodule()
|
|
{
|
|
if (s_sysmoduleLoaded) return true;
|
|
s_sysmoduleResult = sceSysmoduleLoadModule(PS4_SYSMODULE_KEYBOARD);
|
|
// si ya estaba cargado devuelve algo distinto de cero que no es un error.
|
|
// no me fio de este valor, el que decide es el init de abajo
|
|
s_sysmoduleLoaded = true;
|
|
std::printf("[NP2kai/keyboard] sysmodule keyboard result=0x%x\n",
|
|
s_sysmoduleResult);
|
|
return true;
|
|
}
|
|
|
|
bool ensureLibrary()
|
|
{
|
|
if (s_libraryInitialized) return true;
|
|
s_initResult = sceKeyboardInit();
|
|
s_libraryInitialized = s_initResult == 0;
|
|
std::printf("[NP2kai/keyboard] sceKeyboardInit result=0x%x\n", s_initResult);
|
|
return s_libraryInitialized;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
extern "C" {
|
|
|
|
bool ps4KeyboardOpenDevice(int userId)
|
|
{
|
|
if (s_handle >= 0) return true;
|
|
if (userId < 0) return false;
|
|
|
|
++s_openAttempts;
|
|
s_userId = userId;
|
|
if (!ensureSysmodule()) return false;
|
|
if (!ensureLibrary()) return false;
|
|
|
|
s_openResult = sceKeyboardOpen(s_userId, 0, 0, nullptr);
|
|
if (s_openResult < 0) {
|
|
std::printf("[NP2kai/keyboard] sceKeyboardOpen failed result=0x%x "
|
|
"user=%d attempts=%u\n",
|
|
s_openResult, s_userId, s_openAttempts);
|
|
return false;
|
|
}
|
|
|
|
s_handle = s_openResult;
|
|
resetPressedState();
|
|
std::printf("[NP2kai/keyboard] device open handle=%d user=%d attempts=%u\n",
|
|
s_handle, s_userId, s_openAttempts);
|
|
return true;
|
|
}
|
|
|
|
void ps4KeyboardCloseDevice()
|
|
{
|
|
if (s_handle >= 0) {
|
|
sceKeyboardClose(s_handle);
|
|
std::printf("[NP2kai/keyboard] device closed handle=%d\n", s_handle);
|
|
}
|
|
s_handle = -1;
|
|
resetPressedState();
|
|
}
|
|
|
|
bool ps4KeyboardIsOpen()
|
|
{
|
|
return s_handle >= 0;
|
|
}
|
|
|
|
unsigned ps4KeyboardPoll(Ps4KeyboardEvent* events, unsigned maxEvents)
|
|
{
|
|
if (events == nullptr || maxEvents == 0 || s_handle < 0) return 0;
|
|
|
|
OrbisKeyboardData data {};
|
|
s_lastReadResult = sceKeyboardReadState(s_handle, &data);
|
|
if (s_lastReadResult < 0) {
|
|
// o lo han desenchufado o el handle ya no vale. suelto todas las teclas
|
|
// que estaban pulsadas o el pc-98 se queda con una tecla fantasma
|
|
// apretada para siempre (me paso, y es de lo mas molesto)
|
|
unsigned produced = 0;
|
|
for (unsigned usage = 0; usage < USAGE_TABLE_SIZE && produced < maxEvents;
|
|
++usage) {
|
|
if (!s_pressed[usage]) continue;
|
|
events[produced].usage = static_cast<std::uint16_t>(usage);
|
|
events[produced].modifiers = s_modifiers;
|
|
events[produced].locks = s_locks;
|
|
events[produced].down = false;
|
|
++produced;
|
|
}
|
|
ps4KeyboardCloseDevice();
|
|
std::printf("[NP2kai/keyboard] read failed result=0x%x, handle released\n",
|
|
s_lastReadResult);
|
|
return produced;
|
|
}
|
|
|
|
bool current[USAGE_TABLE_SIZE] {};
|
|
|
|
int reportedKeys = data.nkeys;
|
|
if (reportedKeys < 0) reportedKeys = 0;
|
|
const int maxKeys =
|
|
static_cast<int>(sizeof(data.keycodes) / sizeof(data.keycodes[0]));
|
|
if (reportedKeys > maxKeys) reportedKeys = maxKeys;
|
|
|
|
for (int index = 0; index < reportedKeys; ++index) {
|
|
const std::uint16_t usage = data.keycodes[index];
|
|
if (usage == 0 || usage >= USAGE_TABLE_SIZE) continue;
|
|
current[usage] = true;
|
|
}
|
|
|
|
const std::uint16_t modifiers = static_cast<std::uint16_t>(data.mods);
|
|
const std::uint16_t locks = static_cast<std::uint16_t>(data.locks);
|
|
for (const ModifierUsage& entry : MODIFIER_USAGES) {
|
|
if ((modifiers & entry.bit) != 0) current[entry.usage] = true;
|
|
}
|
|
|
|
unsigned produced = 0;
|
|
for (unsigned usage = 0; usage < USAGE_TABLE_SIZE; ++usage) {
|
|
if (current[usage] == s_pressed[usage]) continue;
|
|
if (produced < maxEvents) {
|
|
events[produced].usage = static_cast<std::uint16_t>(usage);
|
|
events[produced].modifiers = modifiers;
|
|
events[produced].locks = locks;
|
|
events[produced].down = current[usage];
|
|
++produced;
|
|
s_pressed[usage] = current[usage];
|
|
}
|
|
// si se llena el buffer, lo que sobra queda pendiente para la siguiente
|
|
// vuelta: solo doy por hecha la tecla que he llegado a entregar
|
|
}
|
|
|
|
s_modifiers = modifiers;
|
|
s_locks = locks;
|
|
return produced;
|
|
}
|
|
|
|
char ps4KeyboardUsageToCharacter(std::uint16_t usage,
|
|
std::uint16_t modifiers,
|
|
std::uint16_t locks)
|
|
{
|
|
if (s_handle >= 0) {
|
|
OrbisKeyboardKey2Char translated {};
|
|
const int result = sceKeyboardGetKey2Char(
|
|
s_handle, false, static_cast<int32_t>(locks),
|
|
static_cast<int32_t>(modifiers), static_cast<int32_t>(usage),
|
|
&translated);
|
|
if (result >= 0 && translated.ok != 0) {
|
|
const int character = translated.keycode;
|
|
if (character > 0 && character < 0x80) {
|
|
return static_cast<char>(character);
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool shift =
|
|
(modifiers & (PS4_KBD_MOD_LEFT_SHIFT | PS4_KBD_MOD_RIGHT_SHIFT)) != 0;
|
|
const bool caps = (locks & PS4_KBD_LOCK_CAPS) != 0;
|
|
return asciiFallback(usage, shift, caps);
|
|
}
|
|
|
|
int ps4KeyboardSysmoduleResult() { return s_sysmoduleResult; }
|
|
int ps4KeyboardInitResult() { return s_initResult; }
|
|
int ps4KeyboardOpenResult() { return s_openResult; }
|
|
int ps4KeyboardLastReadResult() { return s_lastReadResult; }
|
|
unsigned ps4KeyboardOpenAttempts() { return s_openAttempts; }
|
|
int ps4KeyboardUserId() { return s_userId; }
|
|
|
|
} // extern "C"
|