2015-02-18 04:23:06 +00:00
|
|
|
/*
|
|
|
|
* Apple // emulator for *nix
|
|
|
|
*
|
|
|
|
* This software package is subject to the GNU General Public License
|
|
|
|
* version 2 or later (your choice) as published by the Free Software
|
|
|
|
* Foundation.
|
|
|
|
*
|
|
|
|
* THERE ARE NO WARRANTIES WHATSOEVER.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
2015-03-23 01:53:13 +00:00
|
|
|
#include "video/glvideo.h"
|
2015-04-11 19:29:44 +00:00
|
|
|
#include "video/glhudmodel.h"
|
2015-04-12 22:35:16 +00:00
|
|
|
#include "video/glnode.h"
|
|
|
|
|
|
|
|
#if !INTERFACE_TOUCH
|
|
|
|
#error this is a touch interface module, possibly you mean to not compile this at all?
|
|
|
|
#endif
|
2015-02-18 04:23:06 +00:00
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
#define MODEL_DEPTH -1/32.f
|
2015-04-11 07:30:23 +00:00
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
#define AXIS_TEMPLATE_COLS 5
|
|
|
|
#define AXIS_TEMPLATE_ROWS 5
|
|
|
|
|
|
|
|
#define BUTTON_TEMPLATE_COLS 1
|
|
|
|
#define BUTTON_TEMPLATE_ROWS 1
|
|
|
|
|
2015-05-29 06:23:21 +00:00
|
|
|
#define AXIS_FB_WIDTH (AXIS_TEMPLATE_COLS * FONT80_WIDTH_PIXELS)
|
2015-04-04 19:51:32 +00:00
|
|
|
#define AXIS_FB_HEIGHT (AXIS_TEMPLATE_ROWS * FONT_HEIGHT_PIXELS)
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-05-29 06:23:21 +00:00
|
|
|
#define BUTTON_FB_WIDTH (BUTTON_TEMPLATE_COLS * FONT80_WIDTH_PIXELS)
|
2015-04-04 19:51:32 +00:00
|
|
|
#define BUTTON_FB_HEIGHT (BUTTON_TEMPLATE_ROWS * FONT_HEIGHT_PIXELS)
|
|
|
|
|
|
|
|
#define AXIS_OBJ_W 0.4
|
|
|
|
#define AXIS_OBJ_H 0.5
|
|
|
|
#define AXIS_OBJ_HALF_W (AXIS_OBJ_W/2.f)
|
|
|
|
#define AXIS_OBJ_HALF_H (AXIS_OBJ_H/2.f)
|
|
|
|
|
|
|
|
#define BUTTON_OBJ_W 0.2
|
|
|
|
#define BUTTON_OBJ_H 0.25
|
|
|
|
#define BUTTON_OBJ_HALF_W (BUTTON_OBJ_W/2.f)
|
|
|
|
#define BUTTON_OBJ_HALF_H (BUTTON_OBJ_H/2.f)
|
|
|
|
|
2015-06-07 04:28:12 +00:00
|
|
|
#define BUTTON_SWITCH_THRESHOLD_DEFAULT 22
|
|
|
|
#define BUTTON_TAP_DELAY_NANOS_DEFAULT 50000000
|
2015-04-02 02:59:38 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
TOUCHED_NONE = -1,
|
|
|
|
TOUCHED_BUTTON0 = 0,
|
|
|
|
TOUCHED_BUTTON1,
|
|
|
|
TOUCHED_BOTH,
|
|
|
|
};
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
static bool isAvailable = false; // Were there any OpenGL/memory errors on gltouchjoy initialization?
|
2015-06-07 04:28:12 +00:00
|
|
|
static bool isShuttingDown = false;
|
2015-04-11 19:16:54 +00:00
|
|
|
static bool isEnabled = true; // Does player want touchjoy enabled?
|
2015-05-24 06:18:58 +00:00
|
|
|
static bool ownsScreen = true; // Does the touchjoy currently own the screen?
|
2015-04-27 01:12:56 +00:00
|
|
|
static float minAlphaWhenOwnsScreen = 0;
|
|
|
|
static float minAlpha = 0;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
// viewport touch
|
|
|
|
static struct {
|
|
|
|
int width;
|
|
|
|
int height;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
// Axis box
|
|
|
|
int axisX;
|
|
|
|
int axisXMax;
|
|
|
|
int axisY;
|
|
|
|
int axisYMax;
|
2015-04-04 19:51:32 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
// Button box
|
|
|
|
int buttonX;
|
|
|
|
int buttonXMax;
|
|
|
|
int buttonY;
|
|
|
|
int buttonYMax;
|
|
|
|
|
|
|
|
// TODO FIXME : support 2-players!
|
|
|
|
} touchport = { 0 };
|
2015-04-04 19:51:32 +00:00
|
|
|
|
2015-04-02 02:59:38 +00:00
|
|
|
// touch axis variables
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
static struct {
|
|
|
|
|
|
|
|
GLModel *model;
|
|
|
|
uint8_t northChar;
|
|
|
|
uint8_t westChar;
|
|
|
|
uint8_t eastChar;
|
|
|
|
uint8_t southChar;
|
|
|
|
bool modelDirty;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
touchjoy_axis_type_t type;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
int centerX;
|
|
|
|
int centerY;
|
|
|
|
int trackingIndex;
|
|
|
|
struct timespec timingBegin;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
} axes = { 0 };
|
2015-04-11 07:30:23 +00:00
|
|
|
|
2015-04-02 02:59:38 +00:00
|
|
|
// button object variables
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
static struct {
|
|
|
|
GLModel *model;
|
|
|
|
uint8_t activeChar;
|
|
|
|
bool modelDirty;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
int touchDownButton;
|
|
|
|
int northButton;
|
|
|
|
int southButton;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
uint8_t char0;
|
|
|
|
uint8_t char1;
|
|
|
|
uint8_t charBoth;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
int switchThreshold;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
int centerX;
|
|
|
|
int centerY;
|
|
|
|
int trackingIndex;
|
|
|
|
struct timespec timingBegin;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-06-07 04:28:12 +00:00
|
|
|
pthread_t tapDelayThreadId;
|
|
|
|
pthread_mutex_t tapDelayMutex;
|
|
|
|
pthread_cond_t tapDelayCond;
|
|
|
|
unsigned int tapDelayNanos;
|
|
|
|
|
|
|
|
volatile uint8_t currButtonValue0;
|
|
|
|
volatile uint8_t currButtonValue1;
|
|
|
|
volatile uint8_t currButtonChar;
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
} buttons = { 0 };
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 07:30:23 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
#warning FIXME TODO ... this can become a common helper function ...
|
|
|
|
static inline float _get_component_visibility(struct timespec timingBegin) {
|
|
|
|
struct timespec now = { 0 };
|
|
|
|
struct timespec deltat = { 0 };
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
float alpha = minAlpha;
|
|
|
|
deltat = timespec_diff(timingBegin, now, NULL);
|
|
|
|
if (deltat.tv_sec == 0) {
|
|
|
|
alpha = 1.0;
|
|
|
|
if (deltat.tv_nsec >= NANOSECONDS_PER_SECOND/2) {
|
|
|
|
alpha -= ((float)deltat.tv_nsec-(NANOSECONDS_PER_SECOND/2)) / (float)(NANOSECONDS_PER_SECOND/2);
|
|
|
|
if (alpha < minAlpha) {
|
|
|
|
alpha = minAlpha;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return alpha;
|
|
|
|
}
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
static void _setup_axis_object(GLModel *parent) {
|
|
|
|
GLModelHUDElement *hudElement = (GLModelHUDElement *)parent->custom;
|
|
|
|
|
|
|
|
if (hudElement->tpl == NULL) {
|
|
|
|
// deferred construction ...
|
|
|
|
const char axisTemplate[AXIS_TEMPLATE_ROWS][AXIS_TEMPLATE_COLS+1] = {
|
|
|
|
" @ ",
|
|
|
|
" | ",
|
|
|
|
"@-+-@",
|
|
|
|
" | ",
|
|
|
|
" @ ",
|
|
|
|
};
|
|
|
|
|
|
|
|
const unsigned int size = sizeof(axisTemplate);
|
|
|
|
hudElement->tpl = calloc(size, 1);
|
|
|
|
hudElement->tplWidth = AXIS_TEMPLATE_COLS;
|
|
|
|
hudElement->tplHeight = AXIS_TEMPLATE_ROWS;
|
|
|
|
memcpy(hudElement->tpl, axisTemplate, size);
|
|
|
|
|
|
|
|
hudElement->pixels = calloc(AXIS_FB_WIDTH * AXIS_FB_HEIGHT, 1);
|
|
|
|
hudElement->pixWidth = AXIS_FB_WIDTH;
|
|
|
|
hudElement->pixHeight = AXIS_FB_HEIGHT;
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
|
|
|
|
const unsigned int row = (AXIS_TEMPLATE_COLS+1);
|
|
|
|
((hudElement->tpl)+(row*0))[2] = axes.northChar;
|
|
|
|
((hudElement->tpl)+(row*2))[0] = axes.westChar;
|
|
|
|
((hudElement->tpl)+(row*2))[4] = axes.eastChar;
|
|
|
|
((hudElement->tpl)+(row*4))[2] = axes.southChar;
|
|
|
|
|
2015-04-12 18:27:33 +00:00
|
|
|
glhud_setupDefault(parent);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
static void *_create_touchjoy_hud(void) {
|
2015-04-12 18:27:33 +00:00
|
|
|
GLModelHUDElement *hudElement = (GLModelHUDElement *)glhud_createDefault();
|
2015-04-27 01:12:56 +00:00
|
|
|
if (hudElement) {
|
|
|
|
hudElement->blackIsTransparent = true;
|
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
return hudElement;
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
static void _setup_button_object(GLModel *parent) {
|
|
|
|
GLModelHUDElement *hudElement = (GLModelHUDElement *)parent->custom;
|
|
|
|
|
|
|
|
if (hudElement->tpl == NULL) {
|
|
|
|
// deferred construction ...
|
|
|
|
const char buttonTemplate[BUTTON_TEMPLATE_ROWS][BUTTON_TEMPLATE_COLS+1] = {
|
|
|
|
"@",
|
|
|
|
};
|
|
|
|
|
|
|
|
const unsigned int size = sizeof(buttonTemplate);
|
|
|
|
hudElement->tpl = calloc(size, 1);
|
|
|
|
hudElement->tplWidth = BUTTON_TEMPLATE_COLS;
|
|
|
|
hudElement->tplHeight = BUTTON_TEMPLATE_ROWS;
|
|
|
|
memcpy(hudElement->tpl, buttonTemplate, size);
|
|
|
|
|
|
|
|
hudElement->pixels = calloc(BUTTON_FB_WIDTH * BUTTON_FB_HEIGHT, 1);
|
|
|
|
hudElement->pixWidth = BUTTON_FB_WIDTH;
|
|
|
|
hudElement->pixHeight = BUTTON_FB_HEIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
const unsigned int row = (BUTTON_TEMPLATE_COLS+1);
|
|
|
|
((hudElement->tpl)+(row*0))[0] = buttons.activeChar;
|
|
|
|
|
2015-04-12 18:27:33 +00:00
|
|
|
glhud_setupDefault(parent);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-06-07 04:28:12 +00:00
|
|
|
static inline void _setup_button_object_with_char(char newChar) {
|
|
|
|
if (buttons.activeChar != newChar) {
|
|
|
|
buttons.activeChar = newChar;
|
|
|
|
_setup_button_object(buttons.model);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Tap Delay Thread : delays processing of touch-down so that a different joystick button/key can be fired
|
|
|
|
|
|
|
|
static inline void _schedule_button_tap(void) {
|
|
|
|
pthread_cond_signal(&buttons.tapDelayCond);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *_button_tap_delayed_thread(void *dummyptr) {
|
|
|
|
do {
|
|
|
|
LOG(">>> [DELAYEDTAP] sleeping ...");
|
|
|
|
pthread_cond_wait(&buttons.tapDelayCond, &buttons.tapDelayMutex);
|
|
|
|
|
|
|
|
if (UNLIKELY(isShuttingDown)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec ts = { .tv_sec=0, .tv_nsec=buttons.tapDelayNanos };
|
|
|
|
nanosleep(&ts, NULL);
|
|
|
|
|
|
|
|
do {
|
|
|
|
LOG(">>> [DELAYEDTAP] waking up ...");
|
|
|
|
joy_button0 = buttons.currButtonValue0;
|
|
|
|
joy_button1 = buttons.currButtonValue1;
|
|
|
|
_setup_button_object_with_char(buttons.currButtonChar);
|
|
|
|
|
|
|
|
struct timespec ts = { .tv_sec=0, .tv_nsec=buttons.tapDelayNanos };
|
|
|
|
nanosleep(&ts, NULL);
|
|
|
|
|
|
|
|
if (UNLIKELY(isShuttingDown)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (buttons.trackingIndex != TOUCHED_NONE);
|
|
|
|
|
|
|
|
joy_button0 = 0x0;
|
|
|
|
joy_button1 = 0x0;
|
|
|
|
|
|
|
|
if (UNLIKELY(isShuttingDown)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2015-04-12 22:35:16 +00:00
|
|
|
static void gltouchjoy_setup(void) {
|
|
|
|
LOG("gltouchjoy_setup ...");
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
mdlDestroyModel(&axes.model);
|
|
|
|
mdlDestroyModel(&buttons.model);
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-06-07 04:28:12 +00:00
|
|
|
isShuttingDown = false;
|
|
|
|
assert((buttons.tapDelayThreadId == 0) && "setup called multiple times!");
|
|
|
|
pthread_create(&buttons.tapDelayThreadId, NULL, (void *)&_button_tap_delayed_thread, (void *)NULL);
|
|
|
|
|
2015-05-31 02:08:00 +00:00
|
|
|
axes.model = mdlCreateQuad(-1.05, -1.0, AXIS_OBJ_W, AXIS_OBJ_H, MODEL_DEPTH, AXIS_FB_WIDTH, AXIS_FB_HEIGHT, (GLCustom){
|
2015-04-11 19:29:44 +00:00
|
|
|
.create = &_create_touchjoy_hud,
|
|
|
|
.setup = &_setup_axis_object,
|
2015-04-12 18:27:33 +00:00
|
|
|
.destroy = &glhud_destroyDefault,
|
2015-04-11 19:29:44 +00:00
|
|
|
});
|
|
|
|
if (!axes.model) {
|
2015-04-11 07:30:23 +00:00
|
|
|
LOG("gltouchjoy not initializing axis");
|
2015-04-02 02:59:38 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-05-22 05:31:06 +00:00
|
|
|
if (!axes.model->custom) {
|
|
|
|
LOG("gltouchjoy axes initialization problem");
|
|
|
|
return;
|
|
|
|
}
|
2015-04-02 02:59:38 +00:00
|
|
|
|
|
|
|
// button object
|
|
|
|
|
2015-05-31 02:08:00 +00:00
|
|
|
buttons.model = mdlCreateQuad(1.05-BUTTON_OBJ_W, -1.0, BUTTON_OBJ_W, BUTTON_OBJ_H, MODEL_DEPTH, BUTTON_FB_WIDTH, BUTTON_FB_HEIGHT, (GLCustom){
|
2015-04-11 19:29:44 +00:00
|
|
|
.create = &_create_touchjoy_hud,
|
|
|
|
.setup = &_setup_button_object,
|
2015-04-12 18:27:33 +00:00
|
|
|
.destroy = &glhud_destroyDefault,
|
2015-04-11 19:29:44 +00:00
|
|
|
});
|
|
|
|
if (!buttons.model) {
|
2015-04-02 02:59:38 +00:00
|
|
|
LOG("gltouchjoy not initializing buttons");
|
|
|
|
return;
|
|
|
|
}
|
2015-05-22 05:31:06 +00:00
|
|
|
if (!buttons.model->custom) {
|
|
|
|
LOG("gltouchjoy buttons initialization problem");
|
|
|
|
return;
|
|
|
|
}
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &axes.timingBegin);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &buttons.timingBegin);
|
2015-04-11 07:30:23 +00:00
|
|
|
|
2015-04-02 02:59:38 +00:00
|
|
|
isAvailable = true;
|
|
|
|
}
|
|
|
|
|
2015-04-12 22:35:16 +00:00
|
|
|
static void gltouchjoy_shutdown(void) {
|
|
|
|
LOG("gltouchjoy_shutdown ...");
|
2015-04-02 02:59:38 +00:00
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isAvailable = false;
|
|
|
|
|
2015-06-07 04:28:12 +00:00
|
|
|
isShuttingDown = true;
|
|
|
|
pthread_cond_signal(&buttons.tapDelayCond);
|
|
|
|
if (pthread_join(buttons.tapDelayThreadId, NULL)) {
|
|
|
|
ERRLOG("OOPS: pthread_join tap delay thread ...");
|
|
|
|
}
|
|
|
|
buttons.tapDelayThreadId = 0;
|
|
|
|
buttons.tapDelayMutex = (pthread_mutex_t){ 0 };
|
|
|
|
buttons.tapDelayCond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
mdlDestroyModel(&axes.model);
|
|
|
|
mdlDestroyModel(&buttons.model);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchjoy_render(void) {
|
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!isEnabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
glViewport(0, 0, touchport.width, touchport.height); // NOTE : show these HUD elements beyond the A2 framebuffer dimensions
|
2015-04-04 19:51:32 +00:00
|
|
|
|
|
|
|
// draw axis
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
float alpha = _get_component_visibility(axes.timingBegin);
|
2015-04-11 19:16:54 +00:00
|
|
|
if (alpha > 0.0) {
|
|
|
|
glUniform1f(alphaValue, alpha);
|
|
|
|
|
|
|
|
glActiveTexture(TEXTURE_ACTIVE_TOUCHJOY_AXIS);
|
2015-04-11 19:29:44 +00:00
|
|
|
glBindTexture(GL_TEXTURE_2D, axes.model->textureName);
|
|
|
|
if (axes.model->texDirty) {
|
|
|
|
axes.model->texDirty = false;
|
2015-05-31 02:08:00 +00:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, axes.model->texWidth, axes.model->texHeight, /*border*/0, TEX_FORMAT, TEX_TYPE, axes.model->texPixels);
|
2015-04-11 19:16:54 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
if (axes.modelDirty) {
|
|
|
|
axes.modelDirty = false;
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, axes.model->posBufferName);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, axes.model->positionArraySize, axes.model->positions, GL_DYNAMIC_DRAW);
|
2015-04-11 19:16:54 +00:00
|
|
|
}
|
|
|
|
glUniform1i(uniformTex2Use, TEXTURE_ID_TOUCHJOY_AXIS);
|
2015-04-12 18:27:33 +00:00
|
|
|
glhud_renderDefault(axes.model);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
// draw button(s)
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
alpha = _get_component_visibility(buttons.timingBegin);
|
2015-04-11 19:16:54 +00:00
|
|
|
if (alpha > 0.0) {
|
|
|
|
glUniform1f(alphaValue, alpha);
|
|
|
|
|
|
|
|
glActiveTexture(TEXTURE_ACTIVE_TOUCHJOY_BUTTON);
|
2015-04-11 19:29:44 +00:00
|
|
|
glBindTexture(GL_TEXTURE_2D, buttons.model->textureName);
|
|
|
|
if (buttons.model->texDirty) {
|
|
|
|
buttons.model->texDirty = false;
|
2015-05-31 02:08:00 +00:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, buttons.model->texWidth, buttons.model->texHeight, /*border*/0, TEX_FORMAT, TEX_TYPE, buttons.model->texPixels);
|
2015-04-11 19:16:54 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
if (buttons.modelDirty) {
|
|
|
|
buttons.modelDirty = false;
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, buttons.model->posBufferName);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, buttons.model->positionArraySize, buttons.model->positions, GL_DYNAMIC_DRAW);
|
2015-04-11 19:16:54 +00:00
|
|
|
}
|
|
|
|
glUniform1i(uniformTex2Use, TEXTURE_ID_TOUCHJOY_BUTTON);
|
2015-04-12 18:27:33 +00:00
|
|
|
glhud_renderDefault(buttons.model);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchjoy_reshape(int w, int h) {
|
|
|
|
LOG("gltouchjoy_reshape(%d, %d)", w, h);
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
touchport.axisX = 0;
|
2015-04-27 01:12:56 +00:00
|
|
|
touchport.axisY = 0;
|
|
|
|
touchport.buttonY = 0;
|
2015-04-04 19:51:32 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
if (w > touchport.width) {
|
|
|
|
touchport.width = w;
|
|
|
|
touchport.axisXMax = w>>1;
|
|
|
|
touchport.buttonX = w>>1;
|
|
|
|
touchport.buttonXMax = w;
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
if (h > touchport.height) {
|
|
|
|
touchport.height = h;
|
|
|
|
touchport.axisYMax = h;
|
|
|
|
touchport.buttonYMax = h;
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gltouchjoy_resetJoystick(void) {
|
|
|
|
// no-op
|
|
|
|
}
|
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
static inline bool _is_point_on_axis_side(float x, float y) {
|
2015-04-11 19:29:44 +00:00
|
|
|
return (x >= touchport.axisX && x <= touchport.axisXMax && y >= touchport.axisY && y <= touchport.axisYMax);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
static inline bool _is_point_on_button_side(float x, float y) {
|
2015-04-11 19:29:44 +00:00
|
|
|
return (x >= touchport.buttonX && x <= touchport.buttonXMax && y >= touchport.buttonY && y <= touchport.buttonYMax);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-10 05:49:53 +00:00
|
|
|
static inline void _reset_model_position(GLModel *model, float touchX, float touchY, float objHalfW, float objHalfH) {
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
float centerX = 0.f;
|
|
|
|
float centerY = 0.f;
|
2015-04-19 21:28:47 +00:00
|
|
|
glhud_screenToModel(touchX, touchY, touchport.width, touchport.height, ¢erX, ¢erY);
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
/* 2...3
|
|
|
|
* .
|
|
|
|
* .
|
|
|
|
* .
|
|
|
|
* 0...1
|
|
|
|
*/
|
|
|
|
|
|
|
|
GLfloat *quad = (GLfloat *)(model->positions);
|
|
|
|
quad[0 +0] = centerX-objHalfW;
|
|
|
|
quad[0 +1] = centerY-objHalfH;
|
|
|
|
quad[4 +0] = centerX+objHalfW;
|
|
|
|
quad[4 +1] = centerY-objHalfH;
|
|
|
|
quad[8 +0] = centerX-objHalfW;
|
|
|
|
quad[8 +1] = centerY+objHalfH;
|
|
|
|
quad[12+0] = centerX+objHalfW;
|
|
|
|
quad[12+1] = centerY+objHalfH;
|
|
|
|
}
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
static inline void _move_joystick_axis(int x, int y) {
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
x = (x - axes.centerX) + 0x80;
|
|
|
|
y = (y - axes.centerY) + 0x80;
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
if (x < 0) {
|
|
|
|
x = 0;
|
|
|
|
}
|
|
|
|
if (x > 0xff) {
|
|
|
|
x = 0xff;
|
|
|
|
}
|
|
|
|
if (y < 0) {
|
|
|
|
y = 0;
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
2015-04-04 19:51:32 +00:00
|
|
|
if (y > 0xff) {
|
|
|
|
y = 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
joy_x = x;
|
|
|
|
joy_y = y;
|
|
|
|
}
|
|
|
|
|
2015-06-07 04:28:12 +00:00
|
|
|
static inline void _set_current_joy_button_values(int theButton) {
|
|
|
|
if (theButton == TOUCHED_BUTTON0) {
|
|
|
|
buttons.currButtonValue0 = 0x80;
|
|
|
|
buttons.currButtonValue1 = 0;
|
|
|
|
buttons.currButtonChar = buttons.char0;
|
|
|
|
} else if (theButton == TOUCHED_BUTTON1) {
|
|
|
|
buttons.currButtonValue0 = 0;
|
|
|
|
buttons.currButtonValue1 = 0x80;
|
|
|
|
buttons.currButtonChar = buttons.char1;
|
|
|
|
} else if (theButton == TOUCHED_BOTH) {
|
|
|
|
buttons.currButtonValue0 = 0x80;
|
|
|
|
buttons.currButtonValue1 = 0x80;
|
|
|
|
buttons.currButtonChar = buttons.charBoth;
|
|
|
|
} else {
|
|
|
|
buttons.currButtonValue0 = 0;
|
|
|
|
buttons.currButtonValue1 = 0;
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
2015-04-04 19:51:32 +00:00
|
|
|
}
|
2015-04-02 02:59:38 +00:00
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
static inline void _move_button_axis(int x, int y) {
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
x -= buttons.centerX;
|
|
|
|
y -= buttons.centerY;
|
2015-04-04 19:51:32 +00:00
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
if ((y < -buttons.switchThreshold) || (y > buttons.switchThreshold)) {
|
2015-06-07 04:28:12 +00:00
|
|
|
int theButton = -1;
|
2015-04-04 19:51:32 +00:00
|
|
|
if (y < 0) {
|
2015-06-07 04:28:12 +00:00
|
|
|
theButton = buttons.northButton;
|
2015-04-04 19:51:32 +00:00
|
|
|
} else {
|
2015-06-07 04:28:12 +00:00
|
|
|
theButton = buttons.southButton;
|
2015-04-04 19:51:32 +00:00
|
|
|
}
|
2015-06-07 04:28:12 +00:00
|
|
|
|
|
|
|
_set_current_joy_button_values(theButton);
|
2015-04-04 19:51:32 +00:00
|
|
|
}
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-12 22:35:16 +00:00
|
|
|
static bool gltouchjoy_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
|
2015-04-02 02:59:38 +00:00
|
|
|
|
|
|
|
if (!isAvailable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!isEnabled) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-04-27 01:12:56 +00:00
|
|
|
if (!ownsScreen) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-04-02 02:59:38 +00:00
|
|
|
|
|
|
|
bool axisConsumed = false;
|
|
|
|
bool buttonConsumed = false;
|
|
|
|
|
|
|
|
float x = x_coords[pointer_idx];
|
|
|
|
float y = y_coords[pointer_idx];
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case TOUCH_DOWN:
|
|
|
|
case TOUCH_POINTER_DOWN:
|
2015-04-04 19:51:32 +00:00
|
|
|
if (_is_point_on_axis_side(x, y)) {
|
|
|
|
axisConsumed = true;
|
2015-04-11 19:29:44 +00:00
|
|
|
axes.trackingIndex = pointer_idx;
|
|
|
|
axes.centerX = (int)x;
|
|
|
|
axes.centerY = (int)y;
|
|
|
|
_reset_model_position(axes.model, x, y, AXIS_OBJ_HALF_W, AXIS_OBJ_HALF_H);
|
|
|
|
axes.modelDirty = true;
|
2015-04-04 19:51:32 +00:00
|
|
|
LOG("---TOUCH %sDOWN (axis index %d) center:(%d,%d) -> joy(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "),
|
2015-04-11 19:29:44 +00:00
|
|
|
axes.trackingIndex, axes.centerX, axes.centerY, joy_x, joy_y);
|
2015-04-04 19:51:32 +00:00
|
|
|
} else if (_is_point_on_button_side(x, y)) {
|
|
|
|
buttonConsumed = true;
|
2015-04-11 19:29:44 +00:00
|
|
|
buttons.trackingIndex = pointer_idx;
|
2015-06-07 04:28:12 +00:00
|
|
|
_set_current_joy_button_values(buttons.touchDownButton);
|
|
|
|
_schedule_button_tap();
|
2015-04-11 19:29:44 +00:00
|
|
|
buttons.centerX = (int)x;
|
|
|
|
buttons.centerY = (int)y;
|
|
|
|
_reset_model_position(buttons.model, x, y, BUTTON_OBJ_HALF_W, BUTTON_OBJ_HALF_H);
|
|
|
|
buttons.modelDirty = true;
|
2015-04-04 19:51:32 +00:00
|
|
|
LOG("---TOUCH %sDOWN (buttons index %d) center:(%d,%d) -> buttons(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "),
|
2015-04-11 19:29:44 +00:00
|
|
|
buttons.trackingIndex, buttons.centerX, buttons.centerY, joy_button0, joy_button1);
|
2015-04-04 19:51:32 +00:00
|
|
|
} else {
|
2015-06-07 04:28:12 +00:00
|
|
|
// ...
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOUCH_MOVE:
|
2015-04-11 19:29:44 +00:00
|
|
|
if (axes.trackingIndex >= 0) {
|
2015-04-02 02:59:38 +00:00
|
|
|
axisConsumed = true;
|
2015-04-11 19:29:44 +00:00
|
|
|
_move_joystick_axis((int)x_coords[axes.trackingIndex], (int)y_coords[axes.trackingIndex]);
|
|
|
|
LOG("---TOUCH MOVE ...tracking axis:%d (%d,%d) -> joy(0x%02X,0x%02X)", axes.trackingIndex,
|
|
|
|
(int)x_coords[axes.trackingIndex], (int)y_coords[axes.trackingIndex], joy_x, joy_y);
|
2015-04-04 19:51:32 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
if (buttons.trackingIndex >= 0) {
|
2015-04-04 19:51:32 +00:00
|
|
|
buttonConsumed = true;
|
2015-04-11 19:29:44 +00:00
|
|
|
_move_button_axis((int)x_coords[buttons.trackingIndex], (int)y_coords[buttons.trackingIndex]);
|
|
|
|
LOG("+++TOUCH MOVE ...tracking button:%d (%d,%d) -> buttons(0x%02X,0x%02X)", buttons.trackingIndex,
|
|
|
|
(int)x_coords[buttons.trackingIndex], (int)y_coords[buttons.trackingIndex], joy_button0, joy_button1);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOUCH_UP:
|
|
|
|
case TOUCH_POINTER_UP:
|
2015-04-11 19:29:44 +00:00
|
|
|
if (pointer_idx == axes.trackingIndex) {
|
2015-04-02 02:59:38 +00:00
|
|
|
axisConsumed = true;
|
2015-04-04 19:51:32 +00:00
|
|
|
bool resetIndex = false;
|
2015-04-11 19:29:44 +00:00
|
|
|
if (buttons.trackingIndex > axes.trackingIndex) {
|
2015-04-04 19:51:32 +00:00
|
|
|
// TODO FIXME : is resetting the pointer index just an Android-ism?
|
|
|
|
resetIndex = true;
|
2015-04-11 19:29:44 +00:00
|
|
|
--buttons.trackingIndex;
|
2015-04-04 19:51:32 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
axes.trackingIndex = TOUCHED_NONE;
|
2015-04-02 02:59:38 +00:00
|
|
|
joy_x = HALF_JOY_RANGE;
|
|
|
|
joy_y = HALF_JOY_RANGE;
|
2015-04-04 19:51:32 +00:00
|
|
|
LOG("---TOUCH %sUP (axis went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset buttons index!)" : ""));
|
2015-04-11 19:29:44 +00:00
|
|
|
} else if (pointer_idx == buttons.trackingIndex) {
|
2015-04-02 02:59:38 +00:00
|
|
|
buttonConsumed = true;
|
2015-04-04 19:51:32 +00:00
|
|
|
bool resetIndex = false;
|
2015-04-11 19:29:44 +00:00
|
|
|
if (axes.trackingIndex > buttons.trackingIndex) {
|
2015-04-04 19:51:32 +00:00
|
|
|
// TODO FIXME : is resetting the pointer index just an Android-ism?
|
|
|
|
resetIndex = true;
|
2015-04-11 19:29:44 +00:00
|
|
|
--axes.trackingIndex;
|
2015-04-04 19:51:32 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
buttons.trackingIndex = TOUCHED_NONE;
|
2015-04-04 19:51:32 +00:00
|
|
|
LOG("---TOUCH %sUP (buttons went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset axis index!)" : ""));
|
|
|
|
} else {
|
|
|
|
// not tracking tap/gestures originating from control-gesture portion of screen
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOUCH_CANCEL:
|
|
|
|
LOG("---TOUCH CANCEL");
|
2015-04-11 19:29:44 +00:00
|
|
|
axes.trackingIndex = TOUCHED_NONE;
|
|
|
|
buttons.trackingIndex = TOUCHED_NONE;
|
2015-04-02 02:59:38 +00:00
|
|
|
joy_x = HALF_JOY_RANGE;
|
|
|
|
joy_y = HALF_JOY_RANGE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2015-04-04 19:51:32 +00:00
|
|
|
LOG("!!! UNKNOWN TOUCH EVENT : %d", action);
|
2015-04-02 02:59:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
if (axisConsumed) {
|
2015-04-11 19:29:44 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &axes.timingBegin);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
2015-04-04 19:51:32 +00:00
|
|
|
if (buttonConsumed) {
|
2015-04-11 19:29:44 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &buttons.timingBegin);
|
2015-04-04 19:51:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (axisConsumed || buttonConsumed);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool gltouchjoy_isTouchJoystickAvailable(void) {
|
|
|
|
return isAvailable;
|
|
|
|
}
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
static void gltouchjoy_setTouchJoystickEnabled(bool enabled) {
|
2015-04-02 02:59:38 +00:00
|
|
|
isEnabled = enabled;
|
|
|
|
}
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
static void gltouchjoy_setTouchJoystickOwnsScreen(bool pwnd) {
|
|
|
|
ownsScreen = pwnd;
|
|
|
|
if (ownsScreen) {
|
|
|
|
minAlpha = minAlphaWhenOwnsScreen;
|
|
|
|
} else {
|
|
|
|
minAlpha = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _animation_showTouchJoystick(void) {
|
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &axes.timingBegin);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &buttons.timingBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _animation_hideTouchJoystick(void) {
|
|
|
|
if (!isAvailable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
axes.timingBegin = (struct timespec){ 0 };
|
|
|
|
buttons.timingBegin = (struct timespec){ 0 };
|
|
|
|
}
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
static void gltouchjoy_setTouchButtonValues(char char0, char char1) {
|
|
|
|
buttons.char0 = char0;
|
|
|
|
buttons.char1 = char1;
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-04 19:51:32 +00:00
|
|
|
static void gltouchjoy_setTouchAxisType(touchjoy_axis_type_t axisType) {
|
2015-04-11 19:29:44 +00:00
|
|
|
axes.type = axisType;
|
|
|
|
_setup_axis_object(axes.model);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 07:30:23 +00:00
|
|
|
static void gltouchjoy_setTouchAxisValues(char north, char west, char east, char south) {
|
2015-04-11 19:29:44 +00:00
|
|
|
axes.northChar = north;
|
|
|
|
axes.westChar = west;
|
|
|
|
axes.eastChar = east;
|
|
|
|
axes.southChar = south;
|
|
|
|
if (axes.type == AXIS_EMULATED_KEYBOARD) {
|
|
|
|
_setup_axis_object(axes.model);
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-18 05:12:13 +00:00
|
|
|
__attribute__((constructor(CTOR_PRIORITY_LATE)))
|
2015-04-02 02:59:38 +00:00
|
|
|
static void _init_gltouchjoy(void) {
|
|
|
|
LOG("Registering OpenGL software touch joystick");
|
|
|
|
|
2015-04-11 19:29:44 +00:00
|
|
|
axes.centerX = 240;
|
|
|
|
axes.centerY = 160;
|
|
|
|
axes.trackingIndex = TOUCHED_NONE;
|
|
|
|
axes.type = AXIS_EMULATED_DEVICE;
|
|
|
|
axes.northChar = MOUSETEXT_UP;
|
|
|
|
axes.westChar = MOUSETEXT_LEFT;
|
|
|
|
axes.eastChar = MOUSETEXT_RIGHT;
|
|
|
|
axes.southChar = MOUSETEXT_DOWN;
|
|
|
|
|
|
|
|
buttons.centerX = 240;
|
|
|
|
buttons.centerY = 160;
|
|
|
|
buttons.trackingIndex = TOUCHED_NONE;
|
|
|
|
buttons.touchDownButton = TOUCHED_BUTTON0;
|
|
|
|
buttons.northButton = TOUCHED_BOTH;
|
|
|
|
buttons.southButton = TOUCHED_BUTTON1;
|
|
|
|
buttons.char0 = MOUSETEXT_OPENAPPLE;
|
|
|
|
buttons.char1 = MOUSETEXT_CLOSEDAPPLE;
|
|
|
|
buttons.charBoth = '+';
|
|
|
|
buttons.activeChar = MOUSETEXT_OPENAPPLE;
|
|
|
|
buttons.switchThreshold = BUTTON_SWITCH_THRESHOLD_DEFAULT;
|
|
|
|
|
2015-06-07 04:28:12 +00:00
|
|
|
buttons.tapDelayThreadId = 0;
|
|
|
|
buttons.tapDelayMutex = (pthread_mutex_t){ 0 };
|
|
|
|
buttons.tapDelayCond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
|
|
|
buttons.tapDelayNanos = BUTTON_TAP_DELAY_NANOS_DEFAULT;
|
|
|
|
|
2015-04-27 01:12:56 +00:00
|
|
|
video_backend->animation_showTouchJoystick = &_animation_showTouchJoystick;
|
|
|
|
video_backend->animation_hideTouchJoystick = &_animation_hideTouchJoystick;
|
|
|
|
|
2015-04-02 02:59:38 +00:00
|
|
|
joydriver_isTouchJoystickAvailable = &gltouchjoy_isTouchJoystickAvailable;
|
2015-04-27 01:12:56 +00:00
|
|
|
joydriver_setTouchJoystickEnabled = &gltouchjoy_setTouchJoystickEnabled;
|
|
|
|
joydriver_setTouchJoystickOwnsScreen = &gltouchjoy_setTouchJoystickOwnsScreen;
|
2015-04-02 02:59:38 +00:00
|
|
|
joydriver_setTouchButtonValues = &gltouchjoy_setTouchButtonValues;
|
|
|
|
joydriver_setTouchAxisType = &gltouchjoy_setTouchAxisType;
|
|
|
|
joydriver_setTouchAxisValues = &gltouchjoy_setTouchAxisValues;
|
|
|
|
|
2015-04-12 22:35:16 +00:00
|
|
|
glnode_registerNode(RENDER_LOW, (GLNode){
|
2015-04-12 23:20:01 +00:00
|
|
|
.setup = &gltouchjoy_setup,
|
|
|
|
.shutdown = &gltouchjoy_shutdown,
|
|
|
|
.render = &gltouchjoy_render,
|
|
|
|
.reshape = &gltouchjoy_reshape,
|
|
|
|
.onTouchEvent = &gltouchjoy_onTouchEvent,
|
2015-04-12 22:35:16 +00:00
|
|
|
});
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gldriver_joystick_reset(void) {
|
2015-04-11 07:30:23 +00:00
|
|
|
#warning FIXME TODO expunge this olde API ...
|
2015-04-02 02:59:38 +00:00
|
|
|
}
|
2015-04-11 19:29:44 +00:00
|
|
|
|