2024-06-29 20:17:34 +00:00
|
|
|
#include "tasoller.h"
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
uint16_t u16RequestedConsumerControl = 0;
|
|
|
|
uint32_t u32EnterPressStarted = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
#define ENTER_HOLD_TIME 5000 // 5 seconds
|
|
|
|
|
2024-06-29 20:17:34 +00:00
|
|
|
#define HID_MISC_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_MISC_IN)))
|
2024-08-04 21:27:46 +00:00
|
|
|
#define HID_IO4_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_IO4_IN)))
|
|
|
|
|
|
|
|
#define HID_MISC_SEND(buf) \
|
|
|
|
do { \
|
|
|
|
USBD_SET_PAYLOAD_LEN(EP_HID_MISC_IN, sizeof *(buf)); \
|
|
|
|
gu8HIDMiscReady = 0; \
|
|
|
|
} while (0)
|
2024-06-29 20:17:34 +00:00
|
|
|
|
|
|
|
static const uint8_t u8GroundKeymap[32] = {
|
2024-08-04 21:27:46 +00:00
|
|
|
KEY_I, KEY_9, // Dao uses: KEY_I, KEY_COMMA
|
|
|
|
KEY_8, KEY_K, //
|
|
|
|
KEY_U, KEY_M, //
|
|
|
|
KEY_7, KEY_J, //
|
|
|
|
KEY_Y, KEY_N, //
|
|
|
|
KEY_6, KEY_H, //
|
|
|
|
KEY_T, KEY_B, //
|
|
|
|
KEY_5, KEY_G, //
|
|
|
|
KEY_R, KEY_V, //
|
|
|
|
KEY_4, KEY_F, //
|
|
|
|
KEY_E, KEY_C, //
|
|
|
|
KEY_3, KEY_D, //
|
|
|
|
KEY_W, KEY_X, //
|
|
|
|
KEY_2, KEY_S, //
|
|
|
|
KEY_Q, KEY_Z, //
|
|
|
|
KEY_1, KEY_A, //
|
2024-06-29 20:17:34 +00:00
|
|
|
};
|
|
|
|
static const uint8_t u8AirKeymap[6] = {
|
2024-08-04 21:27:46 +00:00
|
|
|
// Dao mapping:
|
|
|
|
// HID_KEYBOARD_SLASH_AND_QUESTION_MARK, // VK_OEM_2
|
|
|
|
// HID_KEYBOARD_PERIOD_AND_GREATER_THAN, // VK_OEM_PERIOD
|
|
|
|
// HID_KEYBOARD_QUOTE_AND_DOUBLEQUOTE, // VK_OEM_7
|
|
|
|
// HID_KEYBOARD_SEMICOLON_AND_COLON, // VK_OEM_1
|
|
|
|
// HID_KEYBOARD_RIGHT_BRACKET_AND_RIGHT_CURLY_BRACE, // VK_OEM_6
|
|
|
|
// HID_KEYBOARD_LEFT_BRACKET_AND_LEFT_CURLY_BRACE, // VK_OEM_4
|
|
|
|
|
|
|
|
// UMIGURI mapping:
|
|
|
|
KEY_0, KEY_O, KEY_L, KEY_P, KEY_COMMA, KEY_PERIOD,
|
2024-06-29 20:17:34 +00:00
|
|
|
};
|
2024-08-04 21:27:46 +00:00
|
|
|
|
|
|
|
static uint8_t _HID_Keyboard_Tick(uint8_t bReleaseAll) {
|
|
|
|
hid_kbd_report_t *buf = (hid_kbd_report_t *)HID_MISC_BUF;
|
|
|
|
|
|
|
|
// Send a report of zeroes to release all keys
|
|
|
|
if (bReleaseAll) {
|
|
|
|
memset(buf, 0, sizeof *buf);
|
|
|
|
buf->bReportId = HID_REPORT_ID_KEYBOARD;
|
|
|
|
|
|
|
|
HID_MISC_SEND(buf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t u8LastButtons = 0;
|
|
|
|
static uint32_t u32LastSlider = 0;
|
|
|
|
|
|
|
|
// If nothing changed, do nothing
|
|
|
|
if (gu8DigitalButtons == u8LastButtons && gu32PSoCDigital == u32LastSlider) {
|
|
|
|
return 0;
|
|
|
|
}
|
2024-06-29 20:17:34 +00:00
|
|
|
|
|
|
|
memset(buf, 0, sizeof *buf);
|
|
|
|
buf->bReportId = HID_REPORT_ID_KEYBOARD;
|
|
|
|
uint8_t kI = 0;
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
if (gu8DigitalButtons & DIGITAL_FN2_Msk) buf->bKeyboard[kI++] = HID_KEYBOARD_F1;
|
|
|
|
if (gu8DigitalButtons & DIGITAL_FN1_Msk) buf->bKeyboard[kI++] = HID_KEYBOARD_F2;
|
|
|
|
if (gu8DigitalButtons & 0x04) buf->bKeyboard[kI++] = u8AirKeymap[0];
|
|
|
|
if (gu8DigitalButtons & 0x08) buf->bKeyboard[kI++] = u8AirKeymap[1];
|
|
|
|
if (gu8DigitalButtons & 0x10) buf->bKeyboard[kI++] = u8AirKeymap[2];
|
|
|
|
if (gu8DigitalButtons & 0x20) buf->bKeyboard[kI++] = u8AirKeymap[3];
|
|
|
|
if (gu8DigitalButtons & 0x40) buf->bKeyboard[kI++] = u8AirKeymap[4];
|
|
|
|
if (gu8DigitalButtons & 0x80) buf->bKeyboard[kI++] = u8AirKeymap[5];
|
|
|
|
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
|
|
if (gu32PSoCDigital & (1 << i)) buf->bKeyboard[kI++] = u8GroundKeymap[i];
|
2024-06-29 20:17:34 +00:00
|
|
|
}
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
HID_MISC_SEND(buf);
|
|
|
|
u8LastButtons = gu8DigitalButtons;
|
|
|
|
u32LastSlider = gu32PSoCDigital;
|
|
|
|
return 1;
|
2024-06-29 20:17:34 +00:00
|
|
|
}
|
2024-08-04 21:27:46 +00:00
|
|
|
static uint8_t _HID_Consumer_Tick(void) {
|
|
|
|
static uint16_t u16Last = 0;
|
|
|
|
if (u16Last == u16RequestedConsumerControl) return 0;
|
|
|
|
u16Last = u16RequestedConsumerControl;
|
2024-06-29 20:17:34 +00:00
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
hid_consumer_report_t *buf = (hid_consumer_report_t *)HID_MISC_BUF;
|
2024-06-29 20:17:34 +00:00
|
|
|
|
|
|
|
memset(buf, 0, sizeof *buf);
|
2024-08-04 21:27:46 +00:00
|
|
|
buf->bReportId = HID_REPORT_ID_CONSUMER_CONTROL;
|
|
|
|
buf->u16Control[0] = u16RequestedConsumerControl;
|
|
|
|
HID_MISC_SEND(buf);
|
|
|
|
return 1;
|
2024-06-29 20:17:34 +00:00
|
|
|
}
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
static uint8_t _HID_Enter_Tick(void) {
|
|
|
|
// TODO: This isn't working, so we're using su8LastState for now instead
|
|
|
|
if (u32EnterPressStarted == 0xFFFFFFFF) return 0;
|
2024-06-29 20:17:34 +00:00
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
static uint8_t su8LastState = 0;
|
|
|
|
uint8_t u8State = 0;
|
|
|
|
|
|
|
|
// There's an _incredibly_ small chance the user tapped the cell at exactly the ms (49 days in!)
|
|
|
|
// when the timer wrapped round to 0. This is too stupid to account for.
|
|
|
|
if (u32EnterPressStarted && (MS_SINCE(u32EnterPressStarted) < ENTER_HOLD_TIME)) {
|
|
|
|
u8State = 1;
|
2024-06-29 20:17:34 +00:00
|
|
|
} else {
|
2024-08-04 21:27:46 +00:00
|
|
|
u8State = 0;
|
|
|
|
u32EnterPressStarted = 0xFFFFFFFF;
|
2024-06-29 20:17:34 +00:00
|
|
|
}
|
2024-08-04 21:27:46 +00:00
|
|
|
|
|
|
|
if (u8State == su8LastState) return 0;
|
|
|
|
su8LastState = u8State;
|
|
|
|
|
|
|
|
hid_enter_report_t *buf = (hid_enter_report_t *)HID_MISC_BUF;
|
|
|
|
memset(buf, 0, sizeof *buf);
|
|
|
|
buf->bReportId = HID_REPORT_ID_ENTER;
|
|
|
|
if (u8State) buf->u8Keyboard[0] = KEY_ENTER;
|
|
|
|
|
|
|
|
HID_MISC_SEND(buf);
|
|
|
|
return 1;
|
2024-06-29 20:17:34 +00:00
|
|
|
}
|
|
|
|
|
2024-08-15 13:54:01 +00:00
|
|
|
#ifdef ENABLE_TOUCH_INPUT
|
|
|
|
enum {
|
|
|
|
u8MaxFingers = (sizeof((hid_touch_report_t *)(0))->sFinger) /
|
|
|
|
(sizeof((hid_touch_report_t *)(0))->sFinger[0])
|
|
|
|
};
|
|
|
|
#define OVERLAP(start1, end1, start2, end2) (Minimum((end2), (end1)) >= Maximum((start1), (start2)))
|
|
|
|
typedef struct {
|
|
|
|
uint8_t bActive;
|
|
|
|
uint8_t u8Start;
|
|
|
|
uint8_t u8End;
|
|
|
|
|
|
|
|
uint8_t u8FakeCentreX;
|
|
|
|
uint8_t u8FakeCentreY;
|
|
|
|
} tracked_finger_t;
|
|
|
|
tracked_finger_t trackedFingers[u8MaxFingers] = { 0 };
|
|
|
|
|
|
|
|
#define SCREEN_HEIGHT 127
|
|
|
|
#define SCREEN_WIDTH 128
|
|
|
|
// Definition for Sonolus on my Pixel 6
|
|
|
|
// #define LEFT_EDGE 28
|
|
|
|
// #define RIGHT_EDGE 20
|
|
|
|
// Definition for PJSK on my Pixel 6
|
|
|
|
// #define LEFT_EDGE 22
|
|
|
|
// #define RIGHT_EDGE 20
|
|
|
|
#define LEFT_EDGE 0
|
|
|
|
#define RIGHT_EDGE 0
|
|
|
|
|
|
|
|
#define VERTICAL_MOVEMENT 10
|
|
|
|
|
|
|
|
#define SCREEN_Y (SCREEN_HEIGHT / 5)
|
|
|
|
#define TOUCH_WIDTH ((SCREEN_WIDTH - LEFT_EDGE - RIGHT_EDGE) / 16)
|
|
|
|
|
|
|
|
static void _TrackTouches(void) {
|
|
|
|
static tracked_finger_t newFingers[u8MaxFingers];
|
|
|
|
memset(newFingers, 0, sizeof newFingers);
|
|
|
|
|
|
|
|
uint8_t nFingers = 0;
|
|
|
|
int8_t iStart = -1;
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
uint16_t u16Bias;
|
|
|
|
uint8_t u8Pos;
|
|
|
|
} sBiasCalc[16] = { 0 };
|
|
|
|
uint16_t u16BiasTop = 0;
|
|
|
|
uint16_t u16BiasBottom = 0;
|
|
|
|
|
|
|
|
// Identify our currently active fingers
|
|
|
|
for (uint8_t x = 0; x <= 16; x++) {
|
|
|
|
if ((x < 16) && gu16PSoCDigital & (1 << x)) {
|
|
|
|
if (iStart == -1) {
|
|
|
|
u16BiasTop = 0;
|
|
|
|
u16BiasBottom = 0;
|
|
|
|
iStart = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Position = [LEFT_EDGE, {scale based on X} , RIGHT_EDGE] = SCREEN_WIDTH
|
|
|
|
// Virtual width = SCREEN_WIDTH - LEFT_EDGE - RIGHT_EDGE
|
|
|
|
|
|
|
|
// L + ((W-L-R) * (16 - x)) / 16)
|
|
|
|
|
|
|
|
// TODO: Might need -1 here not +1
|
|
|
|
sBiasCalc[x - iStart].u8Pos = LEFT_EDGE + ((SCREEN_WIDTH - LEFT_EDGE - RIGHT_EDGE) * (32 - (x * 2 + 1))) / 32;
|
|
|
|
// sBiasCalc[x - iStart].u8Pos -=
|
|
|
|
// (LEFT_EDGE + ((16 - x) * TOUCH_WIDTH) - (TOUCH_WIDTH / 2));
|
|
|
|
sBiasCalc[x - iStart].u16Bias = gu8GroundData[x * 2] + gu8GroundData[x * 2 + 1];
|
|
|
|
|
|
|
|
u16BiasTop += gu8GroundData[x * 2];
|
|
|
|
u16BiasBottom += gu8GroundData[x * 2 + 1];
|
|
|
|
} else {
|
|
|
|
if (iStart != -1) {
|
|
|
|
newFingers[nFingers].bActive = 1;
|
|
|
|
newFingers[nFingers].u8Start = iStart;
|
|
|
|
newFingers[nFingers].u8End = x - 1;
|
|
|
|
|
|
|
|
uint32_t u32TotalWeight = 0;
|
|
|
|
uint64_t u64Biased = 0;
|
|
|
|
for (uint8_t i = 0; i < x - iStart; i++) {
|
|
|
|
u32TotalWeight += (uint32_t)sBiasCalc[i].u16Bias;
|
|
|
|
u64Biased += (uint64_t)sBiasCalc[i].u8Pos * (uint64_t)sBiasCalc[i].u16Bias;
|
|
|
|
}
|
|
|
|
newFingers[nFingers].u8FakeCentreX = u64Biased / u32TotalWeight;
|
|
|
|
newFingers[nFingers].u8FakeCentreY = (u16BiasTop * VERTICAL_MOVEMENT) / (u16BiasTop + u16BiasBottom);
|
|
|
|
|
|
|
|
nFingers++;
|
|
|
|
iStart = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nFingers >= u8MaxFingers) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(trackedFingers, newFingers, sizeof newFingers);
|
|
|
|
}
|
|
|
|
static uint8_t _HID_Touch_Tick(void) {
|
|
|
|
hid_touch_report_t *buf = (hid_touch_report_t *)HID_MISC_BUF;
|
|
|
|
|
|
|
|
memset(buf, 0, sizeof *buf);
|
|
|
|
buf->bReportId = HID_REPORT_ID_TOUCH;
|
|
|
|
|
|
|
|
_TrackTouches();
|
|
|
|
|
|
|
|
// When in portrait, X and Y make sense
|
|
|
|
// In landscape, X and Y are inverse (ie screen rotation is ignored)
|
|
|
|
|
|
|
|
uint8_t nFingers = 0;
|
|
|
|
for (uint8_t i = 0; i < u8MaxFingers; i++) {
|
|
|
|
buf->sFinger[nFingers].bTipSwitch = trackedFingers[i].bActive;
|
|
|
|
buf->sFinger[nFingers].bIdentifier = i;
|
|
|
|
|
|
|
|
if (trackedFingers[i].bActive) {
|
|
|
|
uint8_t width = (trackedFingers[i].u8End + 1) - trackedFingers[i].u8Start;
|
|
|
|
buf->sFinger[nFingers].bX = SCREEN_Y + trackedFingers[i].u8FakeCentreY;
|
|
|
|
buf->sFinger[nFingers].bY = trackedFingers[i].u8FakeCentreX;
|
|
|
|
|
|
|
|
buf->sFinger[nFingers].bW = TOUCH_WIDTH / 2;
|
|
|
|
buf->sFinger[nFingers].bH = width * TOUCH_WIDTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
nFingers++;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->bContactCount = nFingers;
|
|
|
|
|
|
|
|
HID_MISC_SEND(buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
typedef enum {
|
|
|
|
TIMESLOT_KEYBOARD = 0,
|
|
|
|
TIMESLOT_CONSUMER,
|
|
|
|
TIMESLOT_ENTER,
|
2024-08-15 13:54:01 +00:00
|
|
|
#ifdef ENABLE_TOUCH_INPUT
|
|
|
|
TIMESLOT_TOUCH,
|
|
|
|
#endif
|
2024-08-04 21:27:46 +00:00
|
|
|
_TIMESLOT_COUNT,
|
|
|
|
} eTimeslot_t;
|
|
|
|
static void _HID_Misc_Tick(void) {
|
2024-06-29 20:17:34 +00:00
|
|
|
static uint8_t sbLastEnableKeyboard = 0;
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
// We have multiple things we're going to be sending over this HID endpoint, so we timeshare
|
|
|
|
// which reports are sent. If a particular report has nothing to report in its slot, the next
|
|
|
|
// report gets a chance instead.
|
|
|
|
static eTimeslot_t eTimeslot = 0;
|
|
|
|
|
|
|
|
uint8_t u8Tries = _TIMESLOT_COUNT;
|
|
|
|
while (u8Tries--) {
|
|
|
|
switch (eTimeslot++) {
|
|
|
|
case TIMESLOT_KEYBOARD:
|
|
|
|
if (gConfig.bEnableKeyboard) {
|
|
|
|
sbLastEnableKeyboard = 1;
|
|
|
|
if (_HID_Keyboard_Tick(0)) goto timeslot_used;
|
|
|
|
} else if (sbLastEnableKeyboard) {
|
|
|
|
// If we've just disabled the keyboard, make sure to send a packet with all keys
|
|
|
|
// released!
|
|
|
|
sbLastEnableKeyboard = 0;
|
|
|
|
if (_HID_Keyboard_Tick(1)) goto timeslot_used;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TIMESLOT_CONSUMER:
|
|
|
|
if (_HID_Consumer_Tick()) goto timeslot_used;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TIMESLOT_ENTER:
|
|
|
|
if (_HID_Enter_Tick()) goto timeslot_used;
|
|
|
|
break;
|
|
|
|
|
2024-08-15 13:54:01 +00:00
|
|
|
#ifdef ENABLE_TOUCH_INPUT
|
|
|
|
case TIMESLOT_TOUCH:
|
|
|
|
if (_HID_Touch_Tick()) goto timeslot_used;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (eTimeslot > _TIMESLOT_COUNT) eTimeslot = 0;
|
|
|
|
}
|
|
|
|
timeslot_used:;
|
|
|
|
;
|
2024-06-29 20:17:34 +00:00
|
|
|
}
|
|
|
|
|
2024-08-04 21:27:46 +00:00
|
|
|
void USBD_HID_PrepareReport(void) {
|
|
|
|
if (gu8HIDIO4Ready) IO4_HID_Tick();
|
2024-06-29 20:17:34 +00:00
|
|
|
if (gu8HIDMiscReady) _HID_Misc_Tick();
|
|
|
|
}
|
|
|
|
static uint8_t sIO4InBuffer[sizeof(io4_hid_in_t)] = { 0 };
|
|
|
|
uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size) {
|
|
|
|
switch (u8ReportId) {
|
|
|
|
case HID_REPORT_ID_IO4:
|
2024-08-04 21:27:46 +00:00
|
|
|
IO4_HID_Prepare(sIO4InBuffer);
|
2024-06-29 20:17:34 +00:00
|
|
|
*pu32Size = sizeof sIO4InBuffer;
|
|
|
|
return sIO4InBuffer;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void USBD_HID_SetReport(volatile uint8_t *pu8EpBuf, uint32_t u32Size) {
|
|
|
|
// TODO: is pu8EpBuf[0] the report ID?
|
|
|
|
// We need to switch on that report ID so we know what we're doing!
|
|
|
|
|
|
|
|
if (u32Size < 2) return;
|
2024-08-04 21:27:46 +00:00
|
|
|
IO4_Control(pu8EpBuf[1], u32Size - 2, &pu8EpBuf[2]);
|
2024-06-29 20:17:34 +00:00
|
|
|
}
|