2015-04-19 21:31:30 +00:00
|
|
|
/*
|
2015-10-22 05:13:26 +00:00
|
|
|
* Apple // emulator for *ix
|
2015-04-19 21:31:30 +00:00
|
|
|
*
|
|
|
|
* This software package is subject to the GNU General Public License
|
2015-10-22 05:13:26 +00:00
|
|
|
* version 3 or later (your choice) as published by the Free Software
|
2015-04-19 21:31:30 +00:00
|
|
|
* Foundation.
|
|
|
|
*
|
2015-10-22 05:13:26 +00:00
|
|
|
* Copyright 2013-2015 Aaron Culliney
|
2015-04-19 21:31:30 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "video/glvideo.h"
|
|
|
|
#include "video/glhudmodel.h"
|
|
|
|
#include "video/glnode.h"
|
2015-08-29 05:12:53 +00:00
|
|
|
#include "json_parse.h"
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
#if !INTERFACE_TOUCH
|
|
|
|
#error this is a touch interface module, possibly you mean to not compile this at all?
|
|
|
|
#endif
|
|
|
|
|
2015-07-26 17:41:12 +00:00
|
|
|
#define DEBUG_TOUCH_KBD 0
|
|
|
|
#if DEBUG_TOUCH_KBD
|
|
|
|
# define TOUCH_KBD_LOG(...) LOG(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
# define TOUCH_KBD_LOG(...)
|
|
|
|
#endif
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
#define MODEL_DEPTH -1/32.f
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
#define KBD_TEMPLATE_COLS 10
|
2015-12-17 07:00:52 +00:00
|
|
|
#define KBD_TEMPLATE_ROWS 8
|
2015-05-10 22:09:07 +00:00
|
|
|
|
2015-06-06 19:02:03 +00:00
|
|
|
#define DEFAULT_CTRL_COL 2
|
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
#define CTRLROW 2 // first non-empty default row
|
|
|
|
#define MAINROW 4 // main keyboard row offset
|
|
|
|
#define SWITCHCOL 0
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2016-01-01 22:11:27 +00:00
|
|
|
#define KBD_FB_WIDTH (KBD_TEMPLATE_COLS * FONT80_WIDTH_PIXELS) // 10 * 7 == 70
|
|
|
|
#define KBD_FB_HEIGHT (KBD_TEMPLATE_ROWS * FONT_HEIGHT_PIXELS) // 8 * 16 == 128
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
#define KBD_OBJ_W 2.0
|
2015-12-17 07:00:52 +00:00
|
|
|
#define KBD_OBJ_H 2.0
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
static bool isAvailable = false; // Were there any OpenGL/memory errors on gltouchkbd initialization?
|
|
|
|
static bool isEnabled = true; // Does player want touchkbd enabled?
|
2015-05-24 06:18:58 +00:00
|
|
|
static bool ownsScreen = false; // Does the touchkbd currently own the screen to the exclusion?
|
2015-08-21 05:15:56 +00:00
|
|
|
static bool isCalibrating = false; // Are we in calibration mode?
|
2015-05-13 04:37:13 +00:00
|
|
|
static bool allowLowercase = false; // show lowercase keyboard
|
2015-04-27 01:12:56 +00:00
|
|
|
static float minAlphaWhenOwnsScreen = 1/4.f;
|
2015-05-24 06:18:58 +00:00
|
|
|
static float minAlpha = 0.f;
|
2015-08-23 18:42:45 +00:00
|
|
|
static float maxAlpha = 1.f;
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2015-11-11 06:22:31 +00:00
|
|
|
static uint8_t kbdTemplateUCase[KBD_TEMPLATE_ROWS][KBD_TEMPLATE_COLS+1] = {
|
2015-12-17 07:00:52 +00:00
|
|
|
" ",
|
|
|
|
" ",
|
2015-05-13 04:37:13 +00:00
|
|
|
"@ @ @ @ @ ",
|
|
|
|
"1234567890",
|
2015-04-19 21:31:30 +00:00
|
|
|
"QWERTYUIOP",
|
2015-05-13 04:37:13 +00:00
|
|
|
"ASDFG@HJKL",
|
|
|
|
" ZXCVBNM @",
|
2015-08-11 03:15:48 +00:00
|
|
|
"@@ spa. @@",
|
2015-04-19 21:31:30 +00:00
|
|
|
};
|
|
|
|
|
2015-11-11 06:22:31 +00:00
|
|
|
static uint8_t kbdTemplateLCase[KBD_TEMPLATE_ROWS][KBD_TEMPLATE_COLS+1] = {
|
2015-12-17 07:00:52 +00:00
|
|
|
" ",
|
|
|
|
" ",
|
2015-05-13 04:37:13 +00:00
|
|
|
"@ @ @ @ @ ",
|
|
|
|
"1234567890",
|
2015-04-19 21:31:30 +00:00
|
|
|
"qwertyuiop",
|
2015-05-13 04:37:13 +00:00
|
|
|
"asdfg@hjkl",
|
|
|
|
" zxcvbnm @",
|
2015-08-11 03:15:48 +00:00
|
|
|
"@@ SPA. @@",
|
2015-04-19 21:31:30 +00:00
|
|
|
};
|
|
|
|
|
2015-11-11 06:22:31 +00:00
|
|
|
static uint8_t kbdTemplateAlt[KBD_TEMPLATE_ROWS][KBD_TEMPLATE_COLS+1] = {
|
2015-12-17 07:00:52 +00:00
|
|
|
" ",
|
|
|
|
" ",
|
2015-05-13 04:37:13 +00:00
|
|
|
"@ @ @ @ @ ",
|
2015-04-19 21:31:30 +00:00
|
|
|
"1234567890",
|
|
|
|
"@#%&*/-+()",
|
2015-05-13 04:37:13 +00:00
|
|
|
"~=_<>X{}[]",
|
|
|
|
"?!\"'`:;, X",
|
|
|
|
"$|\\XXX.^XX",
|
2015-04-19 21:31:30 +00:00
|
|
|
};
|
|
|
|
|
2015-12-10 06:11:44 +00:00
|
|
|
static uint8_t kbdTemplateUserAlt[KBD_TEMPLATE_ROWS][KBD_TEMPLATE_COLS+1] = {
|
2015-12-17 07:00:52 +00:00
|
|
|
" ",
|
|
|
|
" ",
|
2015-05-13 04:37:13 +00:00
|
|
|
"@ @ @ @ @ ",
|
|
|
|
" ",
|
2015-08-23 22:03:30 +00:00
|
|
|
" @ ",
|
|
|
|
" @@@ ",
|
|
|
|
" @ ",
|
2015-05-13 04:37:13 +00:00
|
|
|
" ",
|
2015-04-19 21:31:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// touch viewport
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
|
|
|
|
// Keyboard box
|
|
|
|
int kbdX;
|
|
|
|
int kbdXMax;
|
|
|
|
int kbdY;
|
|
|
|
int kbdYMax;
|
|
|
|
|
|
|
|
int kbdW;
|
|
|
|
int kbdH;
|
|
|
|
} touchport = { 0 };
|
|
|
|
|
|
|
|
// keyboard variables
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
GLModel *model;
|
|
|
|
|
|
|
|
int selectedCol;
|
|
|
|
int selectedRow;
|
|
|
|
|
2015-06-06 19:02:03 +00:00
|
|
|
int ctrlCol;
|
|
|
|
int ctrlRow;
|
|
|
|
|
|
|
|
bool ctrlPressed;
|
|
|
|
|
2016-01-01 22:11:27 +00:00
|
|
|
unsigned int glyphMultiplier;
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
struct timespec timingBegin;
|
2016-01-02 08:14:21 +00:00
|
|
|
|
|
|
|
// pending changes requiring reinitialization
|
|
|
|
unsigned int nextGlyphMultiplier;
|
2015-04-19 21:31:30 +00:00
|
|
|
} kbd = { 0 };
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Misc internal methods
|
|
|
|
|
2016-01-01 22:11:27 +00:00
|
|
|
#warning FIXME TODO ... make this a generic GLModelHUDElement function
|
2015-09-05 05:27:57 +00:00
|
|
|
static void _rerender_character(int col, int row) {
|
2016-01-03 19:57:48 +00:00
|
|
|
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)(kbd.model->custom);
|
2015-08-29 19:12:29 +00:00
|
|
|
|
2016-01-01 22:11:27 +00:00
|
|
|
// In English, this changes one glyph within the keyboard texture data to be the (un)selected color. It handles
|
|
|
|
// scaling from indexed color to RGBA8888 (4x) and then possibly scaling to 2x or greater.
|
|
|
|
|
|
|
|
const unsigned int fb_w = hudKeyboard->pixWidth;
|
|
|
|
const unsigned int pixelSize = sizeof(PIXEL_TYPE);
|
|
|
|
const unsigned int glyphScale = hudKeyboard->glyphMultiplier;
|
|
|
|
const unsigned int dstPointStride = pixelSize * glyphScale;
|
|
|
|
const unsigned int dstRowStride = fb_w * dstPointStride;
|
|
|
|
const unsigned int texSubRowStride = dstRowStride + (dstRowStride * (glyphScale-1));
|
|
|
|
const unsigned int indexedIdx = (row * fb_w * FONT_HEIGHT_PIXELS) + (col * FONT80_WIDTH_PIXELS);
|
|
|
|
unsigned int texIdx = ((row * fb_w * FONT_HEIGHT_PIXELS * /*1 row:*/glyphScale) + (col * FONT80_WIDTH_PIXELS)) * dstPointStride;
|
|
|
|
|
|
|
|
for (unsigned int i=0; i<FONT_HEIGHT_PIXELS; i++, texIdx+=texSubRowStride) {
|
|
|
|
for (unsigned int j=0; j<FONT80_WIDTH_PIXELS; j++, texIdx+=dstPointStride) {
|
|
|
|
// HACK : red <-> green swap of texture data
|
|
|
|
PIXEL_TYPE rgba = *((PIXEL_TYPE *)(kbd.model->texPixels + texIdx));
|
2015-09-05 18:38:35 +00:00
|
|
|
PIXEL_TYPE r = (rgba >> SHIFT_R) & MAX_SATURATION;
|
|
|
|
PIXEL_TYPE g = (rgba >> SHIFT_G) & MAX_SATURATION;
|
2015-09-05 05:27:57 +00:00
|
|
|
#if USE_RGBA4444
|
|
|
|
#error fixme
|
|
|
|
#else
|
|
|
|
rgba = ( ((rgba>>SHIFT_B)<<SHIFT_B) | (r << SHIFT_G) | (g << SHIFT_R) );
|
|
|
|
#endif
|
2016-01-01 22:11:27 +00:00
|
|
|
// scale texture data 1x, 2x, ...
|
|
|
|
unsigned int dstIdx = texIdx;
|
|
|
|
for (unsigned int k=0; k<glyphScale; k++, dstIdx+=dstRowStride) {
|
|
|
|
for (unsigned int l=0; l<glyphScale; l++, dstIdx+=pixelSize) {
|
|
|
|
*( (PIXEL_TYPE *)(kbd.model->texPixels + dstIdx) ) = rgba;
|
|
|
|
}
|
|
|
|
dstIdx -= dstPointStride;
|
|
|
|
}
|
2015-08-29 19:12:29 +00:00
|
|
|
}
|
2016-01-01 22:11:27 +00:00
|
|
|
texIdx -= (FONT80_WIDTH_PIXELS * dstPointStride);
|
2015-08-29 19:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kbd.model->texDirty = true;
|
|
|
|
}
|
|
|
|
|
2015-09-05 05:27:57 +00:00
|
|
|
static inline void _rerender_selected(int col, int row) {
|
|
|
|
if ((col >= 0) && (row >= 0)) {
|
|
|
|
_rerender_character(col, row);
|
2015-12-13 21:24:25 +00:00
|
|
|
|
|
|
|
// rerender certain adjacent keys ...
|
2016-01-03 19:57:48 +00:00
|
|
|
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)(kbd.model->custom);
|
2015-12-13 21:24:25 +00:00
|
|
|
const unsigned int indexRow = (hudKeyboard->tplWidth+1) * row;
|
|
|
|
uint8_t key = (hudKeyboard->tpl+indexRow)[col];
|
|
|
|
switch (key) {
|
|
|
|
case ICONTEXT_LEFTSPACE:
|
2015-09-05 05:27:57 +00:00
|
|
|
_rerender_character(col+1, row);
|
|
|
|
_rerender_character(col+2, row);
|
2015-12-13 21:24:25 +00:00
|
|
|
break;
|
|
|
|
case ICONTEXT_MIDSPACE:
|
|
|
|
_rerender_character(col-1, row);
|
|
|
|
_rerender_character(col+1, row);
|
|
|
|
break;
|
|
|
|
case ICONTEXT_RIGHTSPACE:
|
2015-09-05 05:27:57 +00:00
|
|
|
_rerender_character(col-2, row);
|
2015-12-13 21:24:25 +00:00
|
|
|
_rerender_character(col-1, row);
|
|
|
|
break;
|
|
|
|
case ICONTEXT_RETURN_L:
|
|
|
|
_rerender_character(col+1, row);
|
|
|
|
break;
|
|
|
|
case ICONTEXT_RETURN_R:
|
|
|
|
_rerender_character(col-1, row);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2015-09-05 05:27:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 06:22:31 +00:00
|
|
|
static inline void _switch_keyboard(GLModel *parent, uint8_t *template) {
|
2016-01-03 19:57:48 +00:00
|
|
|
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)parent->custom;
|
2015-04-19 21:31:30 +00:00
|
|
|
memcpy(hudKeyboard->tpl, template, sizeof(kbdTemplateUCase/* assuming all the same size */));
|
|
|
|
|
|
|
|
// setup normal color pixels
|
|
|
|
hudKeyboard->colorScheme = RED_ON_BLACK;
|
|
|
|
glhud_setupDefault(parent);
|
2015-08-29 19:12:29 +00:00
|
|
|
|
|
|
|
// find the CTRL visual(s) and render them engaged
|
|
|
|
if (kbd.ctrlPressed) {
|
|
|
|
for (unsigned int i=0, row=0; row<KBD_TEMPLATE_ROWS; row++, i++) {
|
|
|
|
for (unsigned int col=0; col<KBD_TEMPLATE_COLS; col++, i++) {
|
|
|
|
uint8_t ch = template[i];
|
|
|
|
if (ch == ICONTEXT_CTRL) {
|
2015-09-05 05:27:57 +00:00
|
|
|
_rerender_character(col, row);
|
2015-08-29 19:12:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool _is_point_on_keyboard(float x, float y) {
|
|
|
|
return (x >= touchport.kbdX && x <= touchport.kbdXMax && y >= touchport.kbdY && y <= touchport.kbdYMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void _screen_to_keyboard(float x, float y, OUTPARM int *col, OUTPARM int *row) {
|
2016-01-03 19:57:48 +00:00
|
|
|
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)(kbd.model->custom);
|
2015-05-29 06:23:21 +00:00
|
|
|
const unsigned int keyW = touchport.kbdW / hudKeyboard->tplWidth;
|
|
|
|
const unsigned int keyH = touchport.kbdH / hudKeyboard->tplHeight;
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2015-05-29 06:23:21 +00:00
|
|
|
*col = (x - touchport.kbdX) / keyW;
|
2015-04-19 21:31:30 +00:00
|
|
|
if (*col < 0) {
|
|
|
|
*col = 0;
|
2015-05-29 06:23:21 +00:00
|
|
|
} else if (*col >= hudKeyboard->tplWidth) {
|
2015-04-19 21:31:30 +00:00
|
|
|
*col = hudKeyboard->tplWidth-1;
|
|
|
|
}
|
|
|
|
*row = (y - touchport.kbdY) / keyH;
|
|
|
|
if (*row < 0) {
|
|
|
|
*row = 0;
|
|
|
|
}
|
|
|
|
|
2015-07-26 17:41:12 +00:00
|
|
|
TOUCH_KBD_LOG("SCREEN TO KEYBOARD : kbdX:%d kbdXMax:%d kbdW:%d keyW:%d ... scrn:(%f,%f)->kybd:(%d,%d)", touchport.kbdX, touchport.kbdXMax, touchport.kbdW, keyW, x, y, *col, *row);
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
|
|
|
|
2015-08-09 20:43:33 +00:00
|
|
|
static inline int64_t _tap_key_at_point(float x, float y) {
|
2016-01-03 19:57:48 +00:00
|
|
|
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)kbd.model->custom;
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2015-09-05 05:27:57 +00:00
|
|
|
// redraw previous selected key (if any)
|
|
|
|
_rerender_selected(kbd.selectedCol, kbd.selectedRow);
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
if (!_is_point_on_keyboard(x, y)) {
|
2015-06-06 19:02:03 +00:00
|
|
|
joy_button0 = 0x0;
|
|
|
|
joy_button1 = 0x0;
|
|
|
|
kbd.selectedCol = -1;
|
|
|
|
kbd.selectedRow = -1;
|
2015-07-31 04:36:22 +00:00
|
|
|
return false;
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
2015-06-06 19:02:03 +00:00
|
|
|
|
|
|
|
// get current key, col, row
|
|
|
|
|
|
|
|
int col = -1;
|
|
|
|
int row = -1;
|
2015-04-19 21:31:30 +00:00
|
|
|
_screen_to_keyboard(x, y, &col, &row);
|
|
|
|
const unsigned int indexRow = (hudKeyboard->tplWidth+1) * row;
|
2015-08-09 20:43:33 +00:00
|
|
|
|
2015-11-11 06:22:31 +00:00
|
|
|
uint8_t key = (hudKeyboard->tpl+indexRow)[col];
|
|
|
|
uint8_t scancode = 0;
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
// handle key
|
|
|
|
|
2015-07-31 04:36:22 +00:00
|
|
|
bool handled = true;
|
2015-04-19 21:31:30 +00:00
|
|
|
bool isASCII = false;
|
2015-06-06 19:02:03 +00:00
|
|
|
bool isCTRL = false;
|
2015-04-19 21:31:30 +00:00
|
|
|
switch (key) {
|
|
|
|
case ICONTEXT_LOWERCASE:
|
2015-11-11 06:22:31 +00:00
|
|
|
key = 0;
|
2015-08-29 19:07:56 +00:00
|
|
|
_switch_keyboard(kbd.model, kbdTemplateLCase[0]);
|
|
|
|
break;
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
case ICONTEXT_UPPERCASE:
|
2015-11-11 06:22:31 +00:00
|
|
|
key = 0;
|
2015-08-29 19:07:56 +00:00
|
|
|
_switch_keyboard(kbd.model, kbdTemplateUCase[0]);
|
|
|
|
break;
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
case ICONTEXT_SHOWALT1:
|
2015-11-11 06:22:31 +00:00
|
|
|
key = 0;
|
2015-08-29 19:07:56 +00:00
|
|
|
if (allowLowercase && !isCalibrating) {
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateAlt[CTRLROW][SWITCHCOL] = ICONTEXT_LOWERCASE;
|
2015-08-29 19:07:56 +00:00
|
|
|
} else {
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateAlt[CTRLROW][SWITCHCOL] = ICONTEXT_UPPERCASE;
|
2015-08-29 19:07:56 +00:00
|
|
|
}
|
|
|
|
_switch_keyboard(kbd.model, kbdTemplateAlt[0]);
|
2015-04-19 21:31:30 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ICONTEXT_NONACTIONABLE:
|
2015-08-11 03:15:48 +00:00
|
|
|
scancode = 0;
|
2015-09-13 18:23:59 +00:00
|
|
|
handled = false;
|
2015-04-19 21:31:30 +00:00
|
|
|
break;
|
|
|
|
|
2015-05-13 04:37:13 +00:00
|
|
|
case ICONTEXT_CTRL:
|
2015-06-06 19:02:03 +00:00
|
|
|
isCTRL = true;
|
|
|
|
kbd.ctrlPressed = !kbd.ctrlPressed;
|
2015-09-05 05:27:57 +00:00
|
|
|
_rerender_character(kbd.ctrlCol, kbd.ctrlRow);
|
2015-06-06 19:02:03 +00:00
|
|
|
col = -1;
|
|
|
|
row = -1;
|
2015-08-09 20:43:33 +00:00
|
|
|
scancode = SCODE_L_CTRL;
|
2015-05-13 04:37:13 +00:00
|
|
|
break;
|
|
|
|
|
2016-01-03 19:57:48 +00:00
|
|
|
case MOUSETEXT_RETURN:
|
2015-04-19 21:31:30 +00:00
|
|
|
case ICONTEXT_RETURN_L:
|
|
|
|
case ICONTEXT_RETURN_R:
|
2015-08-09 20:43:33 +00:00
|
|
|
key = ICONTEXT_RETURN_L;
|
|
|
|
scancode = SCODE_RET;
|
2015-04-19 21:31:30 +00:00
|
|
|
break;
|
|
|
|
|
2015-05-13 04:37:13 +00:00
|
|
|
case ICONTEXT_ESC:
|
2015-08-09 20:43:33 +00:00
|
|
|
scancode = SCODE_ESC;
|
2015-04-19 21:31:30 +00:00
|
|
|
break;
|
|
|
|
|
2015-05-10 22:09:07 +00:00
|
|
|
case MOUSETEXT_LEFT:
|
2015-04-19 21:31:30 +00:00
|
|
|
case ICONTEXT_BACKSPACE:
|
2015-08-09 20:43:33 +00:00
|
|
|
key = MOUSETEXT_LEFT;
|
|
|
|
scancode = SCODE_L;
|
2015-04-19 21:31:30 +00:00
|
|
|
break;
|
|
|
|
|
2015-05-10 22:09:07 +00:00
|
|
|
case MOUSETEXT_RIGHT:
|
2015-08-09 20:43:33 +00:00
|
|
|
scancode = SCODE_R;
|
2015-05-10 22:09:07 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MOUSETEXT_UP:
|
2015-08-09 20:43:33 +00:00
|
|
|
scancode = SCODE_U;
|
2015-05-10 22:09:07 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MOUSETEXT_DOWN:
|
2015-08-09 20:43:33 +00:00
|
|
|
scancode = SCODE_D;
|
2015-05-10 22:09:07 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MOUSETEXT_OPENAPPLE:
|
2015-06-06 19:02:03 +00:00
|
|
|
joy_button0 = joy_button0 ? 0x0 : 0x80;
|
2015-08-09 20:43:33 +00:00
|
|
|
scancode = SCODE_L_ALT;
|
2015-05-10 22:09:07 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MOUSETEXT_CLOSEDAPPLE:
|
2015-06-06 19:02:03 +00:00
|
|
|
joy_button1 = joy_button1 ? 0x0 : 0x80;
|
2015-08-09 20:43:33 +00:00
|
|
|
scancode = SCODE_R_ALT;
|
2015-05-10 22:09:07 +00:00
|
|
|
break;
|
|
|
|
|
2015-05-13 04:37:13 +00:00
|
|
|
case ICONTEXT_MENU_SPROUT:
|
2015-11-11 06:22:31 +00:00
|
|
|
key = 0;
|
2015-12-17 07:00:52 +00:00
|
|
|
kbd.ctrlPressed = false;
|
2015-12-17 06:15:19 +00:00
|
|
|
_switch_keyboard(kbd.model, kbdTemplateUserAlt[0]);
|
2015-05-13 04:37:13 +00:00
|
|
|
break;
|
|
|
|
|
2015-08-11 03:15:48 +00:00
|
|
|
case ICONTEXT_GOTO:
|
|
|
|
isASCII = true;
|
|
|
|
key = '\t';
|
|
|
|
break;
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
case ICONTEXT_LEFTSPACE:
|
|
|
|
case ICONTEXT_MIDSPACE:
|
|
|
|
case ICONTEXT_RIGHTSPACE:
|
2015-09-05 06:12:41 +00:00
|
|
|
case ICONTEXT_SPACE_VISUAL:
|
2015-08-09 20:43:33 +00:00
|
|
|
isASCII = true;
|
2015-04-19 21:31:30 +00:00
|
|
|
key = ' ';
|
2015-08-09 20:43:33 +00:00
|
|
|
break;
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
default: // ASCII
|
|
|
|
isASCII = true;
|
2015-09-05 06:12:41 +00:00
|
|
|
if (key >= 0x80) {
|
|
|
|
RELEASE_LOG("unhandled key code...");
|
2015-11-11 06:22:31 +00:00
|
|
|
key = 0;
|
2015-09-05 06:12:41 +00:00
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-09 20:43:33 +00:00
|
|
|
assert(scancode < 0x80);
|
2015-04-19 21:31:30 +00:00
|
|
|
if (isASCII) {
|
2015-08-09 20:43:33 +00:00
|
|
|
assert(key < 0x80);
|
|
|
|
scancode = c_keys_ascii_to_scancode(key);
|
2015-06-06 19:02:03 +00:00
|
|
|
if (kbd.ctrlPressed) {
|
2015-08-09 20:43:33 +00:00
|
|
|
c_keys_handle_input(scancode, /*pressed*/true, /*ASCII:*/false);
|
|
|
|
c_keys_handle_input(scancode, /*pressed*/false, /*ASCII:*/false);
|
2015-06-06 19:02:03 +00:00
|
|
|
} else {
|
|
|
|
c_keys_handle_input(key, /*pressed:*/true, /*ASCII:*/true);
|
|
|
|
}
|
2015-08-21 05:15:56 +00:00
|
|
|
if (key == ' ' && isCalibrating) {
|
2015-08-11 03:15:48 +00:00
|
|
|
key = ICONTEXT_SPACE_VISUAL;
|
|
|
|
}
|
2015-06-06 19:02:03 +00:00
|
|
|
} else if (isCTRL) {
|
2015-08-09 20:43:33 +00:00
|
|
|
c_keys_handle_input(scancode, /*pressed:*/kbd.ctrlPressed, /*ASCII:*/false);
|
|
|
|
} else if (scancode != -1) {
|
2015-06-06 19:02:03 +00:00
|
|
|
// perform a press of other keys (ESC, Arrows, etc)
|
2015-08-09 20:43:33 +00:00
|
|
|
c_keys_handle_input(scancode, /*pressed:*/true, /*ASCII:*/false);
|
|
|
|
c_keys_handle_input(scancode, /*pressed:*/false, /*ASCII:*/false);
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
2015-06-06 19:02:03 +00:00
|
|
|
|
2015-09-05 05:27:57 +00:00
|
|
|
// draw current selected key (if any)
|
2015-06-06 19:02:03 +00:00
|
|
|
kbd.selectedCol = col;
|
|
|
|
kbd.selectedRow = row;
|
2015-09-05 05:27:57 +00:00
|
|
|
_rerender_selected(kbd.selectedCol, kbd.selectedRow);
|
2015-08-09 20:43:33 +00:00
|
|
|
|
|
|
|
// return the key+scancode+handled
|
|
|
|
int64_t flags = (handled ? 0x1LL : 0x0LL);
|
|
|
|
|
|
|
|
key = key & 0xff;
|
|
|
|
scancode = scancode & 0xff;
|
|
|
|
|
|
|
|
flags |= ( (int64_t)((key << 8) | scancode) << TOUCH_FLAGS_ASCII_AND_SCANCODE_SHIFT);
|
|
|
|
return flags;
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// GLCustom functions
|
|
|
|
|
2016-01-03 19:57:48 +00:00
|
|
|
static void *_create_touchkbd_hud(GLModel *parent) {
|
|
|
|
|
|
|
|
parent->custom = glhud_createCustom(sizeof(GLModelHUDElement));
|
|
|
|
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)parent->custom;
|
|
|
|
|
|
|
|
if (!hudKeyboard) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
hudKeyboard->blackIsTransparent = true;
|
|
|
|
hudKeyboard->opaquePixelHalo = true;
|
|
|
|
hudKeyboard->glyphMultiplier = kbd.glyphMultiplier;
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
hudKeyboard->tplWidth = KBD_TEMPLATE_COLS;
|
|
|
|
hudKeyboard->tplHeight = KBD_TEMPLATE_ROWS;
|
|
|
|
hudKeyboard->pixWidth = KBD_FB_WIDTH;
|
|
|
|
hudKeyboard->pixHeight = KBD_FB_HEIGHT;
|
|
|
|
|
2015-12-31 06:16:57 +00:00
|
|
|
const unsigned int size = sizeof(kbdTemplateUCase/* assuming all the same dimensions */);
|
|
|
|
hudKeyboard->tpl = MALLOC(size);
|
|
|
|
hudKeyboard->pixels = MALLOC(KBD_FB_WIDTH * KBD_FB_HEIGHT);
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2015-12-31 06:16:57 +00:00
|
|
|
memcpy(hudKeyboard->tpl, kbdTemplateUCase[0], sizeof(kbdTemplateUCase/* assuming all the same dimensions */));
|
2015-12-10 05:54:25 +00:00
|
|
|
|
|
|
|
// setup normal color pixels
|
|
|
|
hudKeyboard->colorScheme = RED_ON_BLACK;
|
2016-01-03 19:57:48 +00:00
|
|
|
|
2015-12-10 05:54:25 +00:00
|
|
|
glhud_setupDefault(parent);
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
return hudKeyboard;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// GLNode functions
|
|
|
|
|
2015-12-10 05:06:36 +00:00
|
|
|
static void gltouchkbd_shutdown(void) {
|
|
|
|
LOG("gltouchkbd_shutdown ...");
|
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isAvailable = false;
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
mdlDestroyModel(&kbd.model);
|
2015-12-10 05:06:36 +00:00
|
|
|
kbd.selectedCol = -1;
|
|
|
|
kbd.selectedRow = -1;
|
|
|
|
kbd.ctrlPressed = false;
|
2016-01-02 08:14:21 +00:00
|
|
|
|
|
|
|
kbd.nextGlyphMultiplier = 0;
|
2015-12-10 05:06:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchkbd_setup(void) {
|
|
|
|
LOG("gltouchkbd_setup ... %u", sizeof(kbd));
|
|
|
|
|
|
|
|
gltouchkbd_shutdown();
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2016-01-02 21:10:02 +00:00
|
|
|
kbd.model = mdlCreateQuad((GLModelParams_s){
|
|
|
|
.skew_x = -1.0,
|
|
|
|
.skew_y = -1.0,
|
|
|
|
.z = MODEL_DEPTH,
|
|
|
|
.obj_w = KBD_OBJ_W,
|
|
|
|
.obj_h = KBD_OBJ_H,
|
|
|
|
.positionUsageHint = GL_STATIC_DRAW, // positions don't change
|
|
|
|
.tex_w = KBD_FB_WIDTH * kbd.glyphMultiplier,
|
|
|
|
.tex_h = KBD_FB_HEIGHT * kbd.glyphMultiplier,
|
|
|
|
.texcoordUsageHint = GL_DYNAMIC_DRAW, // but key texture does
|
|
|
|
}, (GLCustom){
|
2015-04-19 21:31:30 +00:00
|
|
|
.create = &_create_touchkbd_hud,
|
2016-01-03 19:57:48 +00:00
|
|
|
.destroy = &glhud_destroyDefault,
|
2016-01-02 21:10:02 +00:00
|
|
|
});
|
2015-04-19 21:31:30 +00:00
|
|
|
if (!kbd.model) {
|
|
|
|
LOG("gltouchkbd initialization problem");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!kbd.model->custom) {
|
|
|
|
LOG("gltouchkbd HUD initialization problem");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &kbd.timingBegin);
|
|
|
|
|
|
|
|
isAvailable = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchkbd_render(void) {
|
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!isEnabled) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-11 03:15:48 +00:00
|
|
|
if (!ownsScreen) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2016-01-02 08:14:21 +00:00
|
|
|
if (kbd.nextGlyphMultiplier) {
|
|
|
|
kbd.glyphMultiplier = kbd.nextGlyphMultiplier;
|
|
|
|
kbd.nextGlyphMultiplier = 0;
|
|
|
|
gltouchkbd_setup();
|
|
|
|
}
|
|
|
|
|
2016-01-02 22:23:09 +00:00
|
|
|
float alpha = glhud_getTimedVisibility(kbd.timingBegin, minAlpha, maxAlpha);
|
|
|
|
if (alpha < minAlpha) {
|
|
|
|
alpha = minAlpha;
|
|
|
|
_rerender_selected(kbd.selectedCol, kbd.selectedRow);
|
|
|
|
kbd.selectedCol = -1;
|
|
|
|
kbd.selectedRow = -1;
|
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
if (alpha > 0.0) {
|
|
|
|
// draw touch keyboard
|
|
|
|
|
|
|
|
glViewport(0, 0, touchport.width, touchport.height); // NOTE : show these HUD elements beyond the A2 framebuffer dimensions
|
|
|
|
glUniform1f(alphaValue, alpha);
|
|
|
|
|
|
|
|
glActiveTexture(TEXTURE_ACTIVE_TOUCHKBD);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, kbd.model->textureName);
|
|
|
|
if (kbd.model->texDirty) {
|
|
|
|
kbd.model->texDirty = false;
|
2015-10-01 04:55:07 +00:00
|
|
|
_HACKAROUND_GLTEXIMAGE2D_PRE(TEXTURE_ACTIVE_TOUCHKBD, kbd.model->textureName);
|
2015-05-31 02:08:00 +00:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, kbd.model->texWidth, kbd.model->texHeight, /*border*/0, TEX_FORMAT, TEX_TYPE, kbd.model->texPixels);
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
2015-09-27 18:45:37 +00:00
|
|
|
glUniform1i(texSamplerLoc, TEXTURE_ID_TOUCHKBD);
|
2015-04-19 21:31:30 +00:00
|
|
|
glhud_renderDefault(kbd.model);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchkbd_reshape(int w, int h) {
|
|
|
|
LOG("gltouchkbd_reshape(%d, %d)", w, h);
|
|
|
|
|
|
|
|
touchport.kbdX = 0;
|
|
|
|
|
|
|
|
if (w > touchport.width) {
|
|
|
|
touchport.width = w;
|
|
|
|
touchport.kbdXMax = w * (KBD_OBJ_W/2.f);
|
|
|
|
}
|
|
|
|
if (h > touchport.height) {
|
|
|
|
touchport.height = h;
|
2015-05-10 22:09:07 +00:00
|
|
|
touchport.kbdY = h * (1.f - (KBD_OBJ_H/2.f));
|
2015-04-19 21:31:30 +00:00
|
|
|
touchport.kbdYMax = h;
|
|
|
|
}
|
|
|
|
|
|
|
|
touchport.kbdW = touchport.kbdXMax - touchport.kbdX;
|
|
|
|
touchport.kbdH = touchport.kbdYMax - touchport.kbdY;
|
|
|
|
}
|
|
|
|
|
2015-07-31 04:36:22 +00:00
|
|
|
static int64_t gltouchkbd_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
if (!isAvailable) {
|
2015-07-31 04:36:22 +00:00
|
|
|
return 0x0LL;
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
|
|
|
if (!isEnabled) {
|
2015-07-31 04:36:22 +00:00
|
|
|
return 0x0LL;
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
2015-04-27 01:12:56 +00:00
|
|
|
if (!ownsScreen) {
|
2015-07-31 04:36:22 +00:00
|
|
|
return 0x0LL;
|
2015-04-27 01:12:56 +00:00
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
float x = x_coords[pointer_idx];
|
|
|
|
float y = y_coords[pointer_idx];
|
|
|
|
|
2015-07-31 04:36:22 +00:00
|
|
|
int64_t flags = TOUCH_FLAGS_KBD | TOUCH_FLAGS_HANDLED;
|
|
|
|
|
2015-12-13 21:38:03 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &kbd.timingBegin);
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
switch (action) {
|
|
|
|
case TOUCH_DOWN:
|
|
|
|
case TOUCH_POINTER_DOWN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOUCH_MOVE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOUCH_UP:
|
|
|
|
case TOUCH_POINTER_UP:
|
2015-08-09 20:43:33 +00:00
|
|
|
{
|
|
|
|
int64_t handledAndData = _tap_key_at_point(x, y);
|
|
|
|
flags |= ((handledAndData & 0x01) ? TOUCH_FLAGS_KEY_TAP : 0x0LL);
|
|
|
|
flags |= (handledAndData & TOUCH_FLAGS_ASCII_AND_SCANCODE_MASK);
|
|
|
|
}
|
2015-04-27 01:12:56 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TOUCH_CANCEL:
|
|
|
|
LOG("---KBD TOUCH CANCEL");
|
2015-07-31 04:36:22 +00:00
|
|
|
return 0x0LL;
|
2015-04-27 01:12:56 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
LOG("!!!KBD UNKNOWN TOUCH EVENT : %d", action);
|
2015-07-31 04:36:22 +00:00
|
|
|
return 0x0LL;
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
|
|
|
|
2015-07-31 04:36:22 +00:00
|
|
|
return flags;
|
2015-04-19 21:31:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Animation and settings handling
|
|
|
|
|
|
|
|
static bool gltouchkbd_isTouchKeyboardAvailable(void) {
|
|
|
|
return isAvailable;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchkbd_setTouchKeyboardEnabled(bool enabled) {
|
|
|
|
isEnabled = enabled;
|
|
|
|
}
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
static void gltouchkbd_setTouchKeyboardOwnsScreen(bool pwnd) {
|
|
|
|
ownsScreen = pwnd;
|
|
|
|
if (ownsScreen) {
|
|
|
|
minAlpha = minAlphaWhenOwnsScreen;
|
2015-08-29 19:05:09 +00:00
|
|
|
if (allowLowercase) {
|
|
|
|
caps_lock = false;
|
|
|
|
} else {
|
|
|
|
caps_lock = true;
|
|
|
|
}
|
2015-04-27 01:12:56 +00:00
|
|
|
} else {
|
2015-08-29 19:12:29 +00:00
|
|
|
// reset visuals
|
2015-04-27 01:12:56 +00:00
|
|
|
minAlpha = 0.0;
|
2015-09-05 05:27:57 +00:00
|
|
|
|
|
|
|
kbd.selectedCol = -1;
|
|
|
|
kbd.selectedRow = -1;
|
|
|
|
|
2015-08-29 19:12:29 +00:00
|
|
|
if (kbd.model) {
|
2016-01-03 19:57:48 +00:00
|
|
|
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)kbd.model->custom;
|
2015-08-29 19:12:29 +00:00
|
|
|
hudKeyboard->colorScheme = RED_ON_BLACK;
|
|
|
|
glhud_setupDefault(kbd.model);
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset CTRL state upon leaving this touch device
|
|
|
|
kbd.ctrlPressed = false;
|
|
|
|
c_keys_handle_input(SCODE_L_CTRL, /*pressed:*/false, /*ASCII:*/false);
|
2015-04-27 01:12:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-30 04:18:25 +00:00
|
|
|
static bool gltouchkbd_ownsScreen(void) {
|
|
|
|
return ownsScreen;
|
|
|
|
}
|
|
|
|
|
2016-01-02 08:14:21 +00:00
|
|
|
static void gltouchkbd_setGlyphScale(int glyphScale) {
|
|
|
|
if (glyphScale == 0) {
|
|
|
|
glyphScale = 1;
|
|
|
|
}
|
|
|
|
kbd.nextGlyphMultiplier = glyphScale;
|
|
|
|
}
|
|
|
|
|
2015-08-23 18:42:45 +00:00
|
|
|
static void gltouchkbd_setVisibilityWhenOwnsScreen(float inactiveAlpha, float activeAlpha) {
|
|
|
|
minAlphaWhenOwnsScreen = inactiveAlpha;
|
|
|
|
maxAlpha = activeAlpha;
|
|
|
|
if (ownsScreen) {
|
|
|
|
minAlpha = minAlphaWhenOwnsScreen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchkbd_setLowercaseEnabled(bool enabled) {
|
|
|
|
allowLowercase = enabled;
|
2015-09-03 05:33:56 +00:00
|
|
|
if (allowLowercase && ownsScreen) {
|
2015-08-23 18:42:45 +00:00
|
|
|
caps_lock = false;
|
|
|
|
} else {
|
|
|
|
caps_lock = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-21 05:15:56 +00:00
|
|
|
static void gltouchkbd_beginCalibration(void) {
|
|
|
|
video_clear();
|
|
|
|
isCalibrating = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchkbd_endCalibration(void) {
|
|
|
|
video_redraw();
|
|
|
|
isCalibrating = false;
|
|
|
|
}
|
|
|
|
|
2015-08-29 05:12:53 +00:00
|
|
|
static void gltouchkbd_loadAltKbd(const char *kbdPath) {
|
|
|
|
JSON_s parsedData = { 0 };
|
|
|
|
int tokCount = json_createFromFile(kbdPath, &parsedData);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (tokCount < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we are expecting a very specific layout ... abort if anything is not correct
|
|
|
|
int idx=0;
|
|
|
|
|
|
|
|
// begin with array
|
|
|
|
if (parsedData.jsonTokens[idx].type != JSMN_ARRAY) {
|
|
|
|
ERRLOG("Keyboard JSON must start with array");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++idx;
|
|
|
|
|
|
|
|
// next is a global comment string
|
|
|
|
if (parsedData.jsonTokens[idx].type != JSMN_STRING) {
|
|
|
|
ERRLOG("Expecting a comment string at JSON token position 1");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++idx;
|
|
|
|
|
|
|
|
// next is the dictionary of special strings
|
|
|
|
if (parsedData.jsonTokens[idx].type != JSMN_OBJECT) {
|
|
|
|
ERRLOG("Expecting a dictionary at JSON token position 2");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const int dictCount = parsedData.jsonTokens[idx].size;
|
|
|
|
++idx;
|
|
|
|
const int dictBegin = idx;
|
|
|
|
|
|
|
|
// verify all dictionary keys/vals are strings
|
|
|
|
bool allStrings = true;
|
|
|
|
const int dictEnd = dictBegin + (dictCount*2);
|
|
|
|
for (; idx<dictEnd; idx+=2) {
|
|
|
|
if (parsedData.jsonTokens[idx].type != JSMN_STRING) {
|
|
|
|
allStrings = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (parsedData.jsonTokens[idx+1].type != JSMN_STRING) {
|
|
|
|
allStrings = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!allStrings) {
|
|
|
|
ERRLOG("Specials dictionary should only contain strings");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// next are the character rows
|
|
|
|
int row = 0;
|
|
|
|
while (idx < tokCount) {
|
2015-12-17 07:00:52 +00:00
|
|
|
if (row < 2) {
|
|
|
|
if ( !((parsedData.jsonTokens[idx].type == JSMN_ARRAY) && (parsedData.jsonTokens[idx].parent == 0)) ) {
|
|
|
|
ERRLOG("Expecting a reserved array at keyboard row %d", row+1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (parsedData.jsonTokens[idx].size != KBD_TEMPLATE_COLS) {
|
|
|
|
// backwards compatibility when we only had the lower 6 rows for keyboard data ...
|
|
|
|
LOG("You can now edit %d columns of row %d in your custom keyboard", KBD_TEMPLATE_COLS, row+1);
|
|
|
|
for (int col=0; col<KBD_TEMPLATE_COLS; col++) {
|
|
|
|
kbdTemplateUserAlt[row][col] = ICONTEXT_NONACTIONABLE;
|
|
|
|
}
|
|
|
|
++row;
|
|
|
|
++idx;
|
|
|
|
idx += parsedData.jsonTokens[idx-1].size;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if ( !((parsedData.jsonTokens[idx].type == JSMN_ARRAY) && (parsedData.jsonTokens[idx].size == KBD_TEMPLATE_COLS) && (parsedData.jsonTokens[idx].parent == 0)) ) {
|
2015-08-29 05:12:53 +00:00
|
|
|
ERRLOG("Expecting an array of ten items at keyboard row %d", row+1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++idx;
|
|
|
|
const int count = idx+KBD_TEMPLATE_COLS;
|
|
|
|
for (int col=0; idx<count; col++, idx++) {
|
|
|
|
|
|
|
|
if (parsedData.jsonTokens[idx].type != JSMN_STRING) {
|
|
|
|
ERRLOG("Unexpected non-string at keyboard row %d", row+1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
int start = parsedData.jsonTokens[idx].start;
|
|
|
|
int end = parsedData.jsonTokens[idx].end;
|
|
|
|
assert(end >= start && "bug");
|
|
|
|
const int size = end - start;
|
|
|
|
|
|
|
|
if (size == 1) {
|
2015-12-10 06:11:44 +00:00
|
|
|
kbdTemplateUserAlt[row][col] = parsedData.jsonString[start];
|
2015-08-29 05:12:53 +00:00
|
|
|
continue;
|
|
|
|
} else if (size == 0) {
|
2015-12-10 06:11:44 +00:00
|
|
|
kbdTemplateUserAlt[row][col] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 05:12:53 +00:00
|
|
|
continue;
|
|
|
|
} else if (size < 0) {
|
|
|
|
assert(false && "negative size coming from jsmn!");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// assign special interface/mousetext visuals
|
|
|
|
char *key = &parsedData.jsonString[start];
|
|
|
|
|
|
|
|
bool foundMatch = false;
|
|
|
|
for (int i=dictBegin; i<dictEnd; i+=2) {
|
|
|
|
start = parsedData.jsonTokens[i].start;
|
|
|
|
end = parsedData.jsonTokens[i].end;
|
|
|
|
|
|
|
|
assert(end >= start && "bug");
|
|
|
|
|
|
|
|
if (end - start != size) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
foundMatch = (strncmp(key, &parsedData.jsonString[start], size) == 0);
|
|
|
|
|
|
|
|
if (foundMatch) {
|
|
|
|
start = parsedData.jsonTokens[i+1].start;
|
|
|
|
uint8_t ch = (uint8_t)strtol(&parsedData.jsonString[start], NULL, /*base:*/16);
|
2015-12-10 06:11:44 +00:00
|
|
|
kbdTemplateUserAlt[row][col] = ch;
|
2015-08-29 05:12:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
|
|
ERRLOG("no match for found multichar value in keyboard row %d", row+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
++row;
|
2015-12-17 07:00:52 +00:00
|
|
|
|
|
|
|
if (row >= KBD_TEMPLATE_ROWS) {
|
|
|
|
break;
|
|
|
|
}
|
2015-08-29 05:12:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (row != KBD_TEMPLATE_ROWS) {
|
|
|
|
ERRLOG("Did not find expected number of keyboard rows");
|
|
|
|
} else {
|
|
|
|
LOG("Parsed keyboard at %s", kbdPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
json_destroy(&parsedData);
|
|
|
|
}
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
static void _animation_showTouchKeyboard(void) {
|
2015-04-27 01:12:56 +00:00
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &kbd.timingBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _animation_hideTouchKeyboard(void) {
|
2015-04-27 01:12:56 +00:00
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
kbd.timingBegin = (struct timespec){ 0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2015-08-29 16:43:47 +00:00
|
|
|
// Constructors
|
|
|
|
|
|
|
|
static void _initialize_keyboard_templates(void) {
|
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
for (unsigned int i=0; i<(MAINROW-1); i++) {
|
2015-08-29 16:43:47 +00:00
|
|
|
for (unsigned int j=0; j<KBD_TEMPLATE_COLS; j++) {
|
|
|
|
kbdTemplateUCase[i][j] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateLCase[i][j] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateAlt [i][j] = ICONTEXT_NONACTIONABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (unsigned int i=0; i<KBD_TEMPLATE_ROWS; i++) {
|
|
|
|
for (unsigned int j=0; j<KBD_TEMPLATE_COLS; j++) {
|
2015-12-10 06:11:44 +00:00
|
|
|
if (kbdTemplateUserAlt[i][j] == ' ') {
|
|
|
|
kbdTemplateUserAlt[i][j] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateLCase[CTRLROW+0][0] = ICONTEXT_UPPERCASE;
|
|
|
|
kbdTemplateUCase[CTRLROW+0][0] = ICONTEXT_SHOWALT1;
|
|
|
|
kbdTemplateAlt [CTRLROW+0][0] = ICONTEXT_UPPERCASE;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+0][0] = ICONTEXT_UPPERCASE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[CTRLROW+0][2] = ICONTEXT_CTRL;
|
|
|
|
kbdTemplateLCase[CTRLROW+0][2] = ICONTEXT_CTRL;
|
|
|
|
kbdTemplateAlt [CTRLROW+0][2] = ICONTEXT_CTRL;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+0][2] = ICONTEXT_CTRL;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[CTRLROW+0][4] = ICONTEXT_ESC;
|
|
|
|
kbdTemplateLCase[CTRLROW+0][4] = ICONTEXT_ESC;
|
|
|
|
kbdTemplateAlt [CTRLROW+0][4] = ICONTEXT_ESC;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+0][4] = ICONTEXT_ESC;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[CTRLROW+0][6] = MOUSETEXT_OPENAPPLE;
|
|
|
|
kbdTemplateLCase[CTRLROW+0][6] = MOUSETEXT_OPENAPPLE;
|
|
|
|
kbdTemplateAlt [CTRLROW+0][6] = MOUSETEXT_OPENAPPLE;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+0][6] = MOUSETEXT_OPENAPPLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[CTRLROW+0][8] = MOUSETEXT_CLOSEDAPPLE;
|
|
|
|
kbdTemplateLCase[CTRLROW+0][8] = MOUSETEXT_CLOSEDAPPLE;
|
|
|
|
kbdTemplateAlt [CTRLROW+0][8] = MOUSETEXT_CLOSEDAPPLE;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+0][8] = MOUSETEXT_CLOSEDAPPLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUserAlt[CTRLROW+2][5] = MOUSETEXT_UP;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+3][4] = MOUSETEXT_LEFT;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+3][6] = MOUSETEXT_RIGHT;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+4][5] = MOUSETEXT_DOWN;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[CTRLROW+3][5] = ICONTEXT_MENU_SPROUT;
|
|
|
|
kbdTemplateLCase[CTRLROW+3][5] = ICONTEXT_MENU_SPROUT;
|
|
|
|
kbdTemplateAlt [CTRLROW+3][5] = ICONTEXT_MENU_SPROUT;
|
|
|
|
kbdTemplateUserAlt[CTRLROW+3][5] = ICONTEXT_UPPERCASE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+2][0] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateLCase[MAINROW+2][0] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+2][8] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateLCase[MAINROW+2][8] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateAlt [MAINROW+2][8] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+2][9] = ICONTEXT_BACKSPACE;
|
|
|
|
kbdTemplateLCase[MAINROW+2][9] = ICONTEXT_BACKSPACE;
|
|
|
|
kbdTemplateAlt [MAINROW+2][9] = ICONTEXT_BACKSPACE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
|
|
|
// last row specials
|
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateLCase[MAINROW+3][0] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateUCase[MAINROW+3][0] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+3][1] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateLCase[MAINROW+3][1] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+3][2] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateLCase[MAINROW+3][2] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+3][0] = ICONTEXT_GOTO;
|
|
|
|
kbdTemplateLCase[MAINROW+3][0] = ICONTEXT_GOTO;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+3][3] = ICONTEXT_LEFTSPACE;
|
|
|
|
kbdTemplateLCase[MAINROW+3][3] = ICONTEXT_LEFTSPACE;
|
|
|
|
kbdTemplateAlt [MAINROW+3][3] = ICONTEXT_LEFTSPACE;
|
|
|
|
kbdTemplateUCase[MAINROW+3][4] = ICONTEXT_MIDSPACE;
|
|
|
|
kbdTemplateLCase[MAINROW+3][4] = ICONTEXT_MIDSPACE;
|
|
|
|
kbdTemplateAlt [MAINROW+3][4] = ICONTEXT_MIDSPACE;
|
|
|
|
kbdTemplateUCase[MAINROW+3][5] = ICONTEXT_RIGHTSPACE;
|
|
|
|
kbdTemplateLCase[MAINROW+3][5] = ICONTEXT_RIGHTSPACE;
|
|
|
|
kbdTemplateAlt [MAINROW+3][5] = ICONTEXT_RIGHTSPACE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+3][7] = ICONTEXT_NONACTIONABLE;
|
|
|
|
kbdTemplateLCase[MAINROW+3][7] = ICONTEXT_NONACTIONABLE;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+3][8] = ICONTEXT_RETURN_L;
|
|
|
|
kbdTemplateLCase[MAINROW+3][8] = ICONTEXT_RETURN_L;
|
|
|
|
kbdTemplateAlt [MAINROW+3][8] = ICONTEXT_RETURN_L;
|
2015-08-29 16:43:47 +00:00
|
|
|
|
2015-12-17 07:00:52 +00:00
|
|
|
kbdTemplateUCase[MAINROW+3][9] = ICONTEXT_RETURN_R;
|
|
|
|
kbdTemplateLCase[MAINROW+3][9] = ICONTEXT_RETURN_R;
|
|
|
|
kbdTemplateAlt [MAINROW+3][9] = ICONTEXT_RETURN_R;
|
2015-08-29 16:43:47 +00:00
|
|
|
}
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
__attribute__((constructor(CTOR_PRIORITY_LATE)))
|
|
|
|
static void _init_gltouchkbd(void) {
|
|
|
|
LOG("Registering OpenGL software touch keyboard");
|
|
|
|
|
2015-08-29 16:43:47 +00:00
|
|
|
_initialize_keyboard_templates();
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
video_backend->animation_showTouchKeyboard = &_animation_showTouchKeyboard;
|
|
|
|
video_backend->animation_hideTouchKeyboard = &_animation_hideTouchKeyboard;
|
|
|
|
|
|
|
|
keydriver_isTouchKeyboardAvailable = &gltouchkbd_isTouchKeyboardAvailable;
|
|
|
|
keydriver_setTouchKeyboardEnabled = &gltouchkbd_setTouchKeyboardEnabled;
|
2015-04-27 01:12:56 +00:00
|
|
|
keydriver_setTouchKeyboardOwnsScreen = &gltouchkbd_setTouchKeyboardOwnsScreen;
|
2015-07-30 04:18:25 +00:00
|
|
|
keydriver_ownsScreen = &gltouchkbd_ownsScreen;
|
2015-08-23 18:42:45 +00:00
|
|
|
keydriver_setVisibilityWhenOwnsScreen = &gltouchkbd_setVisibilityWhenOwnsScreen;
|
|
|
|
keydriver_setLowercaseEnabled = &gltouchkbd_setLowercaseEnabled;
|
2015-08-21 05:15:56 +00:00
|
|
|
keydriver_beginCalibration = &gltouchkbd_beginCalibration;
|
|
|
|
keydriver_endCalibration = &gltouchkbd_endCalibration;
|
2015-08-29 05:12:53 +00:00
|
|
|
keydriver_loadAltKbd = &gltouchkbd_loadAltKbd;
|
2016-01-02 08:14:21 +00:00
|
|
|
keydriver_setGlyphScale = &gltouchkbd_setGlyphScale;
|
2015-04-19 21:31:30 +00:00
|
|
|
|
|
|
|
kbd.selectedCol = -1;
|
|
|
|
kbd.selectedRow = -1;
|
|
|
|
|
2015-06-06 19:02:03 +00:00
|
|
|
kbd.ctrlCol = DEFAULT_CTRL_COL;
|
2015-12-17 07:00:52 +00:00
|
|
|
kbd.ctrlRow = CTRLROW;
|
2015-06-06 19:02:03 +00:00
|
|
|
|
2016-01-01 22:11:27 +00:00
|
|
|
kbd.glyphMultiplier = 1;
|
|
|
|
|
2015-04-19 21:31:30 +00:00
|
|
|
glnode_registerNode(RENDER_LOW, (GLNode){
|
|
|
|
.setup = &gltouchkbd_setup,
|
|
|
|
.shutdown = &gltouchkbd_shutdown,
|
|
|
|
.render = &gltouchkbd_render,
|
|
|
|
.reshape = &gltouchkbd_reshape,
|
|
|
|
.onTouchEvent = &gltouchkbd_onTouchEvent,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|