2016-02-13 17:29:56 +01:00
|
|
|
#include "hid.h"
|
2016-10-17 23:45:22 +02:00
|
|
|
#include "i2c.h"
|
2016-05-30 01:54:09 +02:00
|
|
|
#include "timer.h"
|
2019-05-01 02:38:31 +02:00
|
|
|
#include "colors.h"
|
2018-03-07 01:15:12 +01:00
|
|
|
#include "screenshot.h" // for screenshots
|
2016-02-13 17:29:56 +01:00
|
|
|
|
2019-04-17 11:58:54 -03:00
|
|
|
#include "arm.h"
|
2019-04-19 12:15:43 -03:00
|
|
|
#include "fixp.h"
|
2019-04-18 16:39:45 -03:00
|
|
|
#include "shmem.h"
|
2019-04-17 11:58:54 -03:00
|
|
|
|
2019-04-19 12:15:43 -03:00
|
|
|
#define HID_TOUCH_MAXPOINT (0x1000)
|
|
|
|
#define HID_TOUCH_MIDPOINT (HID_TOUCH_MAXPOINT / 2)
|
|
|
|
|
2019-04-30 01:52:32 +02:00
|
|
|
|
2019-10-05 20:00:14 +02:00
|
|
|
static void SetNotificationLED(u32 period_ms, u32 rgb565_color)
|
2019-05-01 02:38:31 +02:00
|
|
|
{
|
2019-10-05 20:00:14 +02:00
|
|
|
u32 rgb888_color =
|
|
|
|
((rgb565_color >> 11) << (16+3) |
|
|
|
|
(rgb565_color >> 5) << (8+2) |
|
|
|
|
(rgb565_color << 3));
|
|
|
|
u32 args[] = {period_ms, rgb888_color};
|
2019-05-01 02:38:31 +02:00
|
|
|
PXI_DoCMD(PXI_NOTIFY_LED, args, 2);
|
|
|
|
}
|
|
|
|
|
2019-04-18 16:39:45 -03:00
|
|
|
// there's some weird thing going on when reading this
|
|
|
|
// with an LDRD instruction so for now they'll be two
|
|
|
|
// separate things - hopefully LTO won't get in the way
|
2019-04-17 11:58:54 -03:00
|
|
|
u32 HID_ReadState(void)
|
2019-04-18 16:39:45 -03:00
|
|
|
{
|
|
|
|
return ARM_GetSHMEM()->hid_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 HID_ReadRawTouchState(void)
|
|
|
|
{
|
|
|
|
return ARM_GetSHMEM()->hid_state >> 32;
|
2019-04-17 11:58:54 -03:00
|
|
|
}
|
|
|
|
|
2019-04-22 16:24:45 -03:00
|
|
|
// ts_mult indicates a scalar for each axis
|
|
|
|
// if |ts_mult| > 1 => point must be "scaled out"
|
|
|
|
// if |ts_mult| < 1 => point must be "scaled in"
|
|
|
|
// if ts_mult < 0 => axis is inverted
|
|
|
|
static fixp_t ts_mult[2];
|
|
|
|
|
|
|
|
// ts_org indicates the coordinate system origin
|
2019-04-22 18:44:48 -03:00
|
|
|
static int ts_org[2];
|
2019-05-24 09:56:35 -03:00
|
|
|
|
|
|
|
bool HID_TouchCalibratedTransform(u32 ts, u16 *x, u16 *y)
|
2019-04-19 12:15:43 -03:00
|
|
|
{
|
2019-04-22 18:44:48 -03:00
|
|
|
int xc, yc;
|
2019-05-24 09:56:35 -03:00
|
|
|
int tx, ty;
|
2019-04-19 12:15:43 -03:00
|
|
|
|
2019-04-29 19:20:39 -03:00
|
|
|
if (ts & BIT(31))
|
|
|
|
return false;
|
|
|
|
|
2019-05-24 09:56:35 -03:00
|
|
|
tx = HID_RAW_TX(ts) - HID_TOUCH_MIDPOINT;
|
|
|
|
ty = HID_RAW_TY(ts) - HID_TOUCH_MIDPOINT;
|
2019-04-19 12:15:43 -03:00
|
|
|
|
2019-05-24 09:56:35 -03:00
|
|
|
xc = FIXP_TO_INT(fixp_round(tx * ts_mult[0])) + ts_org[0];
|
|
|
|
yc = FIXP_TO_INT(fixp_round(ty * ts_mult[1])) + ts_org[1];
|
2019-04-22 18:44:48 -03:00
|
|
|
|
|
|
|
*x = clamp(xc, 0, (ts_org[0] * 2) - 1);
|
|
|
|
*y = clamp(yc, 0, (ts_org[1] * 2) - 1);
|
2019-04-29 19:20:39 -03:00
|
|
|
return true;
|
2019-04-19 12:15:43 -03:00
|
|
|
}
|
|
|
|
|
2019-05-24 09:56:35 -03:00
|
|
|
bool HID_ReadTouchState(u16 *x, u16 *y)
|
2019-04-19 12:15:43 -03:00
|
|
|
{
|
2019-05-24 09:56:35 -03:00
|
|
|
return HID_TouchCalibratedTransform(HID_ReadRawTouchState(), x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HID_SetCalibrationData(const HID_CalibrationData *calibs, u32 point_cnt, u32 screen_w, u32 screen_h)
|
|
|
|
{
|
|
|
|
u32 mid_x, mid_y;
|
2019-04-19 12:15:43 -03:00
|
|
|
fixp_t avg_x, avg_y;
|
|
|
|
|
2019-04-22 16:24:45 -03:00
|
|
|
if (!screen_w || !screen_h || point_cnt <= 0 || point_cnt > 7)
|
2019-04-19 12:15:43 -03:00
|
|
|
return false;
|
|
|
|
|
2019-04-22 16:24:45 -03:00
|
|
|
mid_x = screen_w / 2;
|
|
|
|
mid_y = screen_h / 2;
|
2019-04-19 12:15:43 -03:00
|
|
|
|
|
|
|
avg_x = 0;
|
|
|
|
avg_y = 0;
|
2019-05-24 09:56:35 -03:00
|
|
|
for (u32 i = 0; i < point_cnt; i++) {
|
2019-04-19 12:15:43 -03:00
|
|
|
const HID_CalibrationData *data = &calibs[i];
|
|
|
|
fixp_t screen_x, screen_y, touch_x, touch_y;
|
|
|
|
|
|
|
|
// translate the [0, screen_w] x [0, screen_h] system
|
|
|
|
// to [-screen_w/2, screen_w/2] x [-screen_h/2, screen_h/2]
|
2019-04-22 16:24:45 -03:00
|
|
|
screen_x = INT_TO_FIXP(data->screen_x - mid_x);
|
|
|
|
screen_y = INT_TO_FIXP(data->screen_y - mid_y);
|
2019-04-19 12:15:43 -03:00
|
|
|
|
|
|
|
// same thing for raw touchscreen data
|
|
|
|
touch_x = INT_TO_FIXP(HID_RAW_TX(data->ts_raw) - HID_TOUCH_MIDPOINT);
|
|
|
|
touch_y = INT_TO_FIXP(HID_RAW_TY(data->ts_raw) - HID_TOUCH_MIDPOINT);
|
|
|
|
|
|
|
|
// if the data retrieved came right in the middle, it's invalid
|
|
|
|
if (!screen_x || !screen_y || !touch_x || !touch_y)
|
|
|
|
return false;
|
|
|
|
|
2019-04-22 16:24:45 -03:00
|
|
|
// prevent integer overflows by dividing in this step
|
|
|
|
avg_x += fixp_quotient(screen_x, touch_x * point_cnt);
|
|
|
|
avg_y += fixp_quotient(screen_y, touch_y * point_cnt);
|
2019-04-19 12:15:43 -03:00
|
|
|
}
|
|
|
|
|
2019-04-22 16:24:45 -03:00
|
|
|
// set state variables
|
|
|
|
ts_mult[0] = avg_x;
|
|
|
|
ts_mult[1] = avg_y;
|
|
|
|
ts_org[0] = mid_x;
|
|
|
|
ts_org[1] = mid_y;
|
2019-04-19 12:15:43 -03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-08 23:15:00 +02:00
|
|
|
const TouchBox* TouchBoxGet(u32* id, const u16 x, const u16 y, const TouchBox* tbs, const u32 tbn) {
|
2019-05-08 23:14:11 +02:00
|
|
|
// check if inside touchbox
|
|
|
|
for (u32 i = 0; !tbn || (i < tbn); i++) {
|
2019-05-03 01:37:41 +02:00
|
|
|
const TouchBox* tb = tbs + i;
|
2019-05-08 23:14:11 +02:00
|
|
|
if (tb->id == 0) break;
|
|
|
|
if ((x >= tb->x) && (y >= tb->y) &&
|
|
|
|
(x < tb->x + tb->w) && (y < tb->y + tb->h)) {
|
|
|
|
if (id) *id = tb->id;
|
2019-05-08 23:15:00 +02:00
|
|
|
return tb; // we know const is discarded here
|
2019-05-03 01:37:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-08 23:14:11 +02:00
|
|
|
if (id) *id = 0;
|
|
|
|
return NULL;
|
2019-05-03 01:37:41 +02:00
|
|
|
}
|
|
|
|
|
2017-08-04 02:08:56 +02:00
|
|
|
u32 InputWait(u32 timeout_sec) {
|
2016-05-30 03:03:40 +02:00
|
|
|
static u64 delay = 0;
|
2017-08-04 02:08:56 +02:00
|
|
|
u64 timer = timer_start();
|
2019-04-17 11:58:54 -03:00
|
|
|
|
|
|
|
u32 oldpad = HID_ReadState();
|
|
|
|
u32 oldcart = CART_STATE;
|
|
|
|
u32 oldsd = SD_STATE;
|
|
|
|
|
2019-05-01 02:38:31 +02:00
|
|
|
// enable notification LED if shell is closed
|
|
|
|
// (this means we're waiting for user input)
|
|
|
|
if (oldpad & SHELL_CLOSED) {
|
|
|
|
SetNotificationLED(1000, COLOR_GREEN);
|
|
|
|
while (HID_ReadState() & SHELL_CLOSED);
|
|
|
|
}
|
|
|
|
|
2019-05-01 03:22:37 +02:00
|
|
|
delay = delay ? 144 : 256;
|
2019-04-17 11:58:54 -03:00
|
|
|
|
|
|
|
do {
|
|
|
|
u32 newpad = HID_ReadState();
|
|
|
|
|
2019-10-18 18:58:05 +02:00
|
|
|
// handle closed shell (wait for open)
|
|
|
|
if (newpad & SHELL_CLOSED) {
|
|
|
|
while (HID_ReadState() & SHELL_CLOSED);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// no buttons pressed, check for I/O changes instead
|
|
|
|
if (!(newpad & ~(SHELL_OPEN|SHELL_CLOSED))) {
|
2019-04-17 11:58:54 -03:00
|
|
|
u32 state = CART_STATE;
|
|
|
|
if (state != oldcart)
|
|
|
|
return state ? CART_INSERT : CART_EJECT;
|
|
|
|
|
|
|
|
state = SD_STATE;
|
|
|
|
if (state != oldsd)
|
|
|
|
return state ? SD_INSERT : SD_EJECT;
|
|
|
|
|
|
|
|
oldpad = 0;
|
2016-05-30 03:03:40 +02:00
|
|
|
delay = 0;
|
2016-04-05 23:19:29 +02:00
|
|
|
continue;
|
|
|
|
}
|
2019-04-17 11:58:54 -03:00
|
|
|
|
|
|
|
// special case for dpad keys
|
|
|
|
// if any of those are held, don't wait for key changes
|
|
|
|
// but do insert a small latency to make
|
|
|
|
// sure any menus don't go flying off
|
|
|
|
if ((newpad == oldpad) &&
|
|
|
|
(!(newpad & BUTTON_ARROW) ||
|
2017-08-04 02:08:56 +02:00
|
|
|
(delay && (timer_msec(timer) < delay))))
|
2016-04-05 23:19:29 +02:00
|
|
|
continue;
|
2019-04-17 11:58:54 -03:00
|
|
|
|
2019-05-01 03:22:37 +02:00
|
|
|
// screenshot handling
|
|
|
|
if ((newpad & BUTTON_ANY) == (BUTTON_R1 | BUTTON_L1))
|
|
|
|
CreateScreenshot();
|
|
|
|
|
|
|
|
return newpad;
|
2019-04-17 11:58:54 -03:00
|
|
|
} while (!timeout_sec || (timeout_sec && (timer_sec(timer) < timeout_sec)));
|
|
|
|
|
|
|
|
return TIMEOUT_HID;
|
2016-02-13 17:29:56 +01:00
|
|
|
}
|
2016-10-17 23:45:22 +02:00
|
|
|
|
|
|
|
bool CheckButton(u32 button) {
|
2019-04-17 11:58:54 -03:00
|
|
|
return (HID_ReadState() & button) == button;
|
2016-10-17 23:45:22 +02:00
|
|
|
}
|
2018-04-25 15:25:07 +02:00
|
|
|
|
|
|
|
void ButtonToString(u32 button, char* str) {
|
|
|
|
const char* strings[] = { BUTTON_STRINGS };
|
|
|
|
|
|
|
|
*str = '\0';
|
|
|
|
if (button) {
|
|
|
|
u32 b = 0;
|
|
|
|
for (b = 0; !((button>>b)&0x1); b++);
|
|
|
|
if (b < countof(strings)) strcpy(str, strings[b]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 StringToButton(char* str) {
|
|
|
|
const char* strings[] = { BUTTON_STRINGS };
|
|
|
|
|
|
|
|
u32 b = 0;
|
|
|
|
for (b = 0; b < countof(strings); b++)
|
|
|
|
if (strcmp(str, strings[b]) == 0) break;
|
|
|
|
|
|
|
|
return (b == countof(strings)) ? 0 : 1<<b;
|
|
|
|
}
|