mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2025-12-25 09:22:37 +00:00
179 lines
4.8 KiB
C++
179 lines
4.8 KiB
C++
#include <cassert>
|
|
|
|
#include "ultramodern/input.hpp"
|
|
#include "ultramodern/ultra64.h"
|
|
#include "ultramodern/ultramodern.hpp"
|
|
|
|
static ultramodern::input::callbacks_t input_callbacks {};
|
|
|
|
void ultramodern::input::set_callbacks(const callbacks_t& callbacks) {
|
|
input_callbacks = callbacks;
|
|
}
|
|
|
|
static std::chrono::high_resolution_clock::time_point input_poll_time;
|
|
|
|
static void update_poll_time() {
|
|
input_poll_time = std::chrono::high_resolution_clock::now();
|
|
}
|
|
|
|
void ultramodern::measure_input_latency() {
|
|
#if 0
|
|
printf("Delta: %ld micros\n", std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - input_poll_time));
|
|
#endif
|
|
}
|
|
|
|
#define MAXCONTROLLERS 4
|
|
|
|
#define CONT_NO_RESPONSE_ERROR 0x8
|
|
|
|
#define CONT_TYPE_NORMAL 0x0005
|
|
#define CONT_TYPE_MOUSE 0x0002
|
|
#define CONT_TYPE_VOICE 0x0100
|
|
|
|
static int max_controllers = 0;
|
|
|
|
/* Plain controller */
|
|
|
|
static u16 get_controller_type(ultramodern::input::Device device_type) {
|
|
switch (device_type) {
|
|
case ultramodern::input::Device::None:
|
|
return 0;
|
|
|
|
case ultramodern::input::Device::Controller:
|
|
return CONT_TYPE_NORMAL;
|
|
|
|
#if 0
|
|
case ultramodern::input::Device::Mouse:
|
|
return CONT_TYPE_MOUSE;
|
|
|
|
case ultramodern::input::Device::VRU:
|
|
return CONT_TYPE_VOICE;
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __osContGetInitData(u8* pattern, OSContStatus *data) {
|
|
*pattern = 0x00;
|
|
|
|
for (int controller = 0; controller < max_controllers; controller++) {
|
|
ultramodern::input::connected_device_info_t device_info{};
|
|
|
|
if (input_callbacks.get_connected_device_info != nullptr) {
|
|
device_info = input_callbacks.get_connected_device_info(controller);
|
|
}
|
|
|
|
if (device_info.connected_device != ultramodern::input::Device::None) {
|
|
// Mark controller as present
|
|
|
|
data[controller].type = get_controller_type(device_info.connected_device);
|
|
data[controller].status = device_info.connected_pak != ultramodern::input::Pak::None;
|
|
data[controller].err_no = 0x00;
|
|
|
|
*pattern = 1 << controller;
|
|
}
|
|
else {
|
|
// Mark controller as not connected
|
|
|
|
// Libultra doesn't write status or type for absent controllers
|
|
data[controller].err_no = CONT_NO_RESPONSE_ERROR; // CHNL_ERR_NORESP >> 4
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" s32 osContInit(RDRAM_ARG PTR(OSMesgQueue) mq, u8* bitpattern, PTR(OSContStatus) data_) {
|
|
OSContStatus *data = TO_PTR(OSContStatus, data_);
|
|
|
|
max_controllers = MAXCONTROLLERS;
|
|
|
|
__osContGetInitData(bitpattern, data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern "C" s32 osContReset(RDRAM_ARG PTR(OSMesgQueue) mq, PTR(OSContStatus) data) {
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" s32 osContStartQuery(RDRAM_ARG PTR(OSMesgQueue) mq) {
|
|
ultramodern::send_si_message(PASS_RDRAM1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern "C" s32 osContStartReadData(RDRAM_ARG PTR(OSMesgQueue) mq) {
|
|
if (input_callbacks.poll_input != nullptr) {
|
|
input_callbacks.poll_input();
|
|
}
|
|
update_poll_time();
|
|
|
|
ultramodern::send_si_message(rdram);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern "C" s32 osContSetCh(RDRAM_ARG u8 ch) {
|
|
max_controllers = std::min(ch, u8(MAXCONTROLLERS));
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern "C" void osContGetQuery(RDRAM_ARG PTR(OSContStatus) data_) {
|
|
OSContStatus *data = TO_PTR(OSContStatus, data_);
|
|
u8 pattern;
|
|
|
|
__osContGetInitData(&pattern, data);
|
|
}
|
|
|
|
extern "C" void osContGetReadData(OSContPad *data) {
|
|
for (int controller = 0; controller < max_controllers; controller++) {
|
|
uint16_t buttons = 0;
|
|
float x = 0.0f;
|
|
float y = 0.0f;
|
|
bool got_response = false;
|
|
|
|
if (input_callbacks.get_input != nullptr) {
|
|
got_response = input_callbacks.get_input(controller, &buttons, &x, &y);
|
|
}
|
|
|
|
if (got_response) {
|
|
data[controller].button = buttons;
|
|
data[controller].stick_x = (int8_t)(127 * x);
|
|
data[controller].stick_y = (int8_t)(127 * y);
|
|
data[controller].err_no = 0;
|
|
} else {
|
|
data[controller].err_no = CONT_NO_RESPONSE_ERROR; // CHNL_ERR_NORESP >> 4
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Rumble */
|
|
|
|
s32 osMotorInit(RDRAM_ARG PTR(OSMesgQueue) mq, PTR(OSPfs) pfs_, int channel) {
|
|
OSPfs *pfs = TO_PTR(OSPfs, pfs_);
|
|
|
|
pfs->channel = channel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 osMotorStop(RDRAM_ARG PTR(OSPfs) pfs) {
|
|
return __osMotorAccess(PASS_RDRAM pfs, false);
|
|
}
|
|
|
|
s32 osMotorStart(RDRAM_ARG PTR(OSPfs) pfs) {
|
|
return __osMotorAccess(PASS_RDRAM pfs, true);
|
|
}
|
|
|
|
s32 __osMotorAccess(RDRAM_ARG PTR(OSPfs) pfs_, s32 flag) {
|
|
OSPfs *pfs = TO_PTR(OSPfs, pfs_);
|
|
|
|
if (input_callbacks.set_rumble != nullptr) {
|
|
// TODO: Should we check if the Rumble Pak is connected? Or just rumble regardless of the connected Pak?
|
|
input_callbacks.set_rumble(pfs->channel, flag);
|
|
}
|
|
|
|
return 0;
|
|
}
|