Config callback system

No more hacks in options menu code to update stuff when a setting
changes
This commit is contained in:
Andrei "Akari" Alexeyev 2017-02-17 20:23:22 +02:00
parent 32f7edd24e
commit 802cacb799
10 changed files with 218 additions and 106 deletions

View file

@ -17,7 +17,7 @@
static bool config_initialized = false;
CONFIGDEFS_EXPORT ConfigEntry configdefs[] = {
#define CONFIGDEF(type,entryname,default,ufield) {type, entryname, {.ufield = default}},
#define CONFIGDEF(type,entryname,default,ufield) {type, entryname, {.ufield = default}, NULL},
#define CONFIGDEF_KEYBINDING(id,entryname,default) CONFIGDEF(CONFIG_TYPE_KEYBINDING, entryname, default, i)
#define CONFIGDEF_GPKEYBINDING(id,entryname,default) CONFIGDEF(CONFIG_TYPE_INT, entryname, default, i)
#define CONFIGDEF_INT(id,entryname,default) CONFIGDEF(CONFIG_TYPE_INT, entryname, default, i)
@ -91,7 +91,7 @@ ConfigEntry* config_get(ConfigIndex idx) {
}
#endif
ConfigEntry* config_find_entry(char *name) {
static ConfigEntry* config_find_entry(char *name) {
ConfigEntry *e = configdefs;
do if(!strcmp(e->name, name)) return e; while((++e)->name);
return NULL;
@ -139,7 +139,89 @@ KeyIndex config_key_from_gamepad_button(int btn) {
return config_key_from_gamepad_key(config_gamepad_key_from_gamepad_button(btn));
}
FILE* config_open(char *filename, char *mode) {
static void config_set_val(ConfigIndex idx, ConfigValue v) {
ConfigEntry *e = config_get(idx);
ConfigCallback callback = e->callback;
bool difference;
switch(e->type) {
case CONFIG_TYPE_INT:
case CONFIG_TYPE_KEYBINDING:
difference = e->val.i != v.i;
break;
case CONFIG_TYPE_FLOAT:
difference = e->val.f != v.f;
break;
case CONFIG_TYPE_STRING:
difference = strcmp(e->val.s, v.s);
break;
}
if(!difference) {
return;
}
if(callback) {
e->callback = NULL;
callback(idx, v);
e->callback = callback;
return;
}
#ifdef DEBUG
#define PRINTVAL(t) printf("config_set_val(): %s:" #t " = %" #t "\n", e->name, e->val.t);
#else
#define PRINTVAL(t)
#endif
switch(e->type) {
case CONFIG_TYPE_INT:
case CONFIG_TYPE_KEYBINDING:
e->val.i = v.i;
PRINTVAL(i)
break;
case CONFIG_TYPE_FLOAT:
e->val.f = v.f;
PRINTVAL(f)
break;
case CONFIG_TYPE_STRING:
stralloc(&e->val.s, v.s);
PRINTVAL(s)
break;
}
#undef PRINTVAL
}
int config_set_int(ConfigIndex idx, int val) {
ConfigValue v = {.i = val};
config_set_val(idx, v);
return config_get_int(idx);
}
double config_set_float(ConfigIndex idx, double val) {
ConfigValue v = {.f = val};
config_set_val(idx, v);
return config_get_float(idx);
}
char* config_set_str(ConfigIndex idx, const char *val) {
ConfigValue v = {.s = (char*)val};
config_set_val(idx, v);
return config_get_str(idx);
}
void config_set_callback(ConfigIndex idx, ConfigCallback callback) {
ConfigEntry *e = config_get(idx);
assert(e->callback == NULL);
e->callback = callback;
}
static FILE* config_open(char *filename, char *mode) {
char *buf;
FILE *out;
@ -195,7 +277,7 @@ void config_save(char *filename) {
#define INTOF(s) ((int)strtol(s, NULL, 10))
#define FLOATOF(s) ((float)strtod(s, NULL))
void config_set(char *key, char *val) {
static void config_set(char *key, char *val) {
ConfigEntry *e = config_find_entry(key);
if(!e) {

View file

@ -10,6 +10,7 @@
#define CONFIG_H
#include <SDL_keycode.h>
#include <stdbool.h>
/*
Define these macros, then use CONFIGDEFS to expand them all for all config entries, or KEYDEFS for just keybindings.
@ -148,14 +149,19 @@ typedef enum ConfigEntryType {
CONFIG_TYPE_FLOAT
} ConfigEntryType;
typedef union ConfigValue {
int i;
double f;
char *s;
} ConfigValue;
typedef void (*ConfigCallback)(ConfigIndex, ConfigValue);
typedef struct ConfigEntry {
ConfigEntryType type;
char *name;
union {
int i;
double f;
char *s;
} val;
ConfigValue val;
ConfigCallback callback;
} ConfigEntry;
#define CONFIG_LOAD_BUFSIZE 256
@ -171,6 +177,7 @@ void config_init(void);
void config_uninit(void);
void config_load(char *filename);
void config_save(char *filename);
void config_set_callback(ConfigIndex idx, ConfigCallback callback);
#ifndef DEBUG
#define CONFIG_RAWACCESS
@ -189,8 +196,8 @@ void config_save(char *filename);
#define config_get_float(idx) (config_get(idx)->val.f)
#define config_get_str(idx) (config_get(idx)->val.s)
#define config_set_int(idx,val) (config_get_int(idx) = val)
#define config_set_float(idx,val) (config_get_float(idx) = val)
#define config_set_str(idx,val) stralloc(&config_get_str(idx), val)
int config_set_int(ConfigIndex idx, int val);
double config_set_float(ConfigIndex idx, double val);
char* config_set_str(ConfigIndex idx, const char *val);
#endif

View file

@ -52,7 +52,8 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
}
if((scan == SDL_SCANCODE_RETURN && (mod & KMOD_ALT)) || scan == config_get_int(CONFIG_KEY_FULLSCREEN)) {
video_toggle_fullscreen();
// video_toggle_fullscreen();
config_set_int(CONFIG_FULLSCREEN, !config_get_int(CONFIG_FULLSCREEN));
break;
}
}

View file

@ -115,14 +115,6 @@ OptionBinding* bind_scale(int cfgentry, float smin, float smax, float step) {
return bind;
}
// BT_ScaleCallback: float values clamped to a range with callback
OptionBinding* bind_scale_call(int cfgentry, float smin, float smax, float step, FloatSetter callback) {
OptionBinding *bind = bind_scale(cfgentry, smin, smax, step);
bind->type = BT_ScaleCallback;
bind->callback = callback;
return bind;
}
// Returns a pointer to the first found binding that blocks input. If none found, returns NULL.
OptionBinding* bind_getinputblocking(MenuData *m) {
int i;
@ -230,63 +222,6 @@ int bind_common_onoffset_inverted(void *b, int v) {
// --- Binding callbacks for individual options --- //
int bind_fullscreen_set(void *b, int v) {
video_toggle_fullscreen();
return bind_common_onoffset(b, v);
}
int bind_noaudio_set(void *b, int v) {
int i = bind_common_onoffset_inverted(b, v);
if(v)
{
shutdown_sfx();
}
else
{
if(!init_sfx(NULL, NULL)) return 1;
load_resources();
set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
}
return i;
}
int bind_nomusic_set(void *b, int v) {
int i = bind_common_onoffset_inverted(b, v);
if(v)
{
shutdown_bgm();
}
else
{
if(!init_bgm(NULL, NULL)) return 1;
load_resources();
set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
start_bgm("bgm_menu");
}
return i;
}
int bind_noshader_set(void *b, int v) {
int i = bind_common_onoffset_inverted(b, v);
if(!v)
load_resources();
return i;
}
int bind_vsync_set(void *b, int v) {
int i = bind_common_onoffset(b, v);
video_update_vsync();
return i;
}
bool bind_stagebg_fpslimit_dependence(void) {
return config_get_int(CONFIG_NO_STAGEBG) == 2;
}
@ -358,17 +293,17 @@ void options_sub_video(MenuData *parent, void *arg) {
);
add_menu_entry(m, "Fullscreen", do_nothing,
b = bind_option(CONFIG_FULLSCREEN, bind_common_onoffget, bind_fullscreen_set)
b = bind_option(CONFIG_FULLSCREEN, bind_common_onoffget, bind_common_onoffset)
); bind_onoff(b);
add_menu_entry(m, "Vertical synchronization", do_nothing,
b = bind_option(CONFIG_VSYNC, bind_common_onoffget, bind_vsync_set)
b = bind_option(CONFIG_VSYNC, bind_common_onoffget, bind_common_onoffset)
); bind_onoff(b);
add_menu_separator(m);
add_menu_entry(m, "Shaders", do_nothing,
b = bind_option(CONFIG_NO_SHADER, bind_common_onoffget_inverted, bind_noshader_set)
b = bind_option(CONFIG_NO_SHADER, bind_common_onoffget_inverted, bind_common_onoffset_inverted)
); bind_onoff(b);
add_menu_entry(m, "Stage background", do_nothing,
@ -605,8 +540,7 @@ void create_options_menu(MenuData *m) {
add_menu_separator(m);
add_menu_entry(m, "Save replays", do_nothing,
b = bind_option(CONFIG_SAVE_RPY, bind_saverpy_get,
bind_saverpy_set)
b = bind_option(CONFIG_SAVE_RPY, bind_saverpy_get, bind_saverpy_set)
); bind_addvalue(b, "on");
bind_addvalue(b, "off");
bind_addvalue(b, "ask");
@ -614,21 +548,21 @@ void create_options_menu(MenuData *m) {
add_menu_separator(m);
add_menu_entry(m, "Sound effects", do_nothing,
b = bind_option(CONFIG_NO_AUDIO, bind_common_onoffget_inverted,
bind_noaudio_set)
b = bind_option(CONFIG_NO_AUDIO, bind_common_onoffget_inverted,
bind_common_onoffset_inverted)
); bind_onoff(b);
add_menu_entry(m, "SFX volume level", do_nothing,
bind_scale_call(CONFIG_SFX_VOLUME, 0, 1, 0.1, set_sfx_volume)
bind_scale(CONFIG_SFX_VOLUME, 0, 1, 0.1)
);
add_menu_entry(m, "Background music", do_nothing,
b = bind_option(CONFIG_NO_MUSIC, bind_common_onoffget_inverted,
bind_nomusic_set)
b = bind_option(CONFIG_NO_MUSIC, bind_common_onoffget_inverted,
bind_common_onoffset_inverted)
); bind_onoff(b);
add_menu_entry(m, "Music volume level", do_nothing,
bind_scale_call(CONFIG_BGM_VOLUME, 0, 1, 0.1, set_bgm_volume)
bind_scale(CONFIG_BGM_VOLUME, 0, 1, 0.1)
);
add_menu_separator(m);
@ -780,8 +714,7 @@ void draw_options_menu(MenuData *menu) {
break;
}
case BT_Scale:
case BT_ScaleCallback: {
case BT_Scale: {
int w = 200;
int h = 5;
int cw = 5;
@ -930,10 +863,8 @@ static void options_input_event(EventType type, int state, void *arg) {
if(bind) {
if(bind->type == BT_IntValue || bind->type == BT_Resolution)
bind_setprev(bind);
else if((bind->type == BT_Scale) || (bind->type == BT_ScaleCallback)) {
else if(bind->type == BT_Scale) {
config_set_float(bind->configentry, clamp(config_get_float(bind->configentry) - bind->scale_step, bind->scale_min, bind->scale_max));
if (bind->type == BT_ScaleCallback)
bind->callback(config_get_float(bind->configentry));
}
}
break;
@ -943,10 +874,8 @@ static void options_input_event(EventType type, int state, void *arg) {
if(bind) {
if(bind->type == BT_IntValue || bind->type == BT_Resolution)
bind_setnext(bind);
else if((bind->type == BT_Scale) || (bind->type == BT_ScaleCallback)) {
else if(bind->type == BT_Scale) {
config_set_float(bind->configentry, clamp(config_get_float(bind->configentry) + bind->scale_step, bind->scale_min, bind->scale_max));
if (bind->type == BT_ScaleCallback)
bind->callback(config_get_float(bind->configentry));
}
}
break;

View file

@ -22,15 +22,12 @@ typedef int (*BindingGetter)(void*);
typedef int (*BindingSetter)(void*, int);
typedef bool (*BindingDependence)(void);
typedef void (*FloatSetter)(float);
typedef enum BindingType {
BT_IntValue,
BT_KeyBinding,
BT_StrValue,
BT_Resolution,
BT_Scale,
BT_ScaleCallback,
BT_GamepadKeyBinding
} BindingType;
@ -45,7 +42,6 @@ typedef struct OptionBinding {
float scale_step;
BindingGetter getter;
BindingSetter setter;
FloatSetter callback;
BindingDependence dependence;
int selected;
int configentry;

View file

@ -19,7 +19,14 @@
int alut_loaded = 0;
int warn_alut_error(const char *when) {
ALenum error = alutGetError();
ALenum error = alGetError();
if(error != AL_NO_ERROR) {
warnx("AL error %d while %s", error, when);
return 1;
}
error = alutGetError();
if (error == ALUT_ERROR_NO_ERROR) return 0;
warnx("ALUT error %d while %s: %s", error, when, alutGetErrorString(error));
return 1;
@ -56,8 +63,37 @@ int init_alut_if_needed(int *argc, char *argv[]) {
return 1;
}
static void sfx_cfg_noaudio_callback(ConfigIndex idx, ConfigValue v) {
config_set_int(idx, v.i);
if(v.i) {
shutdown_sfx();
return;
}
if(!init_sfx(NULL, NULL)) {
config_set_int(idx, true);
return;
}
load_resources();
set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
}
static void sfx_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
set_sfx_volume(config_set_float(idx, v.f));
}
int init_sfx(int *argc, char *argv[])
{
static bool callbacks_set_up = false;
if(!callbacks_set_up) {
config_set_callback(CONFIG_NO_AUDIO, sfx_cfg_noaudio_callback);
config_set_callback(CONFIG_SFX_VOLUME, sfx_cfg_volume_callback);
callbacks_set_up = true;
}
if (config_get_int(CONFIG_NO_AUDIO)) return 1;
if (!init_alut_if_needed(argc, argv)) return 0;
@ -81,7 +117,6 @@ void shutdown_sfx(void)
delete_sounds();
resources.state &= ~RS_SfxLoaded;
}
config_set_int(CONFIG_NO_AUDIO, 1);
unload_alut_if_needed();
}

View file

@ -21,8 +21,38 @@ struct current_bgm_t current_bgm = { .name = NULL };
char *saved_bgm;
static void bgm_cfg_nomusic_callback(ConfigIndex idx, ConfigValue v) {
config_set_int(idx, v.i);
if(v.i) {
shutdown_bgm();
return;
}
if(!init_bgm(NULL, NULL)) {
config_set_int(idx, true);
return;
}
load_resources();
set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
start_bgm("bgm_menu"); // FIXME: start BGM that's supposed to be playing in the current context
}
static void bgm_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
set_bgm_volume(config_set_float(idx, v.f));
}
int init_bgm(int *argc, char *argv[])
{
static bool callbacks_set_up = false;
if(!callbacks_set_up) {
config_set_callback(CONFIG_NO_MUSIC, bgm_cfg_nomusic_callback);
config_set_callback(CONFIG_BGM_VOLUME, bgm_cfg_volume_callback);
callbacks_set_up = true;
}
if (config_get_int(CONFIG_NO_MUSIC)) return 1;
if (!init_alut_if_needed(argc, argv)) return 0;
@ -47,7 +77,6 @@ void shutdown_bgm(void)
delete_music();
resources.state &= ~RS_BgmLoaded;
}
config_set_int(CONFIG_NO_MUSIC, 1);
unload_alut_if_needed();
}

View file

@ -63,7 +63,22 @@ void recurse_dir(char *path) {
closedir(dir);
}
static void resources_cfg_noshader_callback(ConfigIndex idx, ConfigValue v) {
config_set_int(idx, v.i);
if(!v.i) {
load_resources();
}
}
void load_resources(void) {
static bool callbacks_set_up = false;
if(!callbacks_set_up) {
config_set_callback(CONFIG_NO_SHADER, resources_cfg_noshader_callback);
callbacks_set_up = true;
}
printf("load_resources():\n");
char *path = malloc(strlen(get_prefix())+32);

View file

@ -9,7 +9,6 @@
#include "global.h"
#include "video.h"
#include "taisei_err.h"
#include <stdlib.h>
static VideoMode common_modes[] = {
{RESX, RESY},
@ -133,8 +132,12 @@ int video_isfullscreen(void) {
return !!(SDL_GetWindowFlags(video.window) & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP));
}
void video_set_fullscreen(bool fullscreen) {
video_setmode(video.intended.width, video.intended.height, fullscreen);
}
void video_toggle_fullscreen(void) {
video_setmode(video.intended.width, video.intended.height, !video_isfullscreen());
video_set_fullscreen(!video_isfullscreen());
}
void video_resize(int w, int h) {
@ -143,6 +146,15 @@ void video_resize(int w, int h) {
video_set_viewport();
}
static void video_cfg_fullscreen_callback(ConfigIndex idx, ConfigValue v) {
video_set_fullscreen(config_set_int(idx, v.i));
}
static void video_cfg_vsync_callback(ConfigIndex idx, ConfigValue v) {
config_set_int(idx, v.i);
video_update_vsync();
}
void video_init(void) {
int i, s;
bool fullscreen_available = false;
@ -178,6 +190,9 @@ void video_init(void) {
qsort(video.modes, video.mcount, sizeof(VideoMode), video_compare_modes);
video_setmode(config_get_int(CONFIG_VID_WIDTH), config_get_int(CONFIG_VID_HEIGHT), config_get_int(CONFIG_FULLSCREEN));
config_set_callback(CONFIG_FULLSCREEN, video_cfg_fullscreen_callback);
config_set_callback(CONFIG_VSYNC, video_cfg_vsync_callback);
}
void video_shutdown(void) {

View file

@ -12,6 +12,8 @@
#define WINDOW_TITLE "TaiseiProject"
#define VIEWPORT_ASPECT_RATIO (4.0f/3.0f)
#include <stdbool.h>
typedef struct VideoMode {
int width;
int height;
@ -34,6 +36,7 @@ void video_shutdown(void);
void video_setmode(int w, int h, int fs);
void video_set_viewport(void);
int video_isfullscreen(void);
void video_set_fullscreen(bool);
void video_toggle_fullscreen(void);
void video_resize(int w, int h);
void video_update_vsync(void);