mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-06-28 13:29:29 +00:00
Refactor prefs into JSON publish/subscribe API
- Breaking changes currently only tested on Linux desktop build =P - Goal is to eventually eliminate most/many of the disparate getter/setter functions to allow better modularity/scaling and platform portability
This commit is contained in:
parent
87ae0f08e0
commit
74a5b74ae3
11
.apple2
11
.apple2
|
@ -1,11 +0,0 @@
|
|||
speed = 1.00
|
||||
altspeed = 4.00
|
||||
disk path = /usr/local/games/apple2/disks
|
||||
color = interpolated
|
||||
video = 1X
|
||||
volume = 8
|
||||
caps lock = 1
|
||||
joystick = joy keypad
|
||||
system path = /usr/local/games/apple2/rom
|
||||
pc joystick parms = 128 128 255 1 255 1
|
||||
keypad joystick parms = 8 1
|
|
@ -45,6 +45,7 @@ static unsigned int remainder_buffer_size = 0;
|
|||
static unsigned long remainder_buffer_size_max = 0;
|
||||
static unsigned int remainder_buffer_idx = 0;
|
||||
|
||||
static long speaker_volume = 0;
|
||||
static int16_t speaker_amplitude = SPKR_DATA_INIT;
|
||||
static int16_t speaker_data = 0;
|
||||
|
||||
|
@ -63,6 +64,22 @@ static AudioBuffer_s *speakerBuffer = NULL;
|
|||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
static void speaker_prefsChanged(const char *domain) {
|
||||
prefs_parseLongValue(domain, PREF_SPEAKER_VOLUME, &speaker_volume, /*base:*/10); // expected range 0-10
|
||||
if (speaker_volume < 0) {
|
||||
speaker_volume = 0;
|
||||
}
|
||||
if (speaker_volume > 10) {
|
||||
speaker_volume = 10;
|
||||
}
|
||||
float samplesScale = speaker_volume/10.f;
|
||||
speaker_amplitude = (int16_t)(SPKR_DATA_INIT * samplesScale);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void _init_speaker(void) {
|
||||
prefs_registerListener(PREF_DOMAIN_AUDIO, &speaker_prefsChanged);
|
||||
}
|
||||
|
||||
/*
|
||||
* Because disk image loading is slow (AKA close-to-original-//e-speed), we may auto-switch to "fullspeed" for faster
|
||||
* loading when all the following heuristics hold true:
|
||||
|
@ -480,11 +497,6 @@ bool speaker_isActive(void) {
|
|||
return speaker_recently_active;
|
||||
}
|
||||
|
||||
void speaker_setVolumeZeroToTen(unsigned long goesToTen) {
|
||||
float samplesScale = goesToTen/10.f;
|
||||
speaker_amplitude = (int16_t)(SPKR_DATA_INIT * samplesScale);
|
||||
}
|
||||
|
||||
double speaker_cyclesPerSample(void) {
|
||||
return cycles_per_sample;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ void speaker_init(void);
|
|||
void speaker_destroy(void);
|
||||
void speaker_reset(void);
|
||||
void speaker_flush(void);
|
||||
void speaker_setVolumeZeroToTen(unsigned long goesToTen);
|
||||
bool speaker_isActive(void);
|
||||
|
||||
/*
|
||||
|
|
|
@ -32,9 +32,11 @@
|
|||
#endif
|
||||
|
||||
// custom annotations
|
||||
#define INPARM
|
||||
#define OUTPARM
|
||||
#define INOUT
|
||||
#define INPARM
|
||||
#define _NONNULL
|
||||
#define _NULLABLE
|
||||
#define OUTPARM
|
||||
#define PRIVATE
|
||||
#define PUBLIC
|
||||
#define READONLY
|
||||
|
@ -65,8 +67,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "json_parse.h"
|
||||
#include "misc.h"
|
||||
#include "vm.h"
|
||||
#include "timing.h"
|
||||
#include "cpu.h"
|
||||
|
|
|
@ -39,6 +39,8 @@ static uint8_t video__wider_font[0x8000] = { 0 };
|
|||
static uint8_t video__font[0x4000] = { 0 };
|
||||
static uint8_t video__int_font[3][0x4000] = { { 0 } }; // interface font
|
||||
|
||||
static color_mode_t color_mode = COLOR_NONE;
|
||||
|
||||
// Precalculated framebuffer offsets given VM addr
|
||||
unsigned int video__screen_addresses[8192] = { INT_MIN };
|
||||
uint8_t video__columns[8192] = { 0 };
|
||||
|
@ -474,6 +476,22 @@ static void _initialize_color() {
|
|||
#endif
|
||||
}
|
||||
|
||||
static void video_prefsChanged(const char *domain) {
|
||||
long val = 0;
|
||||
prefs_parseLongValue(domain, PREF_COLOR_MODE, &val, /*base:*/10);
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
}
|
||||
if (val >= NUM_COLOROPTS) {
|
||||
val = NUM_COLOROPTS-1;
|
||||
}
|
||||
color_mode = (color_mode_t)val;
|
||||
#if TESTING
|
||||
color_mode = COLOR;
|
||||
#endif
|
||||
video_reset();
|
||||
}
|
||||
|
||||
void video_reset(void) {
|
||||
_initialize_hires_values();
|
||||
_initialize_tables_video();
|
||||
|
@ -1531,6 +1549,8 @@ static void _init_interface(void) {
|
|||
_initialize_row_col_tables();
|
||||
_initialize_dhires_values();
|
||||
_initialize_color();
|
||||
|
||||
prefs_registerListener(PREF_DOMAIN_VIDEO, &video_prefsChanged);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_interface(void) {
|
||||
|
|
|
@ -12,6 +12,15 @@
|
|||
#ifndef _DISPLAY_H_
|
||||
#define _DISPLAY_H_
|
||||
|
||||
typedef enum color_mode_t {
|
||||
COLOR_NONE = 0,
|
||||
/*LAZY_COLOR, deprecated*/
|
||||
COLOR,
|
||||
/*LAZY_INTERP, deprecated*/
|
||||
COLOR_INTERP,
|
||||
NUM_COLOROPTS
|
||||
} color_mode_t;
|
||||
|
||||
typedef struct video_animation_s {
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
|
|
121
src/interface.c
121
src/interface.c
|
@ -25,6 +25,8 @@ void (*interface_setGlyphScale)(int glyphScale) = NULL;
|
|||
void (*(*interface_getModelDataSetter)(interface_device_t device))(const char *jsonData) = NULL;
|
||||
#endif
|
||||
|
||||
static char disk_path[PATH_MAX] = { 0 };
|
||||
|
||||
// 2015/04/12 : This was legacy code for rendering the menu interfaces on desktop Linux. Portions here are resurrected
|
||||
// to render HUD messages on desktop and mobile. Nothing special or pretty here, but has "just worked" for 20+ years ;-)
|
||||
|
||||
|
@ -420,7 +422,7 @@ void c_interface_select_diskette( int drive )
|
|||
curpos = entries - 1;
|
||||
}
|
||||
|
||||
char temp[PATH_MAX];
|
||||
char temp[PATH_MAX] = { 0 };
|
||||
for (;;)
|
||||
{
|
||||
for (i = 0; i < 18; i++)
|
||||
|
@ -635,6 +637,8 @@ void c_interface_select_diskette( int drive )
|
|||
snprintf(disk_path + len, MIN(ent_len, PATH_MAX-(len+ent_len)), "/%s", namelist[curpos]->d_name);
|
||||
}
|
||||
|
||||
prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, disk_path);
|
||||
|
||||
nextdir = true;
|
||||
break;
|
||||
}
|
||||
|
@ -754,6 +758,16 @@ void c_interface_parameters()
|
|||
int ch;
|
||||
static interface_enum_t option = OPT_CPU;
|
||||
static int cur_y = 0, cur_off = 0, cur_x = 0, cur_pos = 0;
|
||||
long val = 0;
|
||||
|
||||
prefs_parseLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, &val, /*base:*/10);
|
||||
color_mode_t color_mode = (color_mode_t)val;
|
||||
|
||||
prefs_parseLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, &val, /*base:*/10);
|
||||
/* extern */joy_mode = (joystick_mode_t)val;
|
||||
|
||||
prefs_parseLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, &val, /*base:*/10);
|
||||
long speaker_volume = val;
|
||||
|
||||
/* reset the x position, so we don't lose our cursor if path changes */
|
||||
cur_x = 0;
|
||||
|
@ -765,7 +779,8 @@ void c_interface_parameters()
|
|||
c_interface_print_screen( screen );
|
||||
|
||||
#define TEMPSIZE 1024
|
||||
char temp[TEMPSIZE];
|
||||
char temp[TEMPSIZE] = { 0 };
|
||||
bool shutdown = false;
|
||||
for (;;)
|
||||
{
|
||||
for (i = 0; (i < PARAMS_H) && (i < NUM_OPTIONS); i++)
|
||||
|
@ -828,13 +843,13 @@ void c_interface_parameters()
|
|||
break;
|
||||
|
||||
case OPT_VOLUME:
|
||||
if (sound_volume == 0)
|
||||
if (speaker_volume == 0)
|
||||
{
|
||||
snprintf(temp, TEMPSIZE, "%s", "Off ");
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(temp, TEMPSIZE, "%d", sound_volume);
|
||||
snprintf(temp, TEMPSIZE, "%ld", speaker_volume);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -961,6 +976,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_scale_factor = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, cpu_scale_factor);
|
||||
break;
|
||||
|
||||
case OPT_ALTCPU:
|
||||
|
@ -969,6 +985,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_altscale_factor = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, cpu_altscale_factor);
|
||||
break;
|
||||
|
||||
case OPT_PATH:
|
||||
|
@ -983,14 +1000,8 @@ void c_interface_parameters()
|
|||
break;
|
||||
|
||||
case OPT_COLOR:
|
||||
if (color_mode == 0)
|
||||
{
|
||||
color_mode = NUM_COLOROPTS-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
--color_mode;
|
||||
}
|
||||
color_mode = (color_mode == 0) ? NUM_COLOROPTS-1 : color_mode-1;
|
||||
prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, color_mode);
|
||||
break;
|
||||
|
||||
#if !VIDEO_OPENGL
|
||||
|
@ -1009,27 +1020,22 @@ void c_interface_parameters()
|
|||
#endif
|
||||
|
||||
case OPT_VOLUME:
|
||||
if (sound_volume > 0)
|
||||
if (speaker_volume > 0)
|
||||
{
|
||||
--sound_volume;
|
||||
--speaker_volume;
|
||||
}
|
||||
prefs_setLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, speaker_volume);
|
||||
break;
|
||||
|
||||
case OPT_CAPS:
|
||||
if (caps_lock) {
|
||||
caps_lock = false;
|
||||
}
|
||||
use_system_caps_lock = true;
|
||||
prefs_setBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, caps_lock);
|
||||
break;
|
||||
|
||||
case OPT_JOYSTICK:
|
||||
if (joy_mode == 0)
|
||||
{
|
||||
joy_mode = NUM_JOYOPTS-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
--joy_mode;
|
||||
}
|
||||
joy_mode = (joy_mode == 0) ? NUM_JOYOPTS-1 : joy_mode-1;
|
||||
prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, joy_mode);
|
||||
break;
|
||||
|
||||
case OPT_CALIBRATE:
|
||||
|
@ -1055,6 +1061,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_scale_factor = CPU_SCALE_FASTEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, cpu_scale_factor);
|
||||
break;
|
||||
|
||||
case OPT_ALTCPU:
|
||||
|
@ -1063,6 +1070,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_altscale_factor = CPU_SCALE_FASTEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, cpu_altscale_factor);
|
||||
break;
|
||||
|
||||
case OPT_PATH:
|
||||
|
@ -1080,14 +1088,8 @@ void c_interface_parameters()
|
|||
break;
|
||||
|
||||
case OPT_COLOR:
|
||||
if (color_mode == NUM_COLOROPTS-1)
|
||||
{
|
||||
color_mode = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++color_mode;
|
||||
}
|
||||
color_mode = (color_mode >= NUM_COLOROPTS-1) ? 0 : color_mode+1;
|
||||
prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, color_mode);
|
||||
break;
|
||||
|
||||
#if !VIDEO_OPENGL
|
||||
|
@ -1106,28 +1108,23 @@ void c_interface_parameters()
|
|||
#endif
|
||||
|
||||
case OPT_VOLUME:
|
||||
sound_volume++;
|
||||
if (sound_volume > 10)
|
||||
speaker_volume++;
|
||||
if (speaker_volume > 10)
|
||||
{
|
||||
sound_volume = 10;
|
||||
speaker_volume = 10;
|
||||
}
|
||||
prefs_setLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, speaker_volume);
|
||||
break;
|
||||
|
||||
case OPT_CAPS:
|
||||
if (!caps_lock) {
|
||||
caps_lock = true;
|
||||
}
|
||||
use_system_caps_lock = false;
|
||||
prefs_setBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, caps_lock);
|
||||
break;
|
||||
|
||||
case OPT_JOYSTICK:
|
||||
if (joy_mode == NUM_JOYOPTS-1)
|
||||
{
|
||||
joy_mode = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++joy_mode;
|
||||
}
|
||||
joy_mode = (joy_mode == NUM_JOYOPTS-1) ? 0 : joy_mode+1;
|
||||
prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, joy_mode);
|
||||
break;
|
||||
|
||||
case OPT_CALIBRATE:
|
||||
|
@ -1149,8 +1146,9 @@ void c_interface_parameters()
|
|||
video_reset();
|
||||
vm_reinitializeAudio();
|
||||
c_joystick_reset();
|
||||
prefs_save();
|
||||
c_interface_exit(ch);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
else if ((ch == '?') && (option != OPT_PATH))
|
||||
{
|
||||
|
@ -1194,14 +1192,14 @@ void c_interface_parameters()
|
|||
{
|
||||
int i;
|
||||
|
||||
strncpy(temp, disk_path, TEMPSIZE);
|
||||
strncpy(temp, disk_path, TEMPSIZE-1);
|
||||
for (i = strlen(temp); i >= cur_pos + cur_x; i--)
|
||||
{
|
||||
temp[ i + 1 ] = temp[ i ];
|
||||
}
|
||||
|
||||
temp[ cur_pos + cur_x ] = ch;
|
||||
strncpy(disk_path, temp, PATH_MAX);
|
||||
strncpy(disk_path, temp, PATH_MAX-1);
|
||||
if (cur_x < INTERFACE_PATH_MAX-1)
|
||||
{
|
||||
cur_x++;
|
||||
|
@ -1210,6 +1208,8 @@ void c_interface_parameters()
|
|||
{
|
||||
cur_pos++;
|
||||
}
|
||||
|
||||
prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, disk_path);
|
||||
}
|
||||
|
||||
/* Backspace or delete setting path */
|
||||
|
@ -1230,6 +1230,8 @@ void c_interface_parameters()
|
|||
{
|
||||
cur_pos--;
|
||||
}
|
||||
|
||||
prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, disk_path);
|
||||
}
|
||||
|
||||
/* calibrate joystick */
|
||||
|
@ -1269,7 +1271,7 @@ void c_interface_parameters()
|
|||
ch = toupper(ch);
|
||||
if (ch == 'Y')
|
||||
{
|
||||
save_settings();
|
||||
prefs_save();
|
||||
disk6_eject(0);
|
||||
c_interface_print_screen( screen );
|
||||
disk6_eject(1);
|
||||
|
@ -1277,9 +1279,9 @@ void c_interface_parameters()
|
|||
#ifdef __linux__
|
||||
LOG("Back to Linux, w00t!\n");
|
||||
#endif
|
||||
video_shutdown(false); // soft quit video_main_loop()
|
||||
shutdown = true;
|
||||
c_interface_exit(ch);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1299,7 +1301,7 @@ void c_interface_parameters()
|
|||
c_joystick_reset();
|
||||
cpu65_reboot();
|
||||
c_interface_exit(ch);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1307,6 +1309,12 @@ void c_interface_parameters()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shutdown) {
|
||||
video_shutdown(/*emulatorShuttingDown:*/false); // soft quit video_main_loop()
|
||||
} else {
|
||||
prefs_sync(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
|
@ -1537,6 +1545,15 @@ static void *interface_thread(void *current_key)
|
|||
|
||||
cpu_pause();
|
||||
|
||||
char *path = NULL;
|
||||
prefs_copyStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, &path);
|
||||
if (!path) {
|
||||
ASPRINTF(&path, "%s", getenv("HOME"));
|
||||
}
|
||||
strncpy(disk_path, path, PATH_MAX-1);
|
||||
disk_path[PATH_MAX-1] = '\0';
|
||||
FREE(path);
|
||||
|
||||
switch ((__SWORD_TYPE)current_key) {
|
||||
case kF1:
|
||||
c_interface_select_diskette( 0 );
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
extern void copy_and_pad_string(char *dest, const char* src, const char c, const int len, const char cap);
|
||||
#endif
|
||||
|
||||
joystick_mode_t joy_mode = JOY_PCJOY;
|
||||
|
||||
/* parameters for generic and keyboard-simulated joysticks */
|
||||
uint16_t joy_x = HALF_JOY_RANGE;
|
||||
uint16_t joy_y = HALF_JOY_RANGE;
|
||||
|
@ -30,10 +32,33 @@ bool joy_clip_to_radius = false;
|
|||
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
short joy_step = 1;
|
||||
uint8_t joy_auto_recenter = 0;
|
||||
bool joy_auto_recenter = false;
|
||||
#endif
|
||||
|
||||
static void joystick_prefsChanged(const char *domain) {
|
||||
assert(strcmp(domain, PREF_DOMAIN_JOYSTICK) == 0);
|
||||
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
long lVal = 0;
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void _init_joystick(void) {
|
||||
prefs_registerListener(PREF_DOMAIN_JOYSTICK, &joystick_prefsChanged);
|
||||
}
|
||||
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
c_calibrate_pc_joystick() - calibrates joystick. determines extreme
|
||||
and center coordinates. assumes that it can write to the interface
|
||||
|
@ -109,7 +134,7 @@ static void c_calibrate_pc_joystick()
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(KEYPAD_JOYSTICK)
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
static void c_calibrate_keypad_joystick()
|
||||
{
|
||||
|
||||
|
@ -216,6 +241,9 @@ static void c_calibrate_keypad_joystick()
|
|||
static struct timespec ts = { .tv_sec=0, .tv_nsec=33333333 };
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_STEP, joy_step);
|
||||
prefs_setBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, joy_auto_recenter);
|
||||
}
|
||||
#endif // KEYPAD_JOYSTICK
|
||||
|
||||
|
@ -227,7 +255,7 @@ void c_calibrate_joystick()
|
|||
}
|
||||
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
if (joy_mode == JOY_KPAD)
|
||||
else if (joy_mode == JOY_KPAD)
|
||||
{
|
||||
c_calibrate_keypad_joystick();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,16 @@
|
|||
#define HALF_JOY_RANGE (JOY_RANGE>>1)
|
||||
#define QUARTER_JOY_RANGE (HALF_JOY_RANGE>>1)
|
||||
|
||||
typedef enum joystick_mode_t {
|
||||
JOY_PCJOY = 0,
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
JOY_KPAD,
|
||||
#endif
|
||||
NUM_JOYOPTS
|
||||
} joystick_mode_t;
|
||||
|
||||
extern joystick_mode_t joy_mode;
|
||||
|
||||
extern uint16_t joy_x;
|
||||
extern uint16_t joy_y;
|
||||
extern uint8_t joy_button0;
|
||||
|
@ -24,11 +34,12 @@ extern uint8_t joy_button2;
|
|||
extern bool joy_clip_to_radius;
|
||||
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
extern uint8_t joy_auto_recenter;
|
||||
extern bool joy_auto_recenter;
|
||||
extern short joy_step;
|
||||
#endif
|
||||
|
||||
void c_joystick_reset(void);
|
||||
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
void c_calibrate_joystick(void);
|
||||
#endif
|
||||
|
|
|
@ -262,6 +262,9 @@ static int _json_createFromString(const char *jsonString, INOUT JSON_ref *jsonRe
|
|||
case JSMN_ERROR_PART:
|
||||
ERRLOG("%s", "String is not a complete JSON packet, moar bytes expected");
|
||||
break;
|
||||
case JSMN_ERROR_PRIMITIVE_INVAL:
|
||||
ERRLOG("%s", "Invalid character inside JSON primitive");
|
||||
break;
|
||||
default:
|
||||
ERRLOG("UNKNOWN errCount : %d", errCount);
|
||||
break;
|
||||
|
@ -460,6 +463,7 @@ int json_mapCopyJSON(const JSON_ref jsonRef, const char *key, INOUT JSON_ref *va
|
|||
}
|
||||
|
||||
errCount = json_createFromString(str, val);
|
||||
assert(errCount >= 0);
|
||||
FREE(str);
|
||||
|
||||
} while (0);
|
||||
|
|
17
src/keys.c
17
src/keys.c
|
@ -15,12 +15,10 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
/* from misc.c */
|
||||
extern uid_t user, privileged;
|
||||
|
||||
static int next_key = -1;
|
||||
static int last_scancode = -1;
|
||||
bool caps_lock = true; // default enabled because so much breaks otherwise
|
||||
bool use_system_caps_lock = false;
|
||||
|
||||
/* ----------------------------------------------------
|
||||
//e Keymap. Mapping scancodes to Apple //e US Keyboard
|
||||
|
@ -345,7 +343,7 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked)
|
|||
} while(0);
|
||||
}
|
||||
|
||||
#if defined(KEYPAD_JOYSTICK)
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
// Keypad emulated joystick relies on "raw" keyboard input
|
||||
if (joy_mode == JOY_KPAD)
|
||||
{
|
||||
|
@ -484,6 +482,17 @@ bool c_keys_is_interface_key(int key)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void keys_prefsChanged(const char *domain) {
|
||||
bool val = false;
|
||||
if (prefs_parseBoolValue(domain, PREF_KEYBOARD_CAPS, &val)) {
|
||||
caps_lock = val;
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_keys(void) {
|
||||
prefs_registerListener(PREF_DOMAIN_KEYBOARD, &keys_prefsChanged);
|
||||
}
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
bool (*keydriver_isTouchKeyboardAvailable)(void) = NULL;
|
||||
void (*keydriver_setTouchKeyboardEnabled)(bool enabled) = NULL;
|
||||
|
|
|
@ -136,6 +136,7 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
extern bool caps_lock;
|
||||
extern bool use_system_caps_lock;
|
||||
|
||||
int c_mygetch(int block);
|
||||
int c_rawkey();
|
||||
|
@ -183,3 +184,4 @@ extern void (*keydriver_loadAltKbd)(const char *kbdPath);
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1415,9 +1415,7 @@ void c_interface_debugging() {
|
|||
int ch;
|
||||
int command_pos = PROMPT_X;
|
||||
|
||||
opcodes = (apple_mode == 0) ? opcodes_6502 :
|
||||
(apple_mode == 1) ? opcodes_undoc :
|
||||
opcodes_65c02;
|
||||
opcodes = opcodes_65c02;
|
||||
|
||||
/* initialize the buffers */
|
||||
for (i=0; i<BUF_Y; i++)
|
||||
|
|
|
@ -28,8 +28,6 @@ static module_ctor_node_s *head = NULL;
|
|||
|
||||
bool do_logging = true; // also controlled by NDEBUG
|
||||
FILE *error_log = NULL;
|
||||
int sound_volume = 2;
|
||||
color_mode_t color_mode = COLOR;
|
||||
const char *data_dir = NULL;
|
||||
char **argv = NULL;
|
||||
int argc = 0;
|
||||
|
@ -278,7 +276,7 @@ void emulator_start(void) {
|
|||
head = NULL;
|
||||
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
load_settings(); // user prefs
|
||||
prefs_load(); // user prefs
|
||||
c_keys_set_key(kF8); // show credits before emulation start
|
||||
#endif
|
||||
|
||||
|
@ -293,6 +291,7 @@ void emulator_shutdown(void) {
|
|||
disk6_eject(0);
|
||||
disk6_eject(1);
|
||||
video_shutdown(/*emulatorShuttingDown:*/true);
|
||||
prefs_shutdown(/*emulatorShuttingDown:*/true);
|
||||
timing_stopCPU();
|
||||
_shutdown_threads();
|
||||
}
|
||||
|
|
685
src/prefs.c
685
src/prefs.c
|
@ -13,318 +13,92 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "prefs.h"
|
||||
#include "json_parse_private.h"
|
||||
|
||||
#include "common.h"
|
||||
typedef struct prefs_listener_s {
|
||||
prefs_change_callback_f prefsChanged;
|
||||
struct prefs_listener_s *nextListener;
|
||||
} prefs_listener_s;
|
||||
|
||||
#define PRM_NONE 0
|
||||
#define PRM_SPEED 1
|
||||
#define PRM_ALTSPEED 101
|
||||
#define PRM_MODE 2
|
||||
#define PRM_DISK_PATH 3
|
||||
#define PRM_HIRES_COLOR 4
|
||||
#define PRM_VOLUME 5
|
||||
#define PRM_JOY_INPUT 6
|
||||
#define PRM_VIDEO_MODE 7
|
||||
#define PRM_JOY_KPAD_CALIBRATE 11
|
||||
#define PRM_ROM_PATH 12
|
||||
#define PRM_CAPSLOCK 102
|
||||
typedef struct prefs_domain_s {
|
||||
char *domain;
|
||||
struct prefs_listener_s *listeners;
|
||||
struct prefs_domain_s *nextDomain;
|
||||
} prefs_domain_s;
|
||||
|
||||
static JSON_ref jsonPrefs = NULL;
|
||||
static prefs_domain_s *domains = NULL;
|
||||
static char *prefsFile = NULL;
|
||||
|
||||
char system_path[PATH_MAX] = { 0 };
|
||||
char disk_path[PATH_MAX] = { 0 };
|
||||
static pthread_mutex_t prefsLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#warning FIXME TODO : completely excise deprecated apple_mode stuff
|
||||
int apple_mode = 2/*IIE_MODE*/;
|
||||
a2_video_mode_t a2_video_mode = VIDEO_1X;
|
||||
joystick_mode_t joy_mode = JOY_PCJOY;
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static char *config_filename = NULL;
|
||||
void prefs_load(void) {
|
||||
FREE(prefsFile);
|
||||
|
||||
struct match_table
|
||||
{
|
||||
const char *tag;
|
||||
int value;
|
||||
};
|
||||
|
||||
static const struct match_table prefs_table[] =
|
||||
{
|
||||
{ "speed", PRM_SPEED },
|
||||
{ "altspeed", PRM_ALTSPEED },
|
||||
{ "mode", PRM_MODE },
|
||||
{ "path", PRM_DISK_PATH },
|
||||
{ "disk path", PRM_DISK_PATH },
|
||||
{ "disk_path", PRM_DISK_PATH },
|
||||
{ "path", PRM_DISK_PATH },
|
||||
{ "color", PRM_HIRES_COLOR },
|
||||
{ "video", PRM_VIDEO_MODE },
|
||||
{ "volume", PRM_VOLUME },
|
||||
{ "caps_lock", PRM_CAPSLOCK },
|
||||
{ "caps lock", PRM_CAPSLOCK },
|
||||
{ "joystick", PRM_JOY_INPUT },
|
||||
{ "keypad joystick parms", PRM_JOY_KPAD_CALIBRATE },
|
||||
{ "keypad_joystick_parms", PRM_JOY_KPAD_CALIBRATE },
|
||||
{ "system path", PRM_ROM_PATH },
|
||||
{ "system_path", PRM_ROM_PATH },
|
||||
{ 0, PRM_NONE }
|
||||
};
|
||||
|
||||
static const struct match_table color_table[] =
|
||||
{
|
||||
{ "black/white", COLOR_NONE },
|
||||
/*{ "lazy color", LAZY_COLOR }, deprecated*/
|
||||
{ "color", COLOR },
|
||||
/*{ "lazy interpolated", LAZY_INTERP }, deprecated*/
|
||||
{ "interpolated", COLOR_INTERP },
|
||||
{ "off", 0 },
|
||||
{ "on", COLOR },
|
||||
{ 0, COLOR }
|
||||
};
|
||||
|
||||
static const struct match_table video_table[] =
|
||||
{
|
||||
{ "1X", VIDEO_1X },
|
||||
{ "2X", VIDEO_2X },
|
||||
{ "Fullscreen", VIDEO_FULLSCREEN },
|
||||
{ 0, VIDEO_1X }
|
||||
};
|
||||
|
||||
static const struct match_table volume_table[] =
|
||||
{
|
||||
{ "0", 0 },
|
||||
{ "1", 1 },
|
||||
{ "2", 2 },
|
||||
{ "3", 3 },
|
||||
{ "4", 4 },
|
||||
{ "5", 5 },
|
||||
{ "6", 6 },
|
||||
{ "7", 7 },
|
||||
{ "8", 8 },
|
||||
{ "9", 9 },
|
||||
{ "10", 10 },
|
||||
{ 0, 10 },
|
||||
};
|
||||
|
||||
static const struct match_table capslock_table[] =
|
||||
{
|
||||
{ "0", 0 },
|
||||
{ "1", 1 },
|
||||
};
|
||||
|
||||
static const struct match_table joy_input_table[] =
|
||||
{
|
||||
{ "pc joystick", JOY_PCJOY },
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
{ "joy keypad", JOY_KPAD },
|
||||
{ "joy_keypad", JOY_KPAD },
|
||||
#endif
|
||||
{ 0, JOY_PCJOY }
|
||||
};
|
||||
|
||||
/* Find the number assigned to KEYWORD in a match table PARADIGM. If no match,
|
||||
* then the value associated with the terminating entry is used as a
|
||||
* default. */
|
||||
static int match(const struct match_table *paradigm, const char *keyword)
|
||||
{
|
||||
while (paradigm->tag && strcasecmp(paradigm->tag, keyword))
|
||||
{
|
||||
paradigm++;
|
||||
const char *apple2JSON = getenv("APPLE2IX_JSON");
|
||||
if (apple2JSON) {
|
||||
ASPRINTF(&prefsFile, "%s", apple2JSON);
|
||||
}
|
||||
|
||||
return paradigm->value;
|
||||
if (!prefsFile) {
|
||||
ASPRINTF(&prefsFile, "%s/.apple2.json", getenv("HOME"));
|
||||
}
|
||||
assert(prefsFile);
|
||||
|
||||
json_destroy(&jsonPrefs);
|
||||
int tokCount = json_createFromFile(prefsFile, &jsonPrefs);
|
||||
if (tokCount < 0) {
|
||||
tokCount = json_createFromString("{}", &jsonPrefs);
|
||||
assert(tokCount > 0);
|
||||
}
|
||||
|
||||
/* Reverse match -- find a keyword associated with number KEY in
|
||||
* PARADIGM. The first match is used -- synonym keywords appearing later
|
||||
* in the table are not chosen.
|
||||
*
|
||||
* A null is returned for no match.
|
||||
*/
|
||||
static const char *reverse_match(const struct match_table *paradigm, int key)
|
||||
{
|
||||
while (paradigm->tag && key != paradigm->value)
|
||||
{
|
||||
paradigm++;
|
||||
prefs_sync(NULL);
|
||||
}
|
||||
|
||||
return paradigm->tag;
|
||||
void prefs_loadString(const char *jsonString) {
|
||||
json_destroy(&jsonPrefs);
|
||||
int tokCount = json_createFromString(jsonString, &jsonPrefs);
|
||||
if (tokCount < 0) {
|
||||
tokCount = json_createFromString("{}", &jsonPrefs);
|
||||
assert(tokCount > 0);
|
||||
}
|
||||
|
||||
/* Eat leading and trailing whitespace of string X. The old string is
|
||||
* overwritten and a new pointer is returned.
|
||||
*/
|
||||
static char * clean_string(char *x)
|
||||
{
|
||||
size_t y;
|
||||
|
||||
/* Leading white space */
|
||||
while (isspace(*x))
|
||||
{
|
||||
x++;
|
||||
prefs_sync(NULL);
|
||||
}
|
||||
|
||||
/* Trailing white space */
|
||||
y = strlen(x);
|
||||
while (y && x[y--] == ' ')
|
||||
{
|
||||
}
|
||||
bool prefs_save(void) {
|
||||
|
||||
x[y] = 0;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Load the configuration. Must be called *once* at start. */
|
||||
void load_settings(void)
|
||||
{
|
||||
/* set system defaults before user defaults. */
|
||||
strcpy(disk_path, "./disks");
|
||||
strcpy(system_path, "./rom");
|
||||
|
||||
const char *apple2cfg = getenv("APPLE2IXCFG");
|
||||
if (apple2cfg) {
|
||||
config_filename = strdup(apple2cfg);
|
||||
} else {
|
||||
const char *homedir;
|
||||
|
||||
homedir = getenv("HOME");
|
||||
config_filename = malloc(strlen(homedir) + 9);
|
||||
strcpy(config_filename, homedir);
|
||||
strcat(config_filename, "/.apple2");
|
||||
|
||||
/* config_filename is left allocated for convinence in
|
||||
* save_settings */
|
||||
}
|
||||
|
||||
{
|
||||
char *buffer = 0;
|
||||
size_t size = 0;
|
||||
|
||||
FILE *config_file = fopen(config_filename, "r");
|
||||
if (config_file == NULL)
|
||||
{
|
||||
ERRLOG(
|
||||
"Warning. Cannot open the .apple2 system defaults file.\n"
|
||||
"Make sure it's readable in your home directory.");
|
||||
return;
|
||||
}
|
||||
|
||||
while (getline(&buffer, &size, config_file) != -1)
|
||||
{
|
||||
char *parameter;
|
||||
char *argument;
|
||||
|
||||
/* Split line between parameter and argument */
|
||||
|
||||
parameter = buffer;
|
||||
argument = strchr(buffer, '=');
|
||||
argument[0] = 0;
|
||||
argument++;
|
||||
|
||||
parameter = clean_string(parameter);
|
||||
argument = clean_string(argument);
|
||||
|
||||
int main_match = match(prefs_table, parameter);
|
||||
switch (main_match)
|
||||
{
|
||||
case PRM_NONE:
|
||||
ERRLOG("Unrecognized config parameter `%s'", parameter);
|
||||
break;
|
||||
|
||||
case PRM_SPEED:
|
||||
case PRM_ALTSPEED:
|
||||
{
|
||||
double x = strtod(argument, NULL);
|
||||
if (x > CPU_SCALE_FASTEST)
|
||||
{
|
||||
x = CPU_SCALE_FASTEST;
|
||||
}
|
||||
else if (x < CPU_SCALE_SLOWEST)
|
||||
{
|
||||
x = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
if (main_match == PRM_SPEED)
|
||||
{
|
||||
cpu_scale_factor = x;
|
||||
}
|
||||
else
|
||||
{
|
||||
cpu_altscale_factor = x;
|
||||
}
|
||||
int fd = -1;
|
||||
bool success = false;
|
||||
do {
|
||||
if (!prefsFile) {
|
||||
ERRLOG("Not saving preferences, no file loaded...");
|
||||
break;
|
||||
}
|
||||
|
||||
case PRM_DISK_PATH:
|
||||
strncpy(disk_path, argument, PATH_MAX-1);
|
||||
disk_path[PATH_MAX-1] = '\0';
|
||||
break;
|
||||
|
||||
case PRM_HIRES_COLOR:
|
||||
color_mode = match(color_table, argument);
|
||||
break;
|
||||
|
||||
case PRM_VIDEO_MODE:
|
||||
a2_video_mode = match(video_table, argument);
|
||||
break;
|
||||
|
||||
case PRM_VOLUME:
|
||||
sound_volume = match(volume_table, argument);
|
||||
break;
|
||||
|
||||
case PRM_CAPSLOCK:
|
||||
caps_lock = match(capslock_table, argument) ? true : false;
|
||||
break;
|
||||
|
||||
case PRM_JOY_INPUT:
|
||||
joy_mode = match(joy_input_table, argument);
|
||||
break;
|
||||
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
case PRM_JOY_KPAD_CALIBRATE:
|
||||
joy_step = strtol(argument, &argument, 10);
|
||||
if (joy_step < 1)
|
||||
{
|
||||
joy_step = 1;
|
||||
}
|
||||
else if (joy_step > 255)
|
||||
{
|
||||
joy_step = 255;
|
||||
}
|
||||
|
||||
joy_auto_recenter = strtol(argument, &argument, 10);
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PRM_ROM_PATH:
|
||||
strncpy(system_path, argument, PATH_MAX-1);
|
||||
system_path[PATH_MAX-1] = '\0';
|
||||
if (!jsonPrefs) {
|
||||
ERRLOG("Not saving preferences, none loaded...");
|
||||
break;
|
||||
}
|
||||
|
||||
if (((JSON_s *)jsonPrefs)->numTokens <= 0) {
|
||||
ERRLOG("Not saving preferences, no preferences loaded...");
|
||||
break;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
fclose(config_file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Save the configuration */
|
||||
bool save_settings(void)
|
||||
{
|
||||
FILE *config_file = NULL;
|
||||
|
||||
LOG("Saving preferences...");
|
||||
assert(((JSON_s *)jsonPrefs)->jsonString && "string should be valid");
|
||||
|
||||
#define PREFS_ERRPRINT() \
|
||||
ERRLOG( \
|
||||
"Cannot open the .apple2/apple2.cfg system defaults file for writing.\n" \
|
||||
"Make sure it has rw permission in your home directory.")
|
||||
"Cannot open the .apple2.json preferences file for writing.\n" \
|
||||
"Make sure it has R/W permission in your home directory.")
|
||||
|
||||
#define ERROR_SUBMENU_H 9
|
||||
#define ERROR_SUBMENU_H 8
|
||||
#define ERROR_SUBMENU_W 40
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
#if defined(INTERFACE_CLASSIC) && !TESTING
|
||||
int ch = -1;
|
||||
char submenu[ERROR_SUBMENU_H][ERROR_SUBMENU_W+1] =
|
||||
//1. 5. 10. 15. 20. 25. 30. 35. 40.
|
||||
|
@ -332,52 +106,333 @@ bool save_settings(void)
|
|||
"| |",
|
||||
"| |",
|
||||
"| OOPS, could not open or write to the |",
|
||||
"| .apple2/apple2.cfg file in your HOME |",
|
||||
"| directory ... |",
|
||||
"| .apple2.json preferences file |",
|
||||
"| |",
|
||||
"| |",
|
||||
"||||||||||||||||||||||||||||||||||||||||" };
|
||||
#endif
|
||||
|
||||
config_file = fopen(config_filename, "w");
|
||||
if (config_file == NULL)
|
||||
{
|
||||
TEMP_FAILURE_RETRY(fd = open(prefsFile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR));
|
||||
if (fd == -1) {
|
||||
PREFS_ERRPRINT();
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
#if defined(INTERFACE_CLASSIC) && !TESTING
|
||||
c_interface_print_submenu_centered(submenu[0], ERROR_SUBMENU_W, ERROR_SUBMENU_H);
|
||||
while ((ch = c_mygetch(1)) == -1)
|
||||
{
|
||||
while ((ch = c_mygetch(1)) == -1) {
|
||||
// ...
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
int err = 0;
|
||||
err |= fprintf(config_file, "speed = %0.2lf\n", cpu_scale_factor);
|
||||
err |= fprintf(config_file, "altspeed = %0.2lf\n", cpu_altscale_factor);
|
||||
err |= fprintf(config_file, "disk path = %s\n", disk_path);
|
||||
err |= fprintf(config_file, "color = %s\n", reverse_match(color_table, color_mode));
|
||||
err |= fprintf(config_file, "video = %s\n", reverse_match(video_table, a2_video_mode));
|
||||
err |= fprintf(config_file, "volume = %s\n", reverse_match(volume_table, sound_volume));
|
||||
err |= fprintf(config_file, "caps lock = %s\n", reverse_match(capslock_table, (int)caps_lock));
|
||||
err |= fprintf(config_file, "joystick = %s\n", reverse_match(joy_input_table, joy_mode));
|
||||
err |= fprintf(config_file, "system path = %s\n", system_path);
|
||||
success = json_serialize(jsonPrefs, fd, /*pretty:*/true);
|
||||
} while (0);
|
||||
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
err |= fprintf(config_file, "keypad joystick parms = %d %u\n", joy_step, (joy_auto_recenter ? 1 : 0));
|
||||
#endif
|
||||
|
||||
if (err < 0) {
|
||||
PREFS_ERRPRINT();
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
c_interface_print_submenu_centered(submenu[0], ERROR_SUBMENU_W, ERROR_SUBMENU_H);
|
||||
while ((ch = c_mygetch(1)) == -1) { }
|
||||
#endif
|
||||
return false;
|
||||
if (fd != -1) {
|
||||
TEMP_FAILURE_RETRY(fsync(fd));
|
||||
TEMP_FAILURE_RETRY(close(fd));
|
||||
}
|
||||
|
||||
fclose(config_file);
|
||||
|
||||
return true;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool prefs_copyStringValue(const char *domain, const char *key, INOUT char **val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapCopyStringValue(jsonRef, key, val);
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_parseLongValue(const char *domain, const char *key, INOUT long *val, const long base) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapParseLongValue(jsonRef, key, val, base);
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_parseBoolValue(const char *domain, const char *key, INOUT bool *val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapParseBoolValue(jsonRef, key, val);
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_parseFloatValue(const char *domain, const char *key, INOUT float *val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapParseFloatValue(jsonRef, key, val);
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_setStringValue(const char *domain, const char * _NONNULL key, const char * _NONNULL val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount <= 0) {
|
||||
errCount = json_createFromString("{}", &jsonRef);
|
||||
assert(errCount > 0);
|
||||
assert(jsonRef);
|
||||
}
|
||||
|
||||
ret = json_mapSetStringValue(jsonRef, key, val);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapSetJSONValue(jsonPrefs, domain, jsonRef);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_setLongValue(const char * _NONNULL domain, const char * _NONNULL key, long val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount <= 0) {
|
||||
errCount = json_createFromString("{}", &jsonRef);
|
||||
assert(errCount > 0);
|
||||
assert(jsonRef);
|
||||
}
|
||||
|
||||
ret = json_mapSetLongValue(jsonRef, key, val);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapSetJSONValue(jsonPrefs, domain, jsonRef);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_setBoolValue(const char * _NONNULL domain, const char * _NONNULL key, bool val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount <= 0) {
|
||||
errCount = json_createFromString("{}", &jsonRef);
|
||||
assert(errCount > 0);
|
||||
assert(jsonRef);
|
||||
}
|
||||
|
||||
ret = json_mapSetBoolValue(jsonRef, key, val);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapSetJSONValue(jsonPrefs, domain, jsonRef);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_setFloatValue(const char * _NONNULL domain, const char * _NONNULL key, float val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount <= 0) {
|
||||
errCount = json_createFromString("{}", &jsonRef);
|
||||
assert(errCount > 0);
|
||||
assert(jsonRef);
|
||||
}
|
||||
|
||||
ret = json_mapSetFloatValue(jsonRef, key, val);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapSetJSONValue(jsonPrefs, domain, jsonRef);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void prefs_registerListener(const char *domain, prefs_change_callback_f callback) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
assert(domain && "listener needs to specify non-NULL domain");
|
||||
|
||||
prefs_domain_s *dom = domains;
|
||||
while (dom) {
|
||||
if (strcmp(domain, dom->domain) == 0) {
|
||||
break;
|
||||
}
|
||||
dom = dom->nextDomain;
|
||||
}
|
||||
|
||||
if (!dom) {
|
||||
dom = MALLOC(sizeof(*dom));
|
||||
dom->domain = STRDUP(domain);
|
||||
dom->nextDomain = domains;
|
||||
dom->listeners = NULL;
|
||||
domains = dom;
|
||||
}
|
||||
|
||||
prefs_listener_s *newL = MALLOC(sizeof(*newL));
|
||||
prefs_listener_s *oldL = dom->listeners;
|
||||
dom->listeners = newL;
|
||||
newL->nextListener = oldL;
|
||||
newL->prefsChanged = callback;
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
}
|
||||
|
||||
void prefs_sync(const char *domain) {
|
||||
static int syncCount = 0;
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
++syncCount;
|
||||
if (syncCount > 1) {
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
|
||||
prefs_domain_s *dom = domains;
|
||||
do {
|
||||
while (dom) {
|
||||
if (domain && (strcmp(domain, dom->domain) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
prefs_listener_s *listener = dom->listeners;
|
||||
while (listener) {
|
||||
listener->prefsChanged(dom->domain);
|
||||
listener = listener->nextListener;
|
||||
}
|
||||
|
||||
if (domain) {
|
||||
break;
|
||||
}
|
||||
|
||||
dom = dom->nextDomain;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
--syncCount;
|
||||
if (syncCount == 0) {
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void prefs_shutdown(bool emulatorShuttingDown) {
|
||||
if (!emulatorShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
FREE(prefsFile);
|
||||
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
prefs_domain_s *dom = domains;
|
||||
domains = NULL;
|
||||
|
||||
while (dom) {
|
||||
prefs_listener_s *listener = dom->listeners;
|
||||
while (listener) {
|
||||
prefs_listener_s *dead = listener;
|
||||
listener = listener->nextListener;
|
||||
FREE(dead);
|
||||
}
|
||||
|
||||
prefs_domain_s *dead = dom;
|
||||
dom = dom->nextDomain;
|
||||
|
||||
FREE(dead->domain);
|
||||
FREE(dead);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
}
|
||||
|
||||
|
|
118
src/prefs.h
118
src/prefs.h
|
@ -9,52 +9,88 @@
|
|||
* Copyright 1995 Stephen Lee
|
||||
* Copyright 1997, 1998 Aaron Culliney
|
||||
* Copyright 1998, 1999, 2000 Michael Deutschmann
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
* Copyright 2013-2016 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PREFS_H
|
||||
#define PREFS_H
|
||||
#ifndef _PREFS_H_
|
||||
#define _PREFS_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef enum joystick_mode_t {
|
||||
JOY_PCJOY = 0,
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
JOY_KPAD,
|
||||
// pref domains
|
||||
#define PREF_DOMAIN_AUDIO "audio"
|
||||
#define PREF_DOMAIN_INTERFACE "interface"
|
||||
#define PREF_DOMAIN_JOYSTICK "joystick"
|
||||
#define PREF_DOMAIN_KEYBOARD "keyboard"
|
||||
#define PREF_DOMAIN_VIDEO "video"
|
||||
#define PREF_DOMAIN_VM "vm"
|
||||
|
||||
// audio
|
||||
#define PREF_SPEAKER_VOLUME "speakerVolume"
|
||||
#define PREF_MOCKINGBOARD_VOLUME "mbVolume"
|
||||
|
||||
// video
|
||||
#define PREF_COLOR_MODE "colorMode"
|
||||
|
||||
// joystick
|
||||
#define PREF_JOYSTICK_MODE "joystickMode"
|
||||
#define PREF_JOYSTICK_KPAD_AUTO_RECENTER "kpAutoRecenter"
|
||||
#define PREF_JOYSTICK_KPAD_STEP "kpStep"
|
||||
|
||||
// keyboard
|
||||
#define PREF_KEYBOARD_CAPS "caps"
|
||||
|
||||
// interface
|
||||
#define PREF_DISK_PATH "diskPath"
|
||||
|
||||
// vm
|
||||
#define PREF_CPU_SCALE "cpuScale"
|
||||
#define PREF_CPU_SCALE_ALT "cpuScaleAlt"
|
||||
|
||||
typedef void (*prefs_change_callback_f)(const char * _NONNULL domain);
|
||||
|
||||
// load preferences from persistent store
|
||||
extern void prefs_load(void);
|
||||
|
||||
// load preferences from JSON string
|
||||
extern void prefs_loadString(const char * _NONNULL jsonString);
|
||||
|
||||
// save preferences to persistent store
|
||||
extern bool prefs_save(void);
|
||||
|
||||
// copy string value for key in prefs domain, returns true upon success and strdup()'d value in *val
|
||||
extern bool prefs_copyStringValue(const char * _NONNULL domain, const char * _NONNULL key, INOUT char ** _NONNULL val);
|
||||
|
||||
// get long value for key in prefs domain, returns true upon success
|
||||
extern bool prefs_parseLongValue(const char * _NONNULL domain, const char * _NONNULL key, INOUT long *val, const long base);
|
||||
|
||||
// get long value for key in prefs domain, returns true upon success
|
||||
extern bool prefs_parseBoolValue(const char * _NONNULL domain, const char * _NONNULL key, INOUT bool *val);
|
||||
|
||||
// get float value for key in prefs domain, returns true upon success
|
||||
extern bool prefs_parseFloatValue(const char * _NONNULL domain, const char *key, INOUT float * _NONNULL val);
|
||||
|
||||
// set string value for key in prefs domain
|
||||
extern bool prefs_setStringValue(const char *domain, const char * _NONNULL key, const char * _NONNULL val);
|
||||
|
||||
// set long value for key in prefs domain
|
||||
extern bool prefs_setLongValue(const char * _NONNULL domain, const char * _NONNULL key, long val);
|
||||
|
||||
// set bool value for key in map JSON, returns true upon success
|
||||
extern bool prefs_setBoolValue(const char * _NONNULL domain, const char * _NONNULL key, bool val);
|
||||
|
||||
// set float value for key in prefs domain
|
||||
extern bool prefs_setFloatValue(const char * _NONNULL domain, const char * _NONNULL key, float val);
|
||||
|
||||
// register a preferences listener for a particular domain
|
||||
extern void prefs_registerListener(const char * _NONNULL domain, _NONNULL prefs_change_callback_f callback);
|
||||
|
||||
// send change notification to all domain listeners
|
||||
extern void prefs_sync(const char * _NULLABLE domain);
|
||||
|
||||
// cleans up and removes listener in preparation for app shutdown
|
||||
extern void prefs_shutdown(bool emulatorShuttingDown);
|
||||
|
||||
#endif
|
||||
NUM_JOYOPTS
|
||||
} joystick_mode_t;
|
||||
|
||||
typedef enum color_mode_t {
|
||||
COLOR_NONE = 0,
|
||||
/*LAZY_COLOR, deprecated*/
|
||||
COLOR,
|
||||
/*LAZY_INTERP, deprecated*/
|
||||
COLOR_INTERP,
|
||||
NUM_COLOROPTS
|
||||
} color_mode_t;
|
||||
|
||||
typedef enum a2_video_mode_t {
|
||||
VIDEO_FULLSCREEN = 0,
|
||||
VIDEO_1X,
|
||||
VIDEO_2X,
|
||||
NUM_VIDOPTS
|
||||
} a2_video_mode_t;
|
||||
|
||||
extern char system_path[PATH_MAX];
|
||||
extern char disk_path[PATH_MAX];
|
||||
|
||||
extern int apple_mode; /* undocumented instructions or //e mode */
|
||||
extern int sound_volume;
|
||||
extern color_mode_t color_mode;
|
||||
extern a2_video_mode_t a2_video_mode;
|
||||
|
||||
/* generic joystick settings */
|
||||
extern joystick_mode_t joy_mode;
|
||||
|
||||
/* functions in prefs.c */
|
||||
extern void load_settings(void);
|
||||
extern bool save_settings(void);
|
||||
|
||||
#endif /* PREFS_H */
|
||||
|
|
|
@ -33,7 +33,7 @@ static const char *get_default_preferences(void) {
|
|||
" \"diskPath\" : \"/usr/local/games/apple2/disks\""
|
||||
" },"
|
||||
" \"joystick\" : {"
|
||||
" \"variant\" : \"keypad\","
|
||||
" \"joystickMode\" : 1,"
|
||||
" \"pcJoystickParms\" : \"128 128 255 1 255 1\","
|
||||
" \"kpJoystickParms\" : \"8 1\""
|
||||
" },"
|
||||
|
@ -41,11 +41,11 @@ static const char *get_default_preferences(void) {
|
|||
" \"caps\" : true"
|
||||
" },"
|
||||
" \"video\" : {"
|
||||
" \"color\" : \"interpolated\""
|
||||
" \"colorMode\" : \"2\""
|
||||
" },"
|
||||
" \"vm\" : {"
|
||||
" \"speed\" : 1.0,"
|
||||
" \"altSpeed\" : 4.0"
|
||||
" \"cpuScale\" : 1.0,"
|
||||
" \"cpuScaleAlt\" : 4.0"
|
||||
" }"
|
||||
"}"
|
||||
;
|
||||
|
@ -1292,7 +1292,6 @@ TEST test_json_map_mutation_1() {
|
|||
PASS();
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST test_prefs_loadString_1() {
|
||||
const char *prefsJSON = get_default_preferences();
|
||||
prefs_loadString(prefsJSON);
|
||||
|
@ -1302,22 +1301,22 @@ TEST test_prefs_loadString_1() {
|
|||
long lVal = 0;
|
||||
float fVal = 0.f;
|
||||
|
||||
bool ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, "speakerVolume", &lVal, /*base:*/10);
|
||||
bool ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 4);
|
||||
|
||||
ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, "mbVolume", &lVal, /*base:*/10);
|
||||
ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, PREF_MOCKINGBOARD_VOLUME, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 2);
|
||||
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_INTERFACE, "diskPath", &val);
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, &val);
|
||||
ASSERT(ok);
|
||||
ASSERT(strcmp(val, "/usr/local/games/apple2/disks") == 0);
|
||||
FREE(val);
|
||||
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_JOYSTICK, "variant", &val);
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, &val);
|
||||
ASSERT(ok);
|
||||
ASSERT(strcmp(val, "keypad") == 0);
|
||||
ASSERT(strcmp(val, "1") == 0);
|
||||
FREE(val);
|
||||
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_JOYSTICK, "pcJoystickParms", &val);
|
||||
|
@ -1330,20 +1329,20 @@ TEST test_prefs_loadString_1() {
|
|||
ASSERT(strcmp(val, "8 1") == 0);
|
||||
FREE(val);
|
||||
|
||||
ok = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, "caps", &bVal);
|
||||
ok = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, &bVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(bVal == true);
|
||||
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_VIDEO, "color", &val);
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, &val);
|
||||
ASSERT(ok);
|
||||
ASSERT(strcmp(val, "interpolated") == 0);
|
||||
ASSERT(strcmp(val, "2") == 0);
|
||||
FREE(val);
|
||||
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, "speed", &fVal);
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, &fVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(fVal == 1.f);
|
||||
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, "altSpeed", &fVal);
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, &fVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(fVal == 4.f);
|
||||
|
||||
|
@ -1359,34 +1358,117 @@ TEST test_prefs_set_props() {
|
|||
long lVal = 0;
|
||||
float fVal = 0.f;
|
||||
|
||||
bool ok = prefs_setLongValue(PREF_DOMAIN_AUDIO, "speakerVolume", 8);
|
||||
bool ok = prefs_setLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, 8);
|
||||
ASSERT(ok);
|
||||
ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, "speakerVolume", &lVal, /*base:*/10);
|
||||
ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 8);
|
||||
|
||||
ok = prefs_setStringValue(PREF_DOMAIN_INTERFACE, "diskPath", "/home/apple2ix/disks");
|
||||
ok = prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, "/home/apple2ix/disks");
|
||||
ASSERT(ok);
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_INTERFACE, "diskPath", &val);
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, &val);
|
||||
ASSERT(ok);
|
||||
ASSERT(strcmp(val, "/home/apple2ix/disks") == 0);
|
||||
FREE(val);
|
||||
|
||||
ok = prefs_setBoolValue(PREF_DOMAIN_KEYBOARD, "caps", false);
|
||||
ok = prefs_setBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, false);
|
||||
ASSERT(ok);
|
||||
ok = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, "caps", &bVal);
|
||||
ok = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, &bVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(bVal == false);
|
||||
|
||||
ok = prefs_setFloatValue(PREF_DOMAIN_VM, "altSpeed", 0.25f);
|
||||
ok = prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, 0.25f);
|
||||
ASSERT(ok);
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, "altSpeed", &fVal);
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, &fVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(fVal == 0.25f);
|
||||
|
||||
PASS();
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TEST_JSON "test-apple2ix.json"
|
||||
#define EXPECTED_TEST_PREFS_FILE_SIZE 178
|
||||
#define EXPECTED_TEST_PREFS_SHA "263844f0177a9229eece7907cd9f6f72aef535f5"
|
||||
TEST test_prefs_load_and_save() {
|
||||
unlink(TEST_JSON);
|
||||
putenv("APPLE2IX_JSON=" TEST_JSON);
|
||||
prefs_load();
|
||||
prefs_save();
|
||||
|
||||
bool ok = false;
|
||||
char *val = (char *)0xdeadc0de;
|
||||
bool bVal = false;
|
||||
long lVal = 42;
|
||||
float fVal = 0.125;
|
||||
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, &val);
|
||||
ASSERT(!ok);
|
||||
ASSERT(val == (char *)0xdeadc0de);
|
||||
//FREE(val);
|
||||
ok = prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, "/home/apple2ix/disks");
|
||||
ASSERT(ok);
|
||||
ok = prefs_copyStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, &val);
|
||||
ASSERT(ok);
|
||||
ASSERT(val);
|
||||
ASSERT(strcmp(val, "/home/apple2ix/disks") == 0);
|
||||
FREE(val);
|
||||
|
||||
ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, &lVal, /*base:*/10);
|
||||
ASSERT(!ok);
|
||||
ASSERT(lVal == 42);
|
||||
ok = prefs_setLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, 8);
|
||||
ASSERT(ok);
|
||||
ok = prefs_parseLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 8);
|
||||
|
||||
ok = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, &bVal);
|
||||
ASSERT(!ok);
|
||||
ASSERT(bVal == false);
|
||||
ok = prefs_setBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, true);
|
||||
ASSERT(ok);
|
||||
ok = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, &bVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(bVal == true);
|
||||
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, &fVal);
|
||||
ASSERT(!ok);
|
||||
ASSERT(fVal == 0.125f);
|
||||
ok = prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, 0.25f);
|
||||
ASSERT(ok);
|
||||
ok = prefs_parseFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, &fVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(fVal == 0.25f);
|
||||
|
||||
prefs_save();
|
||||
|
||||
do {
|
||||
uint8_t md[SHA_DIGEST_LENGTH];
|
||||
char mdstr0[(SHA_DIGEST_LENGTH*2)+1];
|
||||
|
||||
FILE *fp = fopen(TEST_JSON, "r");
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long expectedSize = ftell(fp);
|
||||
ASSERT(expectedSize == EXPECTED_TEST_PREFS_FILE_SIZE);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
unsigned char *buf = MALLOC(EXPECTED_TEST_PREFS_FILE_SIZE);
|
||||
if (fread(buf, 1, EXPECTED_TEST_PREFS_FILE_SIZE, fp) != EXPECTED_TEST_PREFS_FILE_SIZE) {
|
||||
ASSERT(false);
|
||||
}
|
||||
fclose(fp); fp = NULL;
|
||||
SHA1(buf, EXPECTED_TEST_PREFS_FILE_SIZE, md);
|
||||
FREE(buf);
|
||||
|
||||
sha1_to_str(md, mdstr0);
|
||||
ASSERT(strcasecmp(mdstr0, EXPECTED_TEST_PREFS_SHA) == 0);
|
||||
} while(0);
|
||||
|
||||
unlink(TEST_JSON);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Test Suite
|
||||
|
@ -1436,8 +1518,9 @@ GREATEST_SUITE(test_suite_prefs) {
|
|||
|
||||
RUN_TESTp(test_json_map_mutation_1);
|
||||
|
||||
//RUN_TESTp(test_prefs_loadString_1);
|
||||
//RUN_TESTp(test_prefs_set_props);
|
||||
RUN_TESTp(test_prefs_loadString_1);
|
||||
RUN_TESTp(test_prefs_set_props);
|
||||
RUN_TESTp(test_prefs_load_and_save);
|
||||
|
||||
// --------------------------------
|
||||
pthread_mutex_unlock(&interface_mutex);
|
||||
|
|
16
src/timing.c
16
src/timing.c
|
@ -586,3 +586,19 @@ void timing_testCyclesCountOverflow(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void vm_prefsChanged(const char *domain) {
|
||||
assert(strcmp(domain, PREF_DOMAIN_VM) == 0);
|
||||
|
||||
float fVal = 1.0;
|
||||
prefs_parseFloatValue(domain, PREF_CPU_SCALE, &fVal);
|
||||
cpu_scale_factor = fVal;
|
||||
prefs_parseFloatValue(domain, PREF_CPU_SCALE_ALT, &fVal);
|
||||
cpu_altscale_factor = fVal;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void _init_vm(void) {
|
||||
prefs_registerListener(PREF_DOMAIN_VM, &vm_prefsChanged);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,13 +23,12 @@
|
|||
static inline void _capslock_hackaround(void) {
|
||||
// NOTE : Unfortunately it appears that we cannot get a raw key down/up notification for CAPSlock, so hack that here
|
||||
// ALSO : Emulator initially sets CAPS state based on a user preference, but sync to system state if user changes it
|
||||
static bool modified_caps_lock = false;
|
||||
int modifiers = glutGetModifiers();
|
||||
if (!c_keys_is_shifted()) {
|
||||
if (modifiers & GLUT_ACTIVE_SHIFT) {
|
||||
modified_caps_lock = true;
|
||||
use_system_caps_lock = true;
|
||||
caps_lock = true;
|
||||
} else if (modified_caps_lock) {
|
||||
} else if (use_system_caps_lock) {
|
||||
caps_lock = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,5 +43,17 @@ typedef struct A2Color_s {
|
|||
*/
|
||||
extern A2Color_s colormap[];
|
||||
|
||||
#if !VIDEO_OPENGL
|
||||
// X11 scaling ...
|
||||
typedef enum a2_video_mode_t {
|
||||
VIDEO_FULLSCREEN = 0,
|
||||
VIDEO_1X,
|
||||
VIDEO_2X,
|
||||
NUM_VIDOPTS
|
||||
} a2_video_mode_t;
|
||||
|
||||
extern a2_video_mode_t a2_video_mode;
|
||||
#endif
|
||||
|
||||
#endif /* !A2_VIDEO_H */
|
||||
|
||||
|
|
12
src/vm.c
12
src/vm.c
|
@ -1212,19 +1212,13 @@ void vm_initialize(void) {
|
|||
}
|
||||
|
||||
void vm_reinitializeAudio(void) {
|
||||
#ifdef AUDIO_ENABLED
|
||||
speaker_setVolumeZeroToTen(sound_volume);
|
||||
#if !MOBILE_DEVICE
|
||||
#warning TODO FIXME : disentangle Mockingboard volume from speaker volume on Desktop ...
|
||||
MB_SetVolumeZeroToTen(sound_volume);
|
||||
#endif
|
||||
#endif
|
||||
for (unsigned int i = 0xC030; i < 0xC040; i++) {
|
||||
cpu65_vmem_r[i] = cpu65_vmem_w[i] =
|
||||
#ifdef AUDIO_ENABLED
|
||||
(sound_volume > 0) ? speaker_toggle :
|
||||
#endif
|
||||
speaker_toggle;
|
||||
#else
|
||||
ram_nop;
|
||||
#endif
|
||||
}
|
||||
#warning TODO FIXME ... should unset MB/Phasor hooks if volume is zero ...
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user