mirror of
https://github.com/mauiaaron/apple2.git
synced 2025-01-10 23:29:43 +00:00
Refactor GL Touch Joystick (PART I Native changes)
- Move touch-handling code for "regular" touch joystick into separate file - Add new touch-handling code for "keypad" touch joystick. This is a somewhat complex state-machine to determine which key to press depending on a full keypad rosette. - Adds a callback to vm.c to hook into keyboard read callback. Allows us to immediately queue the next key if the keypad touch joystick is long-pressed for either axis or button keys.
This commit is contained in:
parent
77e96ba1ec
commit
5c3791f5dc
@ -109,7 +109,7 @@ jint Java_org_deadc0de_apple2ix_Apple2Preferences_nativeGetCurrentTouchDevice(JN
|
||||
touchjoy_variant_t variant = joydriver_getTouchVariant();
|
||||
if (variant == EMULATED_JOYSTICK) {
|
||||
return ANDROID_TOUCH_JOYSTICK;
|
||||
} else if (variant == EMULATED_JOYSTICK) {
|
||||
} else if (variant == EMULATED_KEYPAD) {
|
||||
return ANDROID_TOUCH_JOYSTICK_KEYPAD;
|
||||
}
|
||||
} else if (keydriver_ownsScreen()) {
|
||||
@ -147,7 +147,19 @@ void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickButtonTy
|
||||
return;
|
||||
}
|
||||
|
||||
joydriver_setTouchButtonTypes((touchjoy_button_type_t)touchDownButton, (touchjoy_button_type_t)northButton, (touchjoy_button_type_t)southButton);
|
||||
uint8_t rosetteChars[ROSETTE_COLS * ROSETTE_ROWS];
|
||||
int rosetteScancodes[ROSETTE_COLS * ROSETTE_ROWS];
|
||||
rosetteChars[ROSETTE_NORTHWEST] = ' '; rosetteScancodes[ROSETTE_NORTHWEST] = -1;
|
||||
rosetteChars[ROSETTE_NORTH] = (uint8_t)MOUSETEXT_UP; rosetteScancodes[ROSETTE_NORTH] = -1;
|
||||
rosetteChars[ROSETTE_NORTHEAST] = ' '; rosetteScancodes[ROSETTE_NORTHEAST] = -1;
|
||||
rosetteChars[ROSETTE_WEST] = (uint8_t)MOUSETEXT_LEFT; rosetteScancodes[ROSETTE_WEST] = -1;
|
||||
rosetteChars[ROSETTE_CENTER] = '+'; rosetteScancodes[ROSETTE_CENTER] = -1;
|
||||
rosetteChars[ROSETTE_EAST] = (uint8_t)MOUSETEXT_RIGHT; rosetteScancodes[ROSETTE_EAST] = -1;
|
||||
rosetteChars[ROSETTE_SOUTHWEST] = ' '; rosetteScancodes[ROSETTE_SOUTHWEST] = -1;
|
||||
rosetteChars[ROSETTE_SOUTH] = (uint8_t)MOUSETEXT_DOWN; rosetteScancodes[ROSETTE_SOUTH] = -1;
|
||||
rosetteChars[ROSETTE_SOUTHEAST] = ' '; rosetteScancodes[ROSETTE_SOUTHEAST] = -1;
|
||||
joydriver_setTouchAxisTypes(rosetteChars, rosetteScancodes);
|
||||
joydriver_setTouchButtonTypes((touchjoy_button_type_t)touchDownButton, -1, (touchjoy_button_type_t)northButton, -1, (touchjoy_button_type_t)southButton, -1);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickTapDelay(JNIEnv *env, jclass cls, jfloat secs) {
|
||||
@ -175,6 +187,41 @@ void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchJoystickSetAxisOnLe
|
||||
joydriver_setAxisOnLeft(axisIsOnLeft);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchJoystickSetKeypadTypes(JNIEnv *env, jclass cls,
|
||||
jintArray jRosetteChars, jintArray jRosetteScans, jintArray jButtonsChars, jintArray jButtonsScans)
|
||||
{
|
||||
jint *rosetteChars = (*env)->GetIntArrayElements(env, jRosetteChars, 0);
|
||||
jint *rosetteScans = (*env)->GetIntArrayElements(env, jRosetteScans, 0);
|
||||
jint *buttonsChars = (*env)->GetIntArrayElements(env, jButtonsChars, 0);
|
||||
jint *buttonsScans = (*env)->GetIntArrayElements(env, jButtonsScans, 0);
|
||||
|
||||
LOG("nativeTouchJoystickSetKeypadValues() : NW:%c/%d, N:%c/%d, NE:%c/%d, ... SWIPEUP:%c/%d",
|
||||
(char)rosetteChars[0], rosetteScans[0], (char)rosetteChars[1], rosetteScans[1], (char)rosetteChars[2], rosetteScans[2],
|
||||
(char)buttonsChars[1], buttonsScans[1]);
|
||||
LOG("nativeTouchJoystickSetKeypadValues() : W:%c/%d, C:%c/%d, E:%c/%d, ... TAP:%c/%d",
|
||||
(char)rosetteChars[3], rosetteScans[3], (char)rosetteChars[4], rosetteScans[4], (char)rosetteChars[5], rosetteScans[5],
|
||||
(char)buttonsChars[0], buttonsScans[0]);
|
||||
LOG("nativeTouchJoystickSetKeypadValues() : SW:%c/%d, S:%c/%d, SE:%c/%d, ... SWIPEDN:%c/%d",
|
||||
(char)rosetteChars[6], rosetteScans[6], (char)rosetteChars[7], rosetteScans[7], (char)rosetteChars[8], rosetteScans[8],
|
||||
(char)buttonsChars[2], buttonsScans[2]);
|
||||
|
||||
// we could just pass these as jcharArray ... but this isn't a tight loop =P
|
||||
uint8_t actualChars[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
for (unsigned int i=0; i<(ROSETTE_ROWS * ROSETTE_COLS); i++) {
|
||||
actualChars[i] = (uint8_t)rosetteChars[i];
|
||||
}
|
||||
joydriver_setTouchAxisTypes(actualChars, rosetteScans);
|
||||
joydriver_setTouchButtonTypes(
|
||||
(touchjoy_button_type_t)buttonsChars[0], buttonsScans[0],
|
||||
(touchjoy_button_type_t)buttonsChars[1], buttonsScans[1],
|
||||
(touchjoy_button_type_t)buttonsChars[2], buttonsScans[2]);
|
||||
|
||||
(*env)->ReleaseIntArrayElements(env, jRosetteChars, rosetteChars, 0);
|
||||
(*env)->ReleaseIntArrayElements(env, jRosetteScans, rosetteScans, 0);
|
||||
(*env)->ReleaseIntArrayElements(env, jButtonsChars, buttonsChars, 0);
|
||||
(*env)->ReleaseIntArrayElements(env, jButtonsScans, buttonsScans, 0);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchJoystickBeginCalibrationMode(JNIEnv *env, jclass cls) {
|
||||
LOG("nativeTouchJoystickBeginCalibrationMode() ...");
|
||||
joydriver_beginCalibration();
|
||||
|
@ -15,6 +15,8 @@ APPLE2_VIDEO_SRC = \
|
||||
$(APPLE2_SRC_PATH)/video/glhudmodel.c \
|
||||
$(APPLE2_SRC_PATH)/video/glalert.c \
|
||||
$(APPLE2_SRC_PATH)/video/gltouchjoy.c \
|
||||
$(APPLE2_SRC_PATH)/video/gltouchjoy_joy.c \
|
||||
$(APPLE2_SRC_PATH)/video/gltouchjoy_kpad.c \
|
||||
$(APPLE2_SRC_PATH)/video/gltouchkbd.c \
|
||||
$(APPLE2_SRC_PATH)/video/gltouchmenu.c \
|
||||
$(APPLE2_SRC_PATH)/video_util/matrixUtil.c \
|
||||
|
@ -261,17 +261,20 @@ bool (*joydriver_isTouchJoystickAvailable)(void) = NULL;
|
||||
void (*joydriver_setTouchJoystickEnabled)(bool enabled) = NULL;
|
||||
void (*joydriver_setTouchJoystickOwnsScreen)(bool pwnd) = NULL;
|
||||
bool (*joydriver_ownsScreen)(void) = NULL;
|
||||
void (*joydriver_setTouchButtonValues)(char button0Val, char button1Val, char buttonBothVal) = NULL;
|
||||
void (*joydriver_setTouchButtonTypes)(touchjoy_button_type_t down, touchjoy_button_type_t north, touchjoy_button_type_t south) = NULL;
|
||||
void (*joydriver_setTouchButtonTypes)(
|
||||
touchjoy_button_type_t touchDownChar, int downScancode,
|
||||
touchjoy_button_type_t northChar, int northScancode,
|
||||
touchjoy_button_type_t southChar, int southScancode) = NULL;
|
||||
void (*joydriver_setTapDelay)(float secs) = NULL;
|
||||
void (*joydriver_setTouchAxisSensitivity)(float multiplier) = NULL;
|
||||
void (*joydriver_setButtonSwitchThreshold)(int delta) = NULL;
|
||||
void (*joydriver_setTouchVariant)(touchjoy_variant_t variant) = NULL;
|
||||
touchjoy_variant_t (*joydriver_getTouchVariant)(void) = NULL;
|
||||
void (*joydriver_setTouchAxisValues)(char up, char left, char right, char down) = NULL;
|
||||
void (*joydriver_setTouchAxisTypes)(uint8_t rosetteChars[(ROSETTE_ROWS * ROSETTE_COLS)], int rosetteScancodes[(ROSETTE_ROWS * ROSETTE_COLS)]) = NULL;
|
||||
void (*joydriver_setScreenDivision)(float division) = NULL;
|
||||
void (*joydriver_setAxisOnLeft)(bool axisIsOnLeft) = NULL;
|
||||
void (*joydriver_beginCalibration)(void) = NULL;
|
||||
void (*joydriver_endCalibration)(void) = NULL;
|
||||
bool (*joydriver_isCalibrating)(void) = NULL;
|
||||
#endif
|
||||
|
||||
|
@ -44,8 +44,24 @@ typedef enum touchjoy_button_type_t {
|
||||
TOUCH_BUTTON0 = 0,
|
||||
TOUCH_BUTTON1,
|
||||
TOUCH_BOTH,
|
||||
// --or-- an ASCII/fonttext value ...
|
||||
} touchjoy_button_type_t;
|
||||
|
||||
#define ROSETTE_ROWS 3
|
||||
#define ROSETTE_COLS 3
|
||||
|
||||
enum {
|
||||
ROSETTE_NORTHWEST=0,
|
||||
ROSETTE_NORTH,
|
||||
ROSETTE_NORTHEAST,
|
||||
ROSETTE_WEST,
|
||||
ROSETTE_CENTER,
|
||||
ROSETTE_EAST,
|
||||
ROSETTE_SOUTHWEST,
|
||||
ROSETTE_SOUTH,
|
||||
ROSETTE_SOUTHEAST,
|
||||
};
|
||||
|
||||
// is the touch joystick available
|
||||
extern bool (*joydriver_isTouchJoystickAvailable)(void);
|
||||
|
||||
@ -58,16 +74,24 @@ extern void (*joydriver_setTouchJoystickOwnsScreen)(bool pwnd);
|
||||
// query touch screen ownership
|
||||
extern bool (*joydriver_ownsScreen)(void);
|
||||
|
||||
// set the joystick button visuals (these values are also fired for keyboard variant)
|
||||
extern void (*joydriver_setTouchButtonValues)(char button0Val, char button1Val, char buttonBothVal);
|
||||
/*
|
||||
* set the joystick button types/visuals (scancodes are fired for EMULATED_KEYPAD variant)
|
||||
*
|
||||
* - for EMULATED_JOYSTICK, there is an implicit extra layer-of-indirection for the touchjoy_button_type_t, which maps
|
||||
* to the open apple, closed apple, or "both" visual keys
|
||||
*
|
||||
* - for EMULATED_KEYPAD, the touchjoy_button_type_t is the displayed visual (as ASCII value and lookup into font
|
||||
* table)
|
||||
*/
|
||||
extern void (*joydriver_setTouchButtonTypes)(
|
||||
touchjoy_button_type_t touchDownChar, int downScancode,
|
||||
touchjoy_button_type_t northChar, int northScancode,
|
||||
touchjoy_button_type_t southChar, int southScancode);
|
||||
|
||||
// set the joystick button types (for joystick variant)
|
||||
extern void (*joydriver_setTouchButtonTypes)(touchjoy_button_type_t down, touchjoy_button_type_t north, touchjoy_button_type_t south);
|
||||
|
||||
// set the tap delay (to differentiate between single tap and north/south/etc swipe)
|
||||
// set the button tap delay (to differentiate between single tap and north/south/etc swipe)
|
||||
extern void (*joydriver_setTapDelay)(float secs);
|
||||
|
||||
// set the touch axis sensitivity multiplier
|
||||
// set the sensitivity multiplier
|
||||
extern void (*joydriver_setTouchAxisSensitivity)(float multiplier);
|
||||
|
||||
// set the touch button switch threshold
|
||||
@ -79,8 +103,8 @@ extern void (*joydriver_setTouchVariant)(touchjoy_variant_t variant);
|
||||
// get the joystick variant
|
||||
extern touchjoy_variant_t (*joydriver_getTouchVariant)(void);
|
||||
|
||||
// set the axis visuals (these key values are also fired for keyboard variant)
|
||||
extern void (*joydriver_setTouchAxisValues)(char north, char west, char east, char south);
|
||||
// set the axis visuals (scancodes are fired for EMULATED_KEYPAD variant)
|
||||
extern void (*joydriver_setTouchAxisTypes)(uint8_t rosetteChars[(ROSETTE_ROWS * ROSETTE_COLS)], int rosetteScancodes[(ROSETTE_ROWS * ROSETTE_COLS)]);
|
||||
|
||||
// set screen divide between axis and buttons
|
||||
extern void (*joydriver_setScreenDivision)(float division);
|
||||
@ -94,6 +118,9 @@ extern void (*joydriver_beginCalibration)(void);
|
||||
// end calibration mode
|
||||
extern void (*joydriver_endCalibration)(void);
|
||||
|
||||
// end calibration mode
|
||||
extern bool (*joydriver_isCalibrating)(void);
|
||||
|
||||
#endif // INTERFACE_TOUCH
|
||||
|
||||
#endif // whole file
|
||||
|
@ -494,5 +494,6 @@ bool (*keydriver_isTouchKeyboardAvailable)(void) = NULL;
|
||||
void (*keydriver_setTouchKeyboardEnabled)(bool enabled) = NULL;
|
||||
void (*keydriver_setTouchKeyboardOwnsScreen)(bool pwnd) = NULL;
|
||||
bool (*keydriver_ownsScreen)(void) = NULL;
|
||||
void (*keydriver_keyboardReadCallback)(void) = NULL;
|
||||
#endif
|
||||
|
||||
|
@ -160,6 +160,9 @@ extern void (*keydriver_setTouchKeyboardOwnsScreen)(bool pwnd);
|
||||
|
||||
// query touch screen ownership
|
||||
extern bool (*keydriver_ownsScreen)(void);
|
||||
|
||||
// keyboard read callback
|
||||
extern void (*keydriver_keyboardReadCallback)(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -9,10 +9,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "video/glvideo.h"
|
||||
#include "video/glhudmodel.h"
|
||||
#include "video/glnode.h"
|
||||
#include "video/gltouchjoy.h"
|
||||
|
||||
#if !INTERFACE_TOUCH
|
||||
#error this is a touch interface module, possibly you mean to not compile this at all?
|
||||
@ -52,18 +49,15 @@
|
||||
#define BUTTON_SWITCH_THRESHOLD_DEFAULT 22
|
||||
#define BUTTON_TAP_DELAY_NANOS_DEFAULT 50000000
|
||||
|
||||
// module globals
|
||||
GLTouchJoyGlobals joyglobals = { 0 };
|
||||
GLTouchJoyAxes axes = { 0 };
|
||||
GLTouchJoyButtons buttons = { 0 };
|
||||
|
||||
static struct {
|
||||
bool isAvailable; // Were there any OpenGL/memory errors on gltouchjoy initialization?
|
||||
bool isShuttingDown;
|
||||
bool isCalibrating; // Are we running in calibration mode?
|
||||
bool isEnabled; // Does player want touchjoy enabled?
|
||||
bool ownsScreen; // Does the touchjoy currently own the screen?
|
||||
float minAlphaWhenOwnsScreen;
|
||||
float minAlpha;
|
||||
float screenDivider;
|
||||
bool axisIsOnLeft;
|
||||
} joyglobals = { 0 };
|
||||
GLTouchJoyVariant *joys;
|
||||
GLTouchJoyVariant *kpad;
|
||||
GLTouchJoyVariant *curr;
|
||||
} variant = { 0 };
|
||||
|
||||
// viewport touch
|
||||
static struct {
|
||||
@ -85,60 +79,6 @@ static struct {
|
||||
// TODO FIXME : support 2-players!
|
||||
} touchport = { 0 };
|
||||
|
||||
// touch axis variables
|
||||
|
||||
static struct {
|
||||
|
||||
GLModel *model;
|
||||
uint8_t northChar;
|
||||
uint8_t westChar;
|
||||
uint8_t eastChar;
|
||||
uint8_t southChar;
|
||||
bool modelDirty;
|
||||
|
||||
touchjoy_variant_t type;
|
||||
|
||||
int centerX;
|
||||
int centerY;
|
||||
float multiplier;
|
||||
int trackingIndex;
|
||||
struct timespec timingBegin;
|
||||
|
||||
} axes = { 0 };
|
||||
|
||||
// button object variables
|
||||
|
||||
static struct {
|
||||
GLModel *model;
|
||||
uint8_t activeChar;
|
||||
bool modelDirty;
|
||||
|
||||
touchjoy_button_type_t touchDownButton;
|
||||
touchjoy_button_type_t northButton;
|
||||
touchjoy_button_type_t southButton;
|
||||
|
||||
uint8_t char0;
|
||||
uint8_t char1;
|
||||
uint8_t charBoth;
|
||||
|
||||
int switchThreshold;
|
||||
|
||||
int centerX;
|
||||
int centerY;
|
||||
int trackingIndex;
|
||||
struct timespec timingBegin;
|
||||
|
||||
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;
|
||||
|
||||
} buttons = { 0 };
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#warning FIXME TODO ... this can become a common helper function ...
|
||||
@ -192,10 +132,12 @@ static void _setup_axis_object(GLModel *parent) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for (unsigned int i=0; i<ROSETTE_ROWS; i++) {
|
||||
for (unsigned int j=0; j<ROSETTE_COLS; j++) {
|
||||
((hudElement->tpl)+(row*i*2))[j*2] = axes.rosetteChars[(i*ROSETTE_ROWS)+j];
|
||||
}
|
||||
}
|
||||
|
||||
glhud_setupDefault(parent);
|
||||
}
|
||||
@ -276,17 +218,22 @@ static void *_button_tap_delayed_thread(void *dummyptr) {
|
||||
nanosleep(&ts, NULL);
|
||||
pthread_mutex_lock(&buttons.tapDelayMutex);
|
||||
|
||||
// set the emulator's joystick button values until touch up
|
||||
// wait until touch up/cancel
|
||||
do {
|
||||
joy_button0 = buttons.currButtonValue0;
|
||||
joy_button1 = buttons.currButtonValue1;
|
||||
_setup_button_object_with_char(buttons.currButtonChar);
|
||||
|
||||
// now set the emulator's joystick button values (or keypad value)
|
||||
uint8_t displayChar = variant.curr->buttonPress();
|
||||
_setup_button_object_with_char(displayChar);
|
||||
|
||||
if ( (buttons.trackingIndex == TOUCH_NONE) || joyglobals.isShuttingDown) {
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_cond_wait(&buttons.tapDelayCond, &buttons.tapDelayMutex);
|
||||
|
||||
if ( (buttons.trackingIndex == TOUCH_NONE) || joyglobals.isShuttingDown) {
|
||||
break;
|
||||
}
|
||||
TOUCH_JOY_LOG(">>> [DELAYEDTAP] looping ...");
|
||||
} while (1);
|
||||
|
||||
@ -299,8 +246,8 @@ static void *_button_tap_delayed_thread(void *dummyptr) {
|
||||
nanosleep(&ts, NULL);
|
||||
pthread_mutex_lock(&buttons.tapDelayMutex);
|
||||
|
||||
joy_button0 = 0x0;
|
||||
joy_button1 = 0x0;
|
||||
variant.curr->buttonRelease();
|
||||
|
||||
TOUCH_JOY_LOG(">>> [DELAYEDTAP] end ...");
|
||||
} while (1);
|
||||
|
||||
@ -316,6 +263,9 @@ static void *_button_tap_delayed_thread(void *dummyptr) {
|
||||
static void gltouchjoy_setup(void) {
|
||||
LOG("gltouchjoy_setup ...");
|
||||
|
||||
variant.kpad->resetState();
|
||||
variant.joys->resetState();
|
||||
|
||||
mdlDestroyModel(&axes.model);
|
||||
mdlDestroyModel(&buttons.model);
|
||||
|
||||
@ -365,6 +315,9 @@ static void gltouchjoy_shutdown(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
variant.kpad->resetState();
|
||||
variant.joys->resetState();
|
||||
|
||||
joyglobals.isAvailable = false;
|
||||
|
||||
joyglobals.isShuttingDown = true;
|
||||
@ -387,6 +340,9 @@ static void gltouchjoy_render(void) {
|
||||
if (!joyglobals.isEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!joyglobals.ownsScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
glViewport(0, 0, touchport.width, touchport.height); // NOTE : show these HUD elements beyond the A2 framebuffer dimensions
|
||||
|
||||
@ -475,11 +431,11 @@ static void gltouchjoy_resetJoystick(void) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
static inline bool _is_point_on_axis_side(float x, float y) {
|
||||
static inline bool _is_point_on_axis_side(int x, int y) {
|
||||
return (x >= touchport.axisX && x <= touchport.axisXMax && y >= touchport.axisY && y <= touchport.axisYMax);
|
||||
}
|
||||
|
||||
static inline bool _is_point_on_button_side(float x, float y) {
|
||||
static inline bool _is_point_on_button_side(int x, int y) {
|
||||
return (x >= touchport.buttonX && x <= touchport.buttonXMax && y >= touchport.buttonY && y <= touchport.buttonYMax);
|
||||
}
|
||||
|
||||
@ -507,72 +463,91 @@ static inline void _reset_model_position(GLModel *model, float touchX, float tou
|
||||
quad[12+1] = centerY+objHalfH;
|
||||
}
|
||||
|
||||
static inline void _move_joystick_axis(int x, int y) {
|
||||
static inline void _axis_touch_down(int x, int y) {
|
||||
axes.centerX = x;
|
||||
axes.centerY = y;
|
||||
|
||||
x = (x - axes.centerX);
|
||||
y = (y - axes.centerY);
|
||||
_reset_model_position(axes.model, x, y, AXIS_OBJ_HALF_W, AXIS_OBJ_HALF_H);
|
||||
axes.modelDirty = true;
|
||||
|
||||
if (axes.multiplier != 1.f) {
|
||||
x = (int) ((float)x * axes.multiplier);
|
||||
y = (int) ((float)y * axes.multiplier);
|
||||
}
|
||||
|
||||
x += 0x80;
|
||||
y += 0x80;
|
||||
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
}
|
||||
if (x > 0xff) {
|
||||
x = 0xff;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
if (y > 0xff) {
|
||||
y = 0xff;
|
||||
}
|
||||
|
||||
joy_x = x;
|
||||
joy_y = y;
|
||||
TOUCH_JOY_LOG("---TOUCH %sDOWN (axis index %d) center:(%d,%d) -> joy(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "), axes.trackingIndex, axes.centerX, axes.centerY, joy_x, joy_y);
|
||||
variant.curr->axisDown();
|
||||
}
|
||||
|
||||
static inline void _set_current_joy_button_values(int theButton) {
|
||||
if (theButton == TOUCH_BUTTON0) {
|
||||
buttons.currButtonValue0 = 0x80;
|
||||
buttons.currButtonValue1 = 0;
|
||||
buttons.currButtonChar = buttons.char0;
|
||||
} else if (theButton == TOUCH_BUTTON1) {
|
||||
buttons.currButtonValue0 = 0;
|
||||
buttons.currButtonValue1 = 0x80;
|
||||
buttons.currButtonChar = buttons.char1;
|
||||
} else if (theButton == TOUCH_BOTH) {
|
||||
buttons.currButtonValue0 = 0x80;
|
||||
buttons.currButtonValue1 = 0x80;
|
||||
buttons.currButtonChar = buttons.charBoth;
|
||||
} else {
|
||||
buttons.currButtonValue0 = 0;
|
||||
buttons.currButtonValue1 = 0;
|
||||
static inline void _button_touch_down(int x, int y) {
|
||||
variant.curr->setCurrButtonValue(buttons.touchDownChar, buttons.touchDownScancode);
|
||||
_signal_tap_delay();
|
||||
|
||||
buttons.centerX = x;
|
||||
buttons.centerY = y;
|
||||
|
||||
_reset_model_position(buttons.model, x, y, BUTTON_OBJ_HALF_W, BUTTON_OBJ_HALF_H);
|
||||
buttons.modelDirty = true;
|
||||
|
||||
TOUCH_JOY_LOG("---TOUCH %sDOWN (buttons index %d) center:(%d,%d) -> buttons(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "), buttons.trackingIndex, buttons.centerX, buttons.centerY, joy_button0, joy_button1);
|
||||
}
|
||||
|
||||
static inline void _axis_move(int x, int y) {
|
||||
x = (x - axes.centerX);
|
||||
y = (y - axes.centerY);
|
||||
TOUCH_JOY_LOG("---TOUCH MOVE ...tracking axis:%d (%d,%d) -> joy(0x%02X,0x%02X)", axes.trackingIndex, x, y, joy_x, joy_y);
|
||||
variant.curr->axisMove(x, y);
|
||||
}
|
||||
|
||||
static inline void _button_move(int x, int y) {
|
||||
x -= buttons.centerX;
|
||||
y -= buttons.centerY;
|
||||
if ((y < -joyglobals.switchThreshold) || (y > joyglobals.switchThreshold)) {
|
||||
touchjoy_button_type_t theButtonChar = -1;
|
||||
int theButtonScancode = -1;
|
||||
if (y < 0) {
|
||||
theButtonChar = buttons.northChar;
|
||||
theButtonScancode = buttons.northScancode;
|
||||
} else {
|
||||
theButtonChar = buttons.southChar;
|
||||
theButtonScancode = buttons.southScancode;
|
||||
}
|
||||
|
||||
variant.curr->setCurrButtonValue(theButtonChar, theButtonScancode);
|
||||
_signal_tap_delay();
|
||||
TOUCH_JOY_LOG("+++TOUCH MOVE ...tracking button:%d (%d,%d) -> buttons(0x%02X,0x%02X)", buttons.trackingIndex, x, y, joy_button0, joy_button1);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _axis_touch_up(int x, int y) {
|
||||
x = (x - axes.centerX);
|
||||
y = (y - axes.centerY);
|
||||
if (buttons.trackingIndex > axes.trackingIndex) {
|
||||
--buttons.trackingIndex;
|
||||
}
|
||||
variant.curr->axisUp(x, y);
|
||||
#if DEBUG_TOUCH_JOY
|
||||
bool resetIndex = false;
|
||||
if (buttons.trackingIndex > axes.trackingIndex) {
|
||||
// TODO FIXME : is resetting the pointer index just an Android-ism?
|
||||
resetIndex = true;
|
||||
}
|
||||
TOUCH_JOY_LOG("---TOUCH %sUP (axis went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset buttons index!)" : ""));
|
||||
#endif
|
||||
axes.trackingIndex = TOUCH_NONE;
|
||||
}
|
||||
|
||||
static inline void _button_touch_up(void) {
|
||||
#if DEBUG_TOUCH_JOY
|
||||
bool resetIndex = false;
|
||||
if (axes.trackingIndex > buttons.trackingIndex) {
|
||||
// TODO FIXME : is resetting the pointer index just an Android-ism?
|
||||
resetIndex = true;
|
||||
}
|
||||
TOUCH_JOY_LOG("---TOUCH %sUP (buttons went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset axis index!)" : ""));
|
||||
#endif
|
||||
if (axes.trackingIndex > buttons.trackingIndex) {
|
||||
--axes.trackingIndex;
|
||||
}
|
||||
buttons.trackingIndex = TOUCH_NONE;
|
||||
_signal_tap_delay();
|
||||
}
|
||||
|
||||
static inline void _move_button_axis(int x, int y) {
|
||||
|
||||
x -= buttons.centerX;
|
||||
y -= buttons.centerY;
|
||||
|
||||
if ((y < -buttons.switchThreshold) || (y > buttons.switchThreshold)) {
|
||||
int theButton = -1;
|
||||
if (y < 0) {
|
||||
theButton = buttons.northButton;
|
||||
} else {
|
||||
theButton = buttons.southButton;
|
||||
}
|
||||
|
||||
_set_current_joy_button_values(theButton);
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
|
||||
|
||||
@ -589,87 +564,63 @@ static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int point
|
||||
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:
|
||||
if (_is_point_on_axis_side(x, y)) {
|
||||
axisConsumed = true;
|
||||
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;
|
||||
TOUCH_JOY_LOG("---TOUCH %sDOWN (axis index %d) center:(%d,%d) -> joy(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "),
|
||||
axes.trackingIndex, axes.centerX, axes.centerY, joy_x, joy_y);
|
||||
} else if (_is_point_on_button_side(x, y)) {
|
||||
buttonConsumed = true;
|
||||
buttons.trackingIndex = pointer_idx;
|
||||
_set_current_joy_button_values(buttons.touchDownButton);
|
||||
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;
|
||||
TOUCH_JOY_LOG("---TOUCH %sDOWN (buttons index %d) center:(%d,%d) -> buttons(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "),
|
||||
buttons.trackingIndex, buttons.centerX, buttons.centerY, joy_button0, joy_button1);
|
||||
} else {
|
||||
// ...
|
||||
{
|
||||
int x = (int)x_coords[pointer_idx];
|
||||
int y = (int)y_coords[pointer_idx];
|
||||
if (_is_point_on_axis_side(x, y)) {
|
||||
axisConsumed = true;
|
||||
axes.trackingIndex = pointer_idx;
|
||||
_axis_touch_down(x, y);
|
||||
} else if (_is_point_on_button_side(x, y)) {
|
||||
buttonConsumed = true;
|
||||
buttons.trackingIndex = pointer_idx;
|
||||
_button_touch_down(x, y);
|
||||
} else {
|
||||
assert(false && "should either be on axis or button side");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TOUCH_MOVE:
|
||||
if (axes.trackingIndex >= 0) {
|
||||
axisConsumed = true;
|
||||
_move_joystick_axis((int)x_coords[axes.trackingIndex], (int)y_coords[axes.trackingIndex]);
|
||||
TOUCH_JOY_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);
|
||||
int x = (int)x_coords[axes.trackingIndex];
|
||||
int y = (int)y_coords[axes.trackingIndex];
|
||||
_axis_move(x, y);
|
||||
}
|
||||
if (buttons.trackingIndex >= 0) {
|
||||
buttonConsumed = true;
|
||||
_move_button_axis((int)x_coords[buttons.trackingIndex], (int)y_coords[buttons.trackingIndex]);
|
||||
TOUCH_JOY_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);
|
||||
int x = (int)x_coords[buttons.trackingIndex];
|
||||
int y = (int)y_coords[buttons.trackingIndex];
|
||||
_button_move(x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOUCH_UP:
|
||||
case TOUCH_POINTER_UP:
|
||||
if (pointer_idx == axes.trackingIndex) {
|
||||
axisConsumed = true;
|
||||
bool resetIndex = false;
|
||||
if (buttons.trackingIndex > axes.trackingIndex) {
|
||||
// TODO FIXME : is resetting the pointer index just an Android-ism?
|
||||
resetIndex = true;
|
||||
--buttons.trackingIndex;
|
||||
}
|
||||
axes.trackingIndex = TOUCH_NONE;
|
||||
joy_x = HALF_JOY_RANGE;
|
||||
joy_y = HALF_JOY_RANGE;
|
||||
TOUCH_JOY_LOG("---TOUCH %sUP (axis went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset buttons index!)" : ""));
|
||||
int x = (int)x_coords[axes.trackingIndex];
|
||||
int y = (int)y_coords[axes.trackingIndex];
|
||||
_axis_touch_up(x, y);
|
||||
} else if (pointer_idx == buttons.trackingIndex) {
|
||||
buttonConsumed = true;
|
||||
bool resetIndex = false;
|
||||
if (axes.trackingIndex > buttons.trackingIndex) {
|
||||
// TODO FIXME : is resetting the pointer index just an Android-ism?
|
||||
resetIndex = true;
|
||||
--axes.trackingIndex;
|
||||
}
|
||||
buttons.trackingIndex = TOUCH_NONE;
|
||||
_signal_tap_delay();
|
||||
TOUCH_JOY_LOG("---TOUCH %sUP (buttons went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset axis index!)" : ""));
|
||||
_button_touch_up();
|
||||
} else {
|
||||
// not tracking tap/gestures originating from control-gesture portion of screen
|
||||
if (pointer_count == 1) {
|
||||
// last pointer up completely resets state
|
||||
LOG("!!!! ... RESETTING TOUCH JOYSTICK STATE MACHINE");
|
||||
variant.joys->resetState();
|
||||
variant.kpad->resetState();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TOUCH_CANCEL:
|
||||
LOG("---TOUCH CANCEL");
|
||||
axes.trackingIndex = TOUCH_NONE;
|
||||
buttons.trackingIndex = TOUCH_NONE;
|
||||
joy_x = HALF_JOY_RANGE;
|
||||
joy_y = HALF_JOY_RANGE;
|
||||
variant.joys->resetState();
|
||||
variant.kpad->resetState();
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -724,16 +675,19 @@ static void _animation_hideTouchJoystick(void) {
|
||||
buttons.timingBegin = (struct timespec){ 0 };
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchButtonValues(char char0, char char1, char charBoth) {
|
||||
buttons.char0 = char0;
|
||||
buttons.char1 = char1;
|
||||
buttons.charBoth = charBoth;
|
||||
}
|
||||
static void gltouchjoy_setTouchButtonTypes(
|
||||
touchjoy_button_type_t touchDownChar, int touchDownScancode,
|
||||
touchjoy_button_type_t northChar, int northScancode,
|
||||
touchjoy_button_type_t southChar, int southScancode)
|
||||
{
|
||||
buttons.touchDownChar = touchDownChar;
|
||||
buttons.touchDownScancode = touchDownScancode;
|
||||
|
||||
static void gltouchjoy_setTouchButtonTypes(touchjoy_button_type_t touchDownButton, touchjoy_button_type_t northButton, touchjoy_button_type_t southButton) {
|
||||
buttons.touchDownButton = touchDownButton;
|
||||
buttons.southButton = southButton;
|
||||
buttons.northButton = northButton;
|
||||
buttons.southChar = southChar;
|
||||
buttons.southScancode = southScancode;
|
||||
|
||||
buttons.northChar = northChar;
|
||||
buttons.northScancode = northScancode;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTapDelay(float secs) {
|
||||
@ -751,23 +705,36 @@ static void gltouchjoy_setTouchAxisSensitivity(float multiplier) {
|
||||
}
|
||||
|
||||
static void gltouchjoy_setButtonSwitchThreshold(int delta) {
|
||||
buttons.switchThreshold = delta;
|
||||
joyglobals.switchThreshold = delta;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchVariant(touchjoy_variant_t axisType) {
|
||||
axes.type = axisType;
|
||||
_setup_axis_object(axes.model);
|
||||
static void gltouchjoy_setTouchVariant(touchjoy_variant_t variantType) {
|
||||
variant.curr->resetState();
|
||||
|
||||
switch (variantType) {
|
||||
case EMULATED_JOYSTICK:
|
||||
variant.curr = variant.joys;
|
||||
break;
|
||||
|
||||
case EMULATED_KEYPAD:
|
||||
variant.curr = variant.kpad;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false && "touch variant set to invalid");
|
||||
break;
|
||||
}
|
||||
|
||||
variant.curr->resetState();
|
||||
}
|
||||
|
||||
static touchjoy_variant_t gltouchjoy_getTouchVariant(void) {
|
||||
return axes.type;
|
||||
return variant.curr->variant();
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchAxisValues(char north, char west, char east, char south) {
|
||||
axes.northChar = north;
|
||||
axes.westChar = west;
|
||||
axes.eastChar = east;
|
||||
axes.southChar = south;
|
||||
static void gltouchjoy_setTouchAxisTypes(uint8_t rosetteChars[(ROSETTE_ROWS * ROSETTE_COLS)], int rosetteScancodes[(ROSETTE_ROWS * ROSETTE_COLS)]) {
|
||||
memcpy(axes.rosetteChars, rosetteChars, sizeof(uint8_t)*(ROSETTE_ROWS * ROSETTE_COLS));
|
||||
memcpy(axes.rosetteScancodes, rosetteScancodes, sizeof(int) *(ROSETTE_ROWS * ROSETTE_COLS));
|
||||
_setup_axis_object(axes.model);
|
||||
}
|
||||
|
||||
@ -791,6 +758,10 @@ static void gltouchjoy_endCalibration(void) {
|
||||
joyglobals.isCalibrating = false;
|
||||
}
|
||||
|
||||
static bool gltouchjoy_isCalibrating(void) {
|
||||
return joyglobals.isCalibrating;
|
||||
}
|
||||
|
||||
__attribute__((constructor(CTOR_PRIORITY_LATE)))
|
||||
static void _init_gltouchjoy(void) {
|
||||
LOG("Registering OpenGL software touch joystick");
|
||||
@ -799,25 +770,42 @@ static void _init_gltouchjoy(void) {
|
||||
axes.centerY = 160;
|
||||
axes.multiplier = 1.f;
|
||||
axes.trackingIndex = TOUCH_NONE;
|
||||
axes.type = EMULATED_JOYSTICK;
|
||||
axes.northChar = MOUSETEXT_UP;
|
||||
axes.westChar = MOUSETEXT_LEFT;
|
||||
axes.eastChar = MOUSETEXT_RIGHT;
|
||||
axes.southChar = MOUSETEXT_DOWN;
|
||||
|
||||
axes.rosetteChars[0] = ' ';
|
||||
axes.rosetteScancodes[0] = -1;
|
||||
axes.rosetteChars[1] = MOUSETEXT_UP;
|
||||
axes.rosetteScancodes[1] = -1;
|
||||
axes.rosetteChars[2] = ' ';
|
||||
axes.rosetteScancodes[2] = -1;
|
||||
|
||||
axes.rosetteChars[3] = MOUSETEXT_LEFT;
|
||||
axes.rosetteScancodes[3] = -1;
|
||||
axes.rosetteChars[4] = '+';
|
||||
axes.rosetteScancodes[4] = -1;
|
||||
axes.rosetteChars[5] = MOUSETEXT_RIGHT;
|
||||
axes.rosetteScancodes[5] = -1;
|
||||
|
||||
axes.rosetteChars[6] = ' ';
|
||||
axes.rosetteScancodes[6] = -1;
|
||||
axes.rosetteChars[7] = MOUSETEXT_DOWN;
|
||||
axes.rosetteScancodes[7] = -1;
|
||||
axes.rosetteChars[8] = ' ';
|
||||
axes.rosetteScancodes[8] = -1;
|
||||
|
||||
buttons.centerX = 240;
|
||||
buttons.centerY = 160;
|
||||
buttons.trackingIndex = TOUCH_NONE;
|
||||
|
||||
buttons.touchDownButton = TOUCH_BUTTON0;
|
||||
buttons.southButton = TOUCH_BUTTON1;
|
||||
buttons.northButton = TOUCH_BOTH;
|
||||
buttons.char0 = MOUSETEXT_OPENAPPLE;
|
||||
buttons.char1 = MOUSETEXT_CLOSEDAPPLE;
|
||||
buttons.charBoth = '+';
|
||||
buttons.touchDownChar = TOUCH_BUTTON0;
|
||||
buttons.touchDownScancode = -1;
|
||||
|
||||
buttons.southChar = TOUCH_BUTTON1;
|
||||
buttons.southScancode = -1;
|
||||
|
||||
buttons.northChar = TOUCH_BOTH;
|
||||
buttons.northScancode = -1;
|
||||
|
||||
buttons.activeChar = MOUSETEXT_OPENAPPLE;
|
||||
buttons.switchThreshold = BUTTON_SWITCH_THRESHOLD_DEFAULT;
|
||||
|
||||
buttons.tapDelayThreadId = 0;
|
||||
buttons.tapDelayMutex = (pthread_mutex_t){ 0 };
|
||||
@ -828,6 +816,7 @@ static void _init_gltouchjoy(void) {
|
||||
joyglobals.ownsScreen = true;
|
||||
joyglobals.screenDivider = 0.5f;
|
||||
joyglobals.axisIsOnLeft = true;
|
||||
joyglobals.switchThreshold = BUTTON_SWITCH_THRESHOLD_DEFAULT;
|
||||
|
||||
video_backend->animation_showTouchJoystick = &_animation_showTouchJoystick;
|
||||
video_backend->animation_hideTouchJoystick = &_animation_hideTouchJoystick;
|
||||
@ -836,18 +825,18 @@ static void _init_gltouchjoy(void) {
|
||||
joydriver_setTouchJoystickEnabled = &gltouchjoy_setTouchJoystickEnabled;
|
||||
joydriver_setTouchJoystickOwnsScreen = &gltouchjoy_setTouchJoystickOwnsScreen;
|
||||
joydriver_ownsScreen = &gltouchjoy_ownsScreen;
|
||||
joydriver_setTouchButtonValues = &gltouchjoy_setTouchButtonValues;
|
||||
joydriver_setTouchButtonTypes = &gltouchjoy_setTouchButtonTypes;
|
||||
joydriver_setTapDelay = &gltouchjoy_setTapDelay;
|
||||
joydriver_setTouchAxisSensitivity = &gltouchjoy_setTouchAxisSensitivity;
|
||||
joydriver_setButtonSwitchThreshold = &gltouchjoy_setButtonSwitchThreshold;
|
||||
joydriver_setTouchVariant = &gltouchjoy_setTouchVariant;
|
||||
joydriver_getTouchVariant = &gltouchjoy_getTouchVariant;
|
||||
joydriver_setTouchAxisValues = &gltouchjoy_setTouchAxisValues;
|
||||
joydriver_setTouchAxisTypes = &gltouchjoy_setTouchAxisTypes;
|
||||
joydriver_setScreenDivision = &gltouchjoy_setScreenDivision;
|
||||
joydriver_setAxisOnLeft = &gltouchjoy_setAxisOnLeft;
|
||||
joydriver_beginCalibration = &gltouchjoy_beginCalibration;
|
||||
joydriver_endCalibration = &gltouchjoy_endCalibration;
|
||||
joydriver_isCalibrating = &gltouchjoy_isCalibrating;
|
||||
|
||||
glnode_registerNode(RENDER_LOW, (GLNode){
|
||||
.setup = &gltouchjoy_setup,
|
||||
@ -858,6 +847,25 @@ static void _init_gltouchjoy(void) {
|
||||
});
|
||||
}
|
||||
|
||||
void gltouchjoy_registerVariant(touchjoy_variant_t variantType, GLTouchJoyVariant *touchJoyVariant) {
|
||||
switch (variantType) {
|
||||
case EMULATED_JOYSTICK:
|
||||
variant.joys = touchJoyVariant;
|
||||
variant.curr = variant.kpad;
|
||||
break;
|
||||
|
||||
case EMULATED_KEYPAD:
|
||||
variant.kpad = touchJoyVariant;
|
||||
variant.curr = variant.kpad;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false && "invalid touch variant registration");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gldriver_joystick_reset(void) {
|
||||
#warning FIXME TODO expunge this olde API ...
|
||||
}
|
||||
|
99
src/video/gltouchjoy.h
Normal file
99
src/video/gltouchjoy.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GLTOUCHJOY_H_
|
||||
#define _GLTOUCHJOY_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "video/glvideo.h"
|
||||
#include "video/glhudmodel.h"
|
||||
#include "video/glnode.h"
|
||||
|
||||
// globals
|
||||
|
||||
typedef struct GLTouchJoyGlobals {
|
||||
|
||||
bool isAvailable; // Were there any OpenGL/memory errors on gltouchjoy initialization?
|
||||
bool isShuttingDown;
|
||||
bool isCalibrating; // Are we running in calibration mode?
|
||||
bool isEnabled; // Does player want touchjoy enabled?
|
||||
bool ownsScreen; // Does the touchjoy currently own the screen?
|
||||
float minAlphaWhenOwnsScreen;
|
||||
float minAlpha;
|
||||
float screenDivider;
|
||||
bool axisIsOnLeft;
|
||||
|
||||
int switchThreshold;
|
||||
|
||||
} GLTouchJoyGlobals;
|
||||
extern GLTouchJoyGlobals joyglobals;
|
||||
|
||||
// touch axis variables
|
||||
|
||||
typedef struct GLTouchJoyAxes {
|
||||
|
||||
GLModel *model;
|
||||
bool modelDirty;
|
||||
|
||||
uint8_t rosetteChars[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
int rosetteScancodes[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
|
||||
int centerX;
|
||||
int centerY;
|
||||
float multiplier;
|
||||
int trackingIndex;
|
||||
struct timespec timingBegin;
|
||||
|
||||
} GLTouchJoyAxes;
|
||||
extern GLTouchJoyAxes axes;
|
||||
|
||||
// button object variables
|
||||
|
||||
typedef struct GLTouchJoyButtons {
|
||||
GLModel *model;
|
||||
uint8_t activeChar;
|
||||
bool modelDirty;
|
||||
|
||||
touchjoy_button_type_t touchDownChar;
|
||||
int touchDownScancode;
|
||||
touchjoy_button_type_t northChar;
|
||||
int northScancode;
|
||||
touchjoy_button_type_t southChar;
|
||||
int southScancode;
|
||||
|
||||
int centerX;
|
||||
int centerY;
|
||||
int trackingIndex;
|
||||
struct timespec timingBegin;
|
||||
|
||||
pthread_t tapDelayThreadId;
|
||||
pthread_mutex_t tapDelayMutex;
|
||||
pthread_cond_t tapDelayCond;
|
||||
unsigned int tapDelayNanos;
|
||||
|
||||
} GLTouchJoyButtons;
|
||||
extern GLTouchJoyButtons buttons;
|
||||
|
||||
typedef struct GLTouchJoyVariant {
|
||||
touchjoy_variant_t (*variant)(void);
|
||||
void (*resetState)(void);
|
||||
void (*setCurrButtonValue)(touchjoy_button_type_t theButtonChar, int theButtonScancode);
|
||||
uint8_t (*buttonPress)(void);
|
||||
void (*buttonRelease)(void);
|
||||
void (*axisDown)(void);
|
||||
void (*axisMove)(int dx, int dy);
|
||||
void (*axisUp)(int dx, int dy);
|
||||
} GLTouchJoyVariant;
|
||||
|
||||
// registers a touch joystick variant with manager
|
||||
void gltouchjoy_registerVariant(touchjoy_variant_t variant, GLTouchJoyVariant *touchJoyVariant);
|
||||
|
||||
#endif // whole file
|
137
src/video/gltouchjoy_joy.c
Normal file
137
src/video/gltouchjoy_joy.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 "video/gltouchjoy.h"
|
||||
|
||||
#if !INTERFACE_TOUCH
|
||||
#error this is a touch interface module, possibly you mean to not compile this at all?
|
||||
#endif
|
||||
|
||||
static GLTouchJoyVariant happyHappyJoyJoy = { 0 };
|
||||
|
||||
static struct {
|
||||
uint8_t currJoyButtonValue0;
|
||||
uint8_t currJoyButtonValue1;
|
||||
uint8_t currButtonDisplayChar;
|
||||
} joys;
|
||||
|
||||
static touchjoy_variant_t touchjoy_variant(void) {
|
||||
return EMULATED_JOYSTICK;
|
||||
}
|
||||
|
||||
static inline void _reset_axis_state(void) {
|
||||
joy_x = HALF_JOY_RANGE;
|
||||
joy_y = HALF_JOY_RANGE;
|
||||
}
|
||||
|
||||
static inline void _reset_buttons_state(void) {
|
||||
joy_button0 = 0x0;
|
||||
joy_button1 = 0x0;
|
||||
joys.currJoyButtonValue0 = 0;
|
||||
joys.currJoyButtonValue1 = 0;
|
||||
joys.currButtonDisplayChar = ' ';
|
||||
}
|
||||
|
||||
static void touchjoy_resetState(void) {
|
||||
_reset_axis_state();
|
||||
_reset_buttons_state();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// axis state
|
||||
|
||||
static void touchjoy_axisDown(void) {
|
||||
_reset_axis_state();
|
||||
}
|
||||
|
||||
static void touchjoy_axisMove(int x, int y) {
|
||||
if (axes.multiplier != 1.f) {
|
||||
x = (int) ((float)x * axes.multiplier);
|
||||
y = (int) ((float)y * axes.multiplier);
|
||||
}
|
||||
|
||||
x += 0x80;
|
||||
y += 0x80;
|
||||
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
}
|
||||
if (x > 0xff) {
|
||||
x = 0xff;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
if (y > 0xff) {
|
||||
y = 0xff;
|
||||
}
|
||||
|
||||
joy_x = x;
|
||||
joy_y = y;
|
||||
}
|
||||
|
||||
static void touchjoy_axisUp(int x, int y) {
|
||||
_reset_axis_state();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// button state
|
||||
|
||||
static void touchjoy_setCurrButtonValue(touchjoy_button_type_t theButtonChar, int theButtonScancode) {
|
||||
if (theButtonChar == TOUCH_BUTTON0) {
|
||||
joys.currJoyButtonValue0 = 0x80;
|
||||
joys.currJoyButtonValue1 = 0;
|
||||
joys.currButtonDisplayChar = MOUSETEXT_OPENAPPLE;
|
||||
} else if (theButtonChar == TOUCH_BUTTON1) {
|
||||
joys.currJoyButtonValue0 = 0;
|
||||
joys.currJoyButtonValue1 = 0x80;
|
||||
joys.currButtonDisplayChar = MOUSETEXT_CLOSEDAPPLE;
|
||||
} else if (theButtonChar == TOUCH_BOTH) {
|
||||
joys.currJoyButtonValue0 = 0x80;
|
||||
joys.currJoyButtonValue1 = 0x80;
|
||||
joys.currButtonDisplayChar = '+';
|
||||
} else {
|
||||
joys.currJoyButtonValue0 = 0;
|
||||
joys.currJoyButtonValue1 = 0;
|
||||
joys.currButtonDisplayChar = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t touchjoy_buttonPress(void) {
|
||||
joy_button0 = joys.currJoyButtonValue0;
|
||||
joy_button1 = joys.currJoyButtonValue1;
|
||||
return joys.currButtonDisplayChar;
|
||||
}
|
||||
|
||||
static void touchjoy_buttonRelease(void) {
|
||||
_reset_buttons_state();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
__attribute__((constructor(CTOR_PRIORITY_EARLY)))
|
||||
static void _init_gltouchjoy_joy(void) {
|
||||
LOG("Registering OpenGL software touch joystick (joystick variant)");
|
||||
|
||||
happyHappyJoyJoy.variant = &touchjoy_variant,
|
||||
happyHappyJoyJoy.resetState = &touchjoy_resetState,
|
||||
|
||||
happyHappyJoyJoy.setCurrButtonValue = &touchjoy_setCurrButtonValue,
|
||||
happyHappyJoyJoy.buttonPress = &touchjoy_buttonPress,
|
||||
happyHappyJoyJoy.buttonRelease = &touchjoy_buttonRelease,
|
||||
|
||||
happyHappyJoyJoy.axisDown = &touchjoy_axisDown,
|
||||
happyHappyJoyJoy.axisMove = &touchjoy_axisMove,
|
||||
happyHappyJoyJoy.axisUp = &touchjoy_axisUp,
|
||||
|
||||
gltouchjoy_registerVariant(EMULATED_JOYSTICK, &happyHappyJoyJoy);
|
||||
}
|
||||
|
421
src/video/gltouchjoy_kpad.c
Normal file
421
src/video/gltouchjoy_kpad.c
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
* 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 "video/gltouchjoy.h"
|
||||
|
||||
#if !INTERFACE_TOUCH
|
||||
#error this is a touch interface module, possibly you mean to not compile this at all?
|
||||
#endif
|
||||
|
||||
#define KEY_REPEAT_THRESHOLD_NANOS (NANOSECONDS_PER_SECOND / 4)
|
||||
|
||||
// WARNING : this does not match the rosette left-right-down layout, but what is returned by atan2f()
|
||||
typedef enum keypad_octant_t {
|
||||
OCTANT_WEST = 0,
|
||||
OCTANT_NORTHWEST,
|
||||
OCTANT_NORTH,
|
||||
OCTANT_NORTHEAST,
|
||||
OCTANT_EAST,
|
||||
OCTANT_SOUTHEAST,
|
||||
OCTANT_SOUTH,
|
||||
OCTANT_SOUTHWEST,
|
||||
ORIGIN,
|
||||
} keypad_octant_t;
|
||||
|
||||
// Assuming radian value between -PI <-> PI
|
||||
#define RADIANS_NORTHWEST ((-3.f * M_PI) / 4.f)
|
||||
#define RADIANS_NORTH ((-1.f * M_PI) / 2.f)
|
||||
#define RADIANS_NORTHEAST ((-1.f * M_PI) / 4.f)
|
||||
#define RADIANS_SOUTHWEST (( 3.f * M_PI) / 4.f)
|
||||
#define RADIANS_SOUTH (( 1.f * M_PI) / 2.f)
|
||||
#define RADIANS_SOUTHEAST (( 1.f * M_PI) / 4.f)
|
||||
|
||||
typedef enum keypad_fire_t {
|
||||
REPEAT_AXIS = 0,
|
||||
REPEAT_AXIS_ALT,
|
||||
REPEAT_BUTTON,
|
||||
MAX_REPEATING,
|
||||
} keypad_fire_t;
|
||||
|
||||
static struct {
|
||||
keypad_octant_t axisCurrentOctant;
|
||||
|
||||
uint8_t currButtonDisplayChar;
|
||||
|
||||
// scancodes to fire upon axisUp, buttonPress, or as key-repeat
|
||||
keypad_fire_t fireIdx;
|
||||
int scancodes[MAX_REPEATING];
|
||||
struct timespec timingBegins[MAX_REPEATING];
|
||||
|
||||
bool axisTiming;
|
||||
bool buttonTiming;
|
||||
|
||||
} kpad = { 0 };
|
||||
|
||||
static GLTouchJoyVariant kpadJoy = { 0 };
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// repeat key callback
|
||||
|
||||
static void touchkpad_keyboardReadCallback(void) {
|
||||
|
||||
assert(pthread_self() == cpu_thread_id);
|
||||
|
||||
struct timespec now = { 0 };
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
for (unsigned int i=0; i<MAX_REPEATING; i++) {
|
||||
bool fired = false;
|
||||
int scancode = kpad.scancodes[kpad.fireIdx];
|
||||
if (scancode >= 0) {
|
||||
struct timespec deltat = timespec_diff(kpad.timingBegins[kpad.fireIdx], now, NULL);
|
||||
if (deltat.tv_sec || deltat.tv_nsec > KEY_REPEAT_THRESHOLD_NANOS) {
|
||||
LOG("REPEAT #%d/%lu/%lu: %d", kpad.fireIdx, deltat.tv_sec, deltat.tv_nsec, scancode);
|
||||
c_keys_handle_input(scancode, /*pressed:*/true, /*ASCII:*/false);
|
||||
fired = true;
|
||||
}
|
||||
}
|
||||
++kpad.fireIdx;
|
||||
if (kpad.fireIdx >= MAX_REPEATING) {
|
||||
kpad.fireIdx = 0;
|
||||
}
|
||||
if (fired) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kpad.axisTiming && !kpad.buttonTiming) {
|
||||
keydriver_keyboardReadCallback = NULL;// unschedule callback
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static touchjoy_variant_t touchkpad_variant(void) {
|
||||
return EMULATED_KEYPAD;
|
||||
}
|
||||
|
||||
static void touchkpad_resetState(void) {
|
||||
kpad.axisCurrentOctant = ORIGIN;
|
||||
|
||||
for (unsigned int i=0; i<MAX_REPEATING; i++) {
|
||||
kpad.scancodes[i] = -1;
|
||||
}
|
||||
|
||||
kpad.axisTiming = false;
|
||||
kpad.buttonTiming = false;
|
||||
|
||||
kpad.currButtonDisplayChar = ' ';
|
||||
|
||||
for (unsigned int i=0; i<ROSETTE_COLS; i++) {
|
||||
for (unsigned int j=0; j<ROSETTE_ROWS; j++) {
|
||||
c_keys_handle_input(axes.rosetteScancodes[i], /*pressed:*/false, /*ASCII:*/false);
|
||||
}
|
||||
}
|
||||
|
||||
c_keys_handle_input(buttons.touchDownScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(buttons.northScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(buttons.southScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// axis key(s) state
|
||||
|
||||
static void touchkpad_axisDown(void) {
|
||||
kpad.axisCurrentOctant = ORIGIN;
|
||||
if (axes.rosetteScancodes[ROSETTE_CENTER] >= 0) {
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_CENTER];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = -1;
|
||||
clock_gettime(CLOCK_MONOTONIC, &kpad.timingBegins[REPEAT_AXIS]);
|
||||
kpad.axisTiming = true;
|
||||
keydriver_keyboardReadCallback = &touchkpad_keyboardReadCallback;
|
||||
}
|
||||
}
|
||||
|
||||
static void touchkpad_axisMove(int dx, int dy) {
|
||||
|
||||
if ((dx > -joyglobals.switchThreshold) && (dx < joyglobals.switchThreshold) && (dy > -joyglobals.switchThreshold) && (dy < joyglobals.switchThreshold)) {
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_CENTER];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dx == 0 && dy == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
// determine the octant of this dx/dy
|
||||
|
||||
keypad_octant_t axisLastOctant = kpad.axisCurrentOctant;
|
||||
|
||||
const float radians = atan2f(dy, dx); // -180-180deg range
|
||||
float radnorm = radians + M_PI; // 0-360deg range
|
||||
if (UNLIKELY(radnorm < 0.f)) {
|
||||
radnorm = 0.f; // clamp positive
|
||||
}
|
||||
float octant = radnorm + (M_PI/8.f); // rotate to correct octant (+45deg)
|
||||
octant /= (M_PI/4.f); // divide to octant (/90deg)
|
||||
kpad.axisCurrentOctant = (keypad_octant_t) ((int)octant & 0x7);// integer modulo maps to enum
|
||||
|
||||
if (kpad.axisCurrentOctant == axisLastOctant) {
|
||||
break;
|
||||
}
|
||||
|
||||
LOG("radians:%f radnorm:%f octant:%f, currOctant:%d", radians, radnorm, octant, kpad.axisCurrentOctant);
|
||||
|
||||
// handle a new octant
|
||||
|
||||
struct timespec now = { 0 };
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
kpad.timingBegins[REPEAT_AXIS] = now;
|
||||
kpad.timingBegins[REPEAT_AXIS_ALT] = now;
|
||||
keydriver_keyboardReadCallback = &touchkpad_keyboardReadCallback;
|
||||
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = -1;
|
||||
switch (kpad.axisCurrentOctant) {
|
||||
case OCTANT_NORTHWEST:
|
||||
if (axes.rosetteScancodes[ROSETTE_NORTHWEST] >= 0) {
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHWEST];
|
||||
LOG("XY : NORTHWEST, (%d)", axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
} else if (axes.rosetteScancodes[ROSETTE_NORTH] < 0) {
|
||||
if (radians > RADIANS_NORTHWEST) {
|
||||
LOG("IGNORING Y (NORTH) ...");
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
} else {
|
||||
LOG("X : WEST (%d)", axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
}
|
||||
} else if (axes.rosetteScancodes[ROSETTE_WEST] < 0) {
|
||||
if (radians < RADIANS_NORTHWEST) {
|
||||
LOG("IGNORING X (WEST) ...");
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
} else {
|
||||
LOG("Y : NORTH (%d)", axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
}
|
||||
} else {
|
||||
if (radians > RADIANS_NORTHWEST) {
|
||||
LOG("XY : NORTH (%d) & WEST (%d)", axes.rosetteScancodes[ROSETTE_NORTH], axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
} else {
|
||||
LOG("XY : WEST (%d) & NORTH (%d)", axes.rosetteScancodes[ROSETTE_WEST], axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_NORTH:
|
||||
LOG("Y : NORTH (%d)", axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
break;
|
||||
|
||||
case OCTANT_NORTHEAST:
|
||||
if (axes.rosetteScancodes[ROSETTE_NORTHEAST] >= 0) {
|
||||
LOG("XY : NORTHEAST (%d)", axes.rosetteScancodes[ROSETTE_NORTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHEAST];
|
||||
} else if (axes.rosetteScancodes[ROSETTE_NORTH] < 0) {
|
||||
if (radians < RADIANS_NORTHEAST) {
|
||||
LOG("IGNORING Y (NORTH) ...");
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
} else {
|
||||
LOG("X : EAST (%d)", axes.rosetteScancodes[ROSETTE_EAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
}
|
||||
} else if (axes.rosetteScancodes[ROSETTE_EAST] < 0) {
|
||||
if (radians > RADIANS_NORTHEAST) {
|
||||
LOG("IGNORING X (EAST) ...");
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
} else {
|
||||
LOG("Y : NORTH (%d)", axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
}
|
||||
} else {
|
||||
if (radians < RADIANS_NORTHEAST) {
|
||||
LOG("XY : NORTH (%d) & EAST (%d)", axes.rosetteScancodes[ROSETTE_NORTH], axes.rosetteScancodes[ROSETTE_EAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
} else {
|
||||
LOG("XY : EAST (%d) & NORTH (%d)", axes.rosetteScancodes[ROSETTE_EAST], axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_WEST:
|
||||
LOG("Y : WEST (%d)", axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
break;
|
||||
|
||||
case OCTANT_EAST:
|
||||
LOG("Y : EAST (%d)", axes.rosetteScancodes[ROSETTE_EAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
break;
|
||||
|
||||
case OCTANT_SOUTHWEST:
|
||||
if (axes.rosetteScancodes[ROSETTE_SOUTHWEST] >= 0) {
|
||||
LOG("XY : SOUTHWEST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHWEST];
|
||||
} else if (axes.rosetteScancodes[ROSETTE_SOUTH] < 0) {
|
||||
if (radians < RADIANS_SOUTHWEST) {
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
LOG("IGNORING Y (SOUTH) ...");
|
||||
} else {
|
||||
LOG("X : WEST (%d)", axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
}
|
||||
} else if (axes.rosetteScancodes[ROSETTE_WEST] < 0) {
|
||||
if (radians > RADIANS_SOUTHWEST) {
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
LOG("IGNORING X (WEST) ...");
|
||||
} else {
|
||||
LOG("Y : SOUTH (%d)", axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
}
|
||||
} else {
|
||||
if (radians < RADIANS_SOUTHWEST) {
|
||||
LOG("XY : SOUTH (%d) & WEST (%d)", axes.rosetteScancodes[ROSETTE_SOUTH], axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
} else {
|
||||
LOG("XY : WEST (%d) & SOUTH (%d)", axes.rosetteScancodes[ROSETTE_WEST], axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_SOUTH:
|
||||
LOG("Y : SOUTH (%d)", axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
break;
|
||||
|
||||
case OCTANT_SOUTHEAST:
|
||||
if (axes.rosetteScancodes[ROSETTE_SOUTHEAST] >= 0) {
|
||||
LOG("XY : SOUTHEAST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHEAST];
|
||||
} else if (axes.rosetteScancodes[ROSETTE_SOUTH] < 0) {
|
||||
if (radians > RADIANS_SOUTHEAST) {
|
||||
LOG("IGNORING Y (SOUTH) ...");
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
} else {
|
||||
LOG("X : EAST (%d)", axes.rosetteScancodes[ROSETTE_EAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
}
|
||||
} else if (axes.rosetteScancodes[ROSETTE_EAST] < 0) {
|
||||
if (radians < RADIANS_SOUTHEAST) {
|
||||
LOG("IGNORING X (EAST) ...");
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
} else {
|
||||
LOG("Y : SOUTH (%d)", axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
}
|
||||
} else {
|
||||
if (radians > RADIANS_SOUTHEAST) {
|
||||
LOG("XY : SOUTH (%d) & EAST (%d)", axes.rosetteScancodes[ROSETTE_SOUTH], axes.rosetteScancodes[ROSETTE_EAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
} else {
|
||||
LOG("XY : EAST (%d) & SOUTH (%d)", axes.rosetteScancodes[ROSETTE_EAST], axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false && "should not happen");
|
||||
break;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
kpad.axisTiming = true;
|
||||
}
|
||||
|
||||
static void touchkpad_axisUp(int dx, int dy) {
|
||||
touchkpad_axisMove(dx, dy);
|
||||
kpad.axisCurrentOctant = ORIGIN;
|
||||
|
||||
int scancode = kpad.scancodes[REPEAT_AXIS];
|
||||
kpad.scancodes[REPEAT_AXIS] = -1;
|
||||
if (scancode < 0) {
|
||||
scancode = kpad.scancodes[REPEAT_AXIS_ALT];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = -1;
|
||||
}
|
||||
|
||||
c_keys_handle_input(scancode, /*pressed:*/true, /*ASCII:*/false);
|
||||
c_keys_handle_input(scancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
kpad.axisTiming = false;
|
||||
|
||||
// if no other scancodes, REPEAT_AXIS_ALT (if non-negative) will be fired once remaining in callback
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// button key state
|
||||
|
||||
static void touchkpad_setCurrButtonValue(touchjoy_button_type_t theButtonChar, int theButtonScancode) {
|
||||
if (theButtonChar >= 0) {
|
||||
kpad.currButtonDisplayChar = theButtonChar;
|
||||
kpad.scancodes[REPEAT_BUTTON] = theButtonScancode;
|
||||
} else {
|
||||
kpad.currButtonDisplayChar = ' ';
|
||||
kpad.scancodes[REPEAT_BUTTON] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t touchkpad_buttonPress(void) {
|
||||
if (kpad.scancodes[REPEAT_BUTTON] >= 0) {
|
||||
LOG("->BUTT : %d/'%c'", kpad.scancodes[REPEAT_BUTTON], kpad.currButtonDisplayChar);
|
||||
clock_gettime(CLOCK_MONOTONIC, &kpad.timingBegins[REPEAT_BUTTON]);
|
||||
c_keys_handle_input(kpad.scancodes[REPEAT_BUTTON], /*pressed:*/true, /*ASCII:*/false);
|
||||
kpad.buttonTiming = true;
|
||||
keydriver_keyboardReadCallback = &touchkpad_keyboardReadCallback;
|
||||
}
|
||||
return kpad.currButtonDisplayChar;
|
||||
}
|
||||
|
||||
static void touchkpad_buttonRelease(void) {
|
||||
kpad.scancodes[REPEAT_BUTTON] = -1;
|
||||
kpad.buttonTiming = false;
|
||||
c_keys_handle_input(kpad.scancodes[REPEAT_BUTTON], /*pressed:*/false, /*ASCII:*/false);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
__attribute__((constructor(CTOR_PRIORITY_EARLY)))
|
||||
static void _init_gltouchjoy_kpad(void) {
|
||||
LOG("Registering OpenGL software touch joystick (keypad variant)");
|
||||
|
||||
for (unsigned int i=0; i<MAX_REPEATING; i++) {
|
||||
kpad.scancodes[i] = -1;
|
||||
}
|
||||
|
||||
kpad.currButtonDisplayChar = ' ';
|
||||
|
||||
kpadJoy.variant = &touchkpad_variant,
|
||||
kpadJoy.resetState = &touchkpad_resetState,
|
||||
|
||||
kpadJoy.setCurrButtonValue = &touchkpad_setCurrButtonValue,
|
||||
kpadJoy.buttonPress = &touchkpad_buttonPress,
|
||||
kpadJoy.buttonRelease = &touchkpad_buttonRelease,
|
||||
|
||||
kpadJoy.axisDown = &touchkpad_axisDown,
|
||||
kpadJoy.axisMove = &touchkpad_axisMove,
|
||||
kpadJoy.axisUp = &touchkpad_axisUp,
|
||||
|
||||
gltouchjoy_registerVariant(EMULATED_KEYPAD, &kpadJoy);
|
||||
}
|
||||
|
10
src/vm.c
10
src/vm.c
@ -143,7 +143,15 @@ GLUE_C_WRITE(write_unmapped_softswitch)
|
||||
|
||||
GLUE_C_READ(read_keyboard)
|
||||
{
|
||||
return apple_ii_64k[0][0xC000];
|
||||
uint8_t b = apple_ii_64k[0][0xC000];
|
||||
#if INTERFACE_TOUCH
|
||||
// touch interface is expected to rate limit this callback by unregistering/NULLifying
|
||||
void (*readCallback)(void) = keydriver_keyboardReadCallback;
|
||||
if (readCallback) {
|
||||
readCallback();
|
||||
}
|
||||
#endif
|
||||
return b;
|
||||
}
|
||||
|
||||
GLUE_C_READ(read_keyboard_strobe)
|
||||
|
Loading…
x
Reference in New Issue
Block a user