2013-07-06 04:37:13 +00:00
|
|
|
/*
|
2015-10-22 05:13:26 +00:00
|
|
|
* Apple // emulator for *ix
|
|
|
|
*
|
|
|
|
* This software package is subject to the GNU General Public License
|
|
|
|
* version 3 or later (your choice) as published by the Free Software
|
|
|
|
* Foundation.
|
2013-06-11 07:08:15 +00:00
|
|
|
*
|
|
|
|
* Copyright 1994 Alexander Jean-Claude Bottema
|
|
|
|
* Copyright 1995 Stephen Lee
|
|
|
|
* Copyright 1997, 1998 Aaron Culliney
|
|
|
|
* Copyright 1998, 1999, 2000 Michael Deutschmann
|
2015-10-22 05:13:26 +00:00
|
|
|
* Copyright 2013-2015 Aaron Culliney
|
2013-06-11 07:08:15 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-01-23 04:42:34 +00:00
|
|
|
#include "common.h"
|
|
|
|
|
2014-11-09 21:13:27 +00:00
|
|
|
#ifdef INTERFACE_CLASSIC
|
|
|
|
#define TEMPSIZE 256
|
|
|
|
extern void copy_and_pad_string(char *dest, const char* src, const char c, const int len, const char cap);
|
2013-12-07 06:55:00 +00:00
|
|
|
#endif
|
|
|
|
|
2018-11-04 22:06:38 +00:00
|
|
|
joystick_mode_t joy_mode = JOY_MODE_DEFAULT;
|
2016-03-26 05:34:33 +00:00
|
|
|
|
2013-12-28 21:08:35 +00:00
|
|
|
/* parameters for generic and keyboard-simulated joysticks */
|
2014-05-10 20:08:44 +00:00
|
|
|
uint16_t joy_x = HALF_JOY_RANGE;
|
|
|
|
uint16_t joy_y = HALF_JOY_RANGE;
|
2014-12-06 06:31:12 +00:00
|
|
|
bool joy_clip_to_radius = false;
|
2013-12-28 21:08:35 +00:00
|
|
|
|
2013-12-28 21:54:26 +00:00
|
|
|
#ifdef KEYPAD_JOYSTICK
|
2013-12-29 05:48:42 +00:00
|
|
|
short joy_step = 1;
|
2016-03-26 05:34:33 +00:00
|
|
|
bool joy_auto_recenter = false;
|
|
|
|
#endif
|
|
|
|
|
2016-04-17 18:26:35 +00:00
|
|
|
void (*joydriver_resetJoystick)(void) = NULL;
|
|
|
|
|
2016-03-26 05:34:33 +00:00
|
|
|
static void joystick_prefsChanged(const char *domain) {
|
|
|
|
assert(strcmp(domain, PREF_DOMAIN_JOYSTICK) == 0);
|
|
|
|
|
|
|
|
long lVal = 0;
|
2018-11-04 22:06:38 +00:00
|
|
|
|
|
|
|
if (prefs_parseLongValue(domain, PREF_JOYSTICK_MODE, &lVal, /*base:*/10)) {
|
|
|
|
joy_mode = getJoyMode(lVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
prefs_parseBoolValue(domain, PREF_JOYSTICK_CLIP_TO_RADIUS, &joy_clip_to_radius);
|
|
|
|
|
|
|
|
#ifdef KEYPAD_JOYSTICK
|
2016-03-26 05:34:33 +00:00
|
|
|
prefs_parseLongValue(domain, PREF_JOYSTICK_KPAD_STEP, &lVal, /*base:*/10);
|
|
|
|
joy_step = (short)lVal;
|
|
|
|
if (joy_step < 1) {
|
|
|
|
joy_step = 1;
|
|
|
|
}
|
|
|
|
if (joy_step > 255) {
|
|
|
|
joy_step = 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
prefs_parseBoolValue(domain, PREF_JOYSTICK_KPAD_AUTO_RECENTER, &joy_auto_recenter);
|
2013-12-28 21:54:26 +00:00
|
|
|
#endif
|
2016-03-26 05:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static __attribute__((constructor)) void _init_joystick(void) {
|
|
|
|
prefs_registerListener(PREF_DOMAIN_JOYSTICK, &joystick_prefsChanged);
|
|
|
|
}
|
2013-12-28 21:54:26 +00:00
|
|
|
|
2014-03-23 21:25:28 +00:00
|
|
|
#ifdef INTERFACE_CLASSIC
|
2016-03-26 05:34:33 +00:00
|
|
|
|
2013-06-11 07:08:15 +00:00
|
|
|
/* -------------------------------------------------------------------------
|
2013-12-09 00:57:02 +00:00
|
|
|
c_calibrate_pc_joystick() - calibrates joystick. determines extreme
|
2013-06-11 07:08:15 +00:00
|
|
|
and center coordinates. assumes that it can write to the interface
|
|
|
|
screen.
|
|
|
|
------------------------------------------------------------------------- */
|
2013-12-07 06:55:00 +00:00
|
|
|
static void c_calibrate_pc_joystick()
|
|
|
|
{
|
2014-05-12 02:18:53 +00:00
|
|
|
char temp[TEMPSIZE];
|
|
|
|
|
2013-12-22 07:55:23 +00:00
|
|
|
#define CALIBRATE_JOYMENU_H 20
|
|
|
|
#define CALIBRATE_JOYMENU_W 40
|
|
|
|
#define CALIBRATE_TURTLE_X0 4
|
2013-12-28 21:08:35 +00:00
|
|
|
#define CALIBRATE_TURTLE_Y0 3
|
2013-12-22 07:55:23 +00:00
|
|
|
#define CALIBRATE_TURTLE_STEP_X (30.f / 255.f)
|
|
|
|
#define CALIBRATE_TURTLE_STEP_Y (10.f / 255.f)
|
|
|
|
char joymenu[CALIBRATE_JOYMENU_H][CALIBRATE_JOYMENU_W+1] =
|
|
|
|
//1. 5. 10. 15. 20. 25. 30. 35. 40.
|
|
|
|
{ "||||||||||||||||||||||||||||||||||||||||",
|
|
|
|
"| |",
|
|
|
|
"| ||||||||||||||||||||||||||||||||| |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| ||||||||||||||||||||||||||||||||| |",
|
|
|
|
"| |",
|
|
|
|
"| btn1:@ btn2:@ x:@@@@ y:@@@@ |",
|
2013-12-28 21:08:35 +00:00
|
|
|
"| |",
|
|
|
|
"| ESC quits calibration |",
|
2013-12-22 07:55:23 +00:00
|
|
|
"||||||||||||||||||||||||||||||||||||||||" };
|
|
|
|
|
|
|
|
uint8_t x_last=CALIBRATE_JOYMENU_W>>1, y_last=CALIBRATE_JOYMENU_H>>1;
|
|
|
|
const char* const spinney = "|/-\\";
|
|
|
|
uint8_t spinney_idx=0;
|
2013-12-28 21:08:35 +00:00
|
|
|
for (;;)
|
2013-07-06 04:37:13 +00:00
|
|
|
{
|
2013-12-28 21:08:35 +00:00
|
|
|
int ch = c_mygetch(0);
|
2013-12-22 07:55:23 +00:00
|
|
|
|
2013-12-28 21:08:35 +00:00
|
|
|
int x_plot = CALIBRATE_TURTLE_X0 + (int)(joy_x * CALIBRATE_TURTLE_STEP_X);
|
|
|
|
int y_plot = CALIBRATE_TURTLE_Y0 + (int)(joy_y * CALIBRATE_TURTLE_STEP_Y);
|
2013-12-22 07:55:23 +00:00
|
|
|
|
|
|
|
joymenu[y_last][x_last] = ' ';
|
|
|
|
joymenu[y_plot][x_plot] = spinney[spinney_idx];
|
|
|
|
|
|
|
|
x_last = x_plot;
|
|
|
|
y_last = y_plot;
|
|
|
|
|
2018-01-15 21:03:31 +00:00
|
|
|
joymenu[CALIBRATE_JOYMENU_H-4][8] = run_args.joy_button0 ? 'X' : ' ';
|
|
|
|
joymenu[CALIBRATE_JOYMENU_H-4][15] = run_args.joy_button1 ? 'X' : ' ';
|
2014-11-09 21:13:27 +00:00
|
|
|
|
|
|
|
snprintf(temp, TEMPSIZE, "%04x", (short)(joy_x));
|
|
|
|
copy_and_pad_string(&joymenu[CALIBRATE_JOYMENU_H-4][24], temp, ' ', 5, ' ');
|
|
|
|
snprintf(temp, TEMPSIZE, "%04x", (short)(joy_y));
|
|
|
|
copy_and_pad_string(&joymenu[CALIBRATE_JOYMENU_H-4][32], temp, ' ', 5, ' ');
|
|
|
|
c_interface_print_submenu_centered(joymenu[0], CALIBRATE_JOYMENU_W, CALIBRATE_JOYMENU_H);
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2013-12-22 07:55:23 +00:00
|
|
|
spinney_idx = (spinney_idx+1) % 4;
|
|
|
|
|
2013-12-28 21:08:35 +00:00
|
|
|
if (ch == kESC)
|
2013-12-22 07:55:23 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2013-12-28 21:08:35 +00:00
|
|
|
|
|
|
|
static struct timespec ts = { .tv_sec=0, .tv_nsec=33333333 };
|
|
|
|
nanosleep(&ts, NULL);
|
2013-12-22 07:55:23 +00:00
|
|
|
}
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
2013-12-07 06:55:00 +00:00
|
|
|
|
2016-03-26 05:34:33 +00:00
|
|
|
#ifdef KEYPAD_JOYSTICK
|
2013-12-07 06:55:00 +00:00
|
|
|
static void c_calibrate_keypad_joystick()
|
|
|
|
{
|
2013-12-28 21:08:35 +00:00
|
|
|
|
|
|
|
#define KEYPAD_SUBMENU_H 20
|
|
|
|
#define KEYPAD_SUBMENU_W 40
|
|
|
|
#define CALIBRATE_TURTLE_KP_X0 4
|
|
|
|
#define CALIBRATE_TURTLE_KP_Y0 5
|
|
|
|
#define CALIBRATE_TURTLE_KP_STEP_X (14.f / 255.f)
|
|
|
|
char submenu[KEYPAD_SUBMENU_H][KEYPAD_SUBMENU_W+1] =
|
|
|
|
//1. 5. 10. 15. 20. 25. 30. 35. 40.
|
|
|
|
{ "||||||||||||||||||||||||||||||||||||||||",
|
|
|
|
"| |",
|
|
|
|
"| Use keypad to test & tune joystick |",
|
|
|
|
"| |",
|
|
|
|
"| ||||||||||||||||| [You may need to |",
|
|
|
|
"| | | enable NumLock] |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | |",
|
|
|
|
"| | | 7 @ 9 |",
|
|
|
|
"| | | @ 5 @ |",
|
|
|
|
"| | . | 1 @ 3 |",
|
|
|
|
"| | | Alt-l Alt-r |",
|
|
|
|
"| | | |",
|
2013-12-29 05:48:42 +00:00
|
|
|
"| | | + toggles auto- |",
|
|
|
|
"| | | recentering: @@@ |",
|
2013-12-28 21:08:35 +00:00
|
|
|
"| | | < or > to change |",
|
|
|
|
"| ||||||||||||||||| sensitivity: @@ |",
|
|
|
|
"| |",
|
|
|
|
"| Alt btn1:@ Alt btn2:@ x:@@ y:@@ |",
|
|
|
|
"||||||||||||||||||||||||||||||||||||||||" };
|
|
|
|
|
2017-09-22 00:30:09 +00:00
|
|
|
submenu[8][29] = (char)(MOUSETEXT_BEGIN + 0x0b);
|
|
|
|
submenu[9][27] = (char)(MOUSETEXT_BEGIN + 0x08);
|
|
|
|
submenu[9][31] = (char)(MOUSETEXT_BEGIN + 0x15);
|
|
|
|
submenu[10][29] = (char)(MOUSETEXT_BEGIN + 0x0a);
|
2013-12-28 21:08:35 +00:00
|
|
|
|
|
|
|
joy_x = HALF_JOY_RANGE;
|
|
|
|
joy_y = HALF_JOY_RANGE;
|
|
|
|
|
|
|
|
int ch = -1;
|
|
|
|
uint8_t x_last=CALIBRATE_JOYMENU_W>>1, y_last=CALIBRATE_JOYMENU_H>>1;
|
|
|
|
const char* const spinney = "|/-\\";
|
|
|
|
uint8_t spinney_idx=0;
|
2014-05-12 02:18:53 +00:00
|
|
|
char temp[TEMPSIZE];
|
2013-12-28 21:08:35 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
2018-01-15 21:03:31 +00:00
|
|
|
submenu[KEYPAD_SUBMENU_H-2][12] = run_args.joy_button0 ? 'X' : ' ';
|
|
|
|
submenu[KEYPAD_SUBMENU_H-2][23] = run_args.joy_button1 ? 'X' : ' ';
|
2013-12-28 21:08:35 +00:00
|
|
|
|
|
|
|
snprintf(temp, TEMPSIZE, "%02x", (uint8_t)joy_x);
|
|
|
|
copy_and_pad_string(&submenu[KEYPAD_SUBMENU_H-2][31], temp, ' ', 3, ' ');
|
|
|
|
snprintf(temp, TEMPSIZE, "%02x", (uint8_t)joy_y);
|
|
|
|
copy_and_pad_string(&submenu[KEYPAD_SUBMENU_H-2][36], temp, ' ', 3, ' ');
|
|
|
|
|
|
|
|
snprintf(temp, TEMPSIZE, "%02x", (uint8_t)joy_step);
|
|
|
|
copy_and_pad_string(&submenu[KEYPAD_SUBMENU_H-4][36], temp, ' ', 3, ' ');
|
|
|
|
|
2014-10-19 19:54:03 +00:00
|
|
|
snprintf(temp, TEMPSIZE, "%s", joy_auto_recenter ? " on" : "off" );
|
2013-12-29 05:48:42 +00:00
|
|
|
copy_and_pad_string(&submenu[KEYPAD_SUBMENU_H-6][35], temp, ' ', 4, ' ');
|
|
|
|
|
2013-12-28 21:08:35 +00:00
|
|
|
int x_plot = CALIBRATE_TURTLE_KP_X0 + (int)(joy_x * CALIBRATE_TURTLE_KP_STEP_X);
|
|
|
|
int y_plot = CALIBRATE_TURTLE_KP_Y0 + (int)(joy_y * CALIBRATE_TURTLE_STEP_Y);
|
|
|
|
|
|
|
|
submenu[y_last][x_last] = ' ';
|
|
|
|
submenu[y_plot][x_plot] = spinney[spinney_idx];
|
|
|
|
|
|
|
|
x_last = x_plot;
|
|
|
|
y_last = y_plot;
|
|
|
|
|
|
|
|
spinney_idx = (spinney_idx+1) % 4;
|
|
|
|
|
|
|
|
c_interface_print_submenu_centered(submenu[0], KEYPAD_SUBMENU_W, KEYPAD_SUBMENU_H);
|
|
|
|
|
|
|
|
ch = c_mygetch(0);
|
|
|
|
|
|
|
|
if (ch == kESC)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (ch == '<')
|
|
|
|
{
|
|
|
|
if (joy_step > 1)
|
|
|
|
{
|
|
|
|
--joy_step;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ch == '>')
|
|
|
|
{
|
|
|
|
if (joy_step < 0xFF)
|
|
|
|
{
|
|
|
|
++joy_step;
|
|
|
|
}
|
|
|
|
}
|
2013-12-29 05:48:42 +00:00
|
|
|
else if (ch == '+')
|
|
|
|
{
|
2014-10-19 19:54:03 +00:00
|
|
|
joy_auto_recenter = (joy_auto_recenter+1) % 2;
|
|
|
|
if (joy_auto_recenter)
|
2013-12-29 05:48:42 +00:00
|
|
|
{
|
|
|
|
joy_x = HALF_JOY_RANGE;
|
|
|
|
joy_y = HALF_JOY_RANGE;
|
|
|
|
}
|
|
|
|
}
|
2013-12-28 21:08:35 +00:00
|
|
|
|
|
|
|
static struct timespec ts = { .tv_sec=0, .tv_nsec=33333333 };
|
|
|
|
nanosleep(&ts, NULL);
|
|
|
|
}
|
2016-03-26 05:34:33 +00:00
|
|
|
|
|
|
|
prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_STEP, joy_step);
|
|
|
|
prefs_setBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, joy_auto_recenter);
|
2013-12-07 06:55:00 +00:00
|
|
|
}
|
2014-11-09 21:13:27 +00:00
|
|
|
#endif // KEYPAD_JOYSTICK
|
2013-12-07 06:55:00 +00:00
|
|
|
|
|
|
|
void c_calibrate_joystick()
|
|
|
|
{
|
|
|
|
if (joy_mode == JOY_PCJOY)
|
|
|
|
{
|
|
|
|
c_calibrate_pc_joystick();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef KEYPAD_JOYSTICK
|
2016-03-26 05:34:33 +00:00
|
|
|
else if (joy_mode == JOY_KPAD)
|
2013-12-07 06:55:00 +00:00
|
|
|
{
|
|
|
|
c_calibrate_keypad_joystick();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2014-03-23 21:25:28 +00:00
|
|
|
#endif // INTERFACE_CLASSIC
|
2013-12-07 06:55:00 +00:00
|
|
|
|
2019-10-27 20:09:46 +00:00
|
|
|
#if !TESTING
|
2016-04-17 19:51:23 +00:00
|
|
|
// HACK : avoid resetting joystick button values too quickly. This should allow for ClosedApple-Reset. (This is still a
|
|
|
|
// race, but hopefully much less likely to trigger).
|
|
|
|
static void *_joystick_resetDelayed(void *ctx) {
|
|
|
|
(void)ctx;
|
2018-11-23 18:35:38 +00:00
|
|
|
SCOPE_TRACE_INTERFACE("_joystick_resetDelayed");
|
2016-04-17 19:51:23 +00:00
|
|
|
|
|
|
|
// delay
|
|
|
|
sleep(1);
|
|
|
|
|
2018-01-15 21:03:31 +00:00
|
|
|
run_args.joy_button0 = 0x0;
|
|
|
|
run_args.joy_button1 = 0x0;
|
2016-04-17 19:51:23 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-10-27 20:09:46 +00:00
|
|
|
#endif
|
2016-04-17 19:51:23 +00:00
|
|
|
|
2015-04-02 02:59:38 +00:00
|
|
|
void c_joystick_reset(void)
|
2013-12-30 02:07:24 +00:00
|
|
|
{
|
2016-04-17 18:26:35 +00:00
|
|
|
if (joydriver_resetJoystick) {
|
|
|
|
joydriver_resetJoystick();
|
|
|
|
}
|
|
|
|
|
2016-05-03 05:14:42 +00:00
|
|
|
#if TESTING
|
|
|
|
// For "testdisk" determinism, these need to be reset immediately
|
2018-01-15 21:03:31 +00:00
|
|
|
run_args.joy_button0 = 0x0;
|
|
|
|
run_args.joy_button1 = 0x0;
|
2016-05-03 05:14:42 +00:00
|
|
|
#else
|
2016-04-17 19:51:23 +00:00
|
|
|
pthread_t pid;
|
2019-02-24 17:24:12 +00:00
|
|
|
int err = TEMP_FAILURE_RETRY(pthread_create(&pid, NULL, (void *)&_joystick_resetDelayed, (void *)NULL));
|
|
|
|
assert(!err);
|
2016-04-17 19:51:23 +00:00
|
|
|
pthread_detach(pid);
|
2016-05-03 05:14:42 +00:00
|
|
|
#endif
|
2016-04-17 18:26:35 +00:00
|
|
|
|
|
|
|
joy_x = HALF_JOY_RANGE;
|
|
|
|
joy_y = HALF_JOY_RANGE;
|
2013-12-30 02:07:24 +00:00
|
|
|
}
|
|
|
|
|
2015-12-19 21:01:28 +00:00
|
|
|
// clamps modern gamepad controller axis values to the "corners" of a traditional joystick as used on the Apple //e
|
|
|
|
static inline void clampBeyondRadius(uint8_t *x, uint8_t *y) {
|
|
|
|
float half_x = (*x) - HALF_JOY_RANGE;
|
|
|
|
float half_y = (*y) - HALF_JOY_RANGE;
|
|
|
|
float r = sqrtf(half_x*half_x + half_y*half_y);
|
|
|
|
bool shouldClip = (r > HALF_JOY_RANGE);
|
|
|
|
if (joy_clip_to_radius && shouldClip) {
|
|
|
|
if ((*x) < HALF_JOY_RANGE) {
|
|
|
|
(*x) = ((*x) < QUARTER_JOY_RANGE) ? 0.f : (*x);
|
|
|
|
} else {
|
|
|
|
(*x) = ((*x) < HALF_JOY_RANGE+QUARTER_JOY_RANGE) ? (*x) : 0xFF;
|
|
|
|
}
|
|
|
|
if ((*y) < HALF_JOY_RANGE) {
|
|
|
|
(*y) = ((*y) < QUARTER_JOY_RANGE) ? 0.f : (*y);
|
|
|
|
} else {
|
|
|
|
(*y) = ((*y) < HALF_JOY_RANGE+QUARTER_JOY_RANGE) ? (*y) : JOY_RANGE-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void joydriver_setClampBeyondRadius(bool clamp) {
|
|
|
|
joy_clip_to_radius = clamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void joydriver_setAxisValue(uint8_t x, uint8_t y) {
|
|
|
|
clampBeyondRadius(&x, &y);
|
|
|
|
joy_x = x;
|
|
|
|
joy_y = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t joydriver_getAxisX(void) {
|
|
|
|
return joy_x;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return Y axis value
|
|
|
|
uint8_t joydriver_getAxisY(void) {
|
|
|
|
return joy_y;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set button 0 pressed
|
|
|
|
void joydriver_setButton0Pressed(bool pressed) {
|
2018-01-15 21:03:31 +00:00
|
|
|
run_args.joy_button0 = (pressed) ? 0x80 : 0x0;
|
2015-12-19 21:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// set button 1 pressed
|
|
|
|
void joydriver_setButton1Pressed(bool pressed) {
|
2018-01-15 21:03:31 +00:00
|
|
|
run_args.joy_button1 = (pressed) ? 0x80 : 0x0;
|
2015-12-19 21:01:28 +00:00
|
|
|
}
|
|
|
|
|