gamepad: support hotplug, any-device option
This commit is contained in:
parent
58b964baba
commit
b3a0128be6
4 changed files with 261 additions and 151 deletions
|
@ -117,7 +117,7 @@
|
|||
CONFIGDEF_FLOAT (SCORETEXT_ALPHA, "scoretext_alpha", 1) \
|
||||
KEYDEFS \
|
||||
CONFIGDEF_INT (GAMEPAD_ENABLED, "gamepad_enabled", 1) \
|
||||
CONFIGDEF_STRING (GAMEPAD_DEVICE, "gamepad_device", "default") \
|
||||
CONFIGDEF_STRING (GAMEPAD_DEVICE, "gamepad_device", "any") \
|
||||
CONFIGDEF_INT (GAMEPAD_AXIS_UD, "gamepad_axis_ud", 1) \
|
||||
CONFIGDEF_INT (GAMEPAD_AXIS_LR, "gamepad_axis_lr", 0) \
|
||||
CONFIGDEF_INT (GAMEPAD_AXIS_FREE, "gamepad_axis_free", 1) \
|
||||
|
|
265
src/gamepad.c
265
src/gamepad.c
|
@ -25,21 +25,25 @@ typedef struct GamepadButtonState {
|
|||
hrtime_t repeat_time;
|
||||
} GamepadButtonState;
|
||||
|
||||
static struct {
|
||||
bool initialized;
|
||||
int current_devnum;
|
||||
SDL_GameController *device;
|
||||
SDL_JoystickID instance;
|
||||
typedef struct GamepadDevice {
|
||||
SDL_GameController *controller;
|
||||
SDL_JoystickID joy_instance;
|
||||
int sdl_id;
|
||||
} GamepadDevice;
|
||||
|
||||
static struct {
|
||||
GamepadAxisState *axes;
|
||||
GamepadButtonState *buttons;
|
||||
|
||||
struct {
|
||||
int *id_map;
|
||||
size_t count;
|
||||
} devices;
|
||||
GamepadDevice *devices;
|
||||
size_t num_devices;
|
||||
size_t num_devices_allocated;
|
||||
int active_dev_num;
|
||||
bool initialized;
|
||||
bool update_needed;
|
||||
} gamepad;
|
||||
|
||||
#define DEVNUM(dev) (int)(ptrdiff_t)((dev) - gamepad.devices)
|
||||
|
||||
static bool gamepad_event_handler(SDL_Event *event, void *arg);
|
||||
|
||||
static int gamepad_load_mappings(const char *vpath, int warn_noexist) {
|
||||
|
@ -51,8 +55,6 @@ static int gamepad_load_mappings(const char *vpath, int warn_noexist) {
|
|||
int num_loaded = -1;
|
||||
LogLevel loglvl = LOG_WARN;
|
||||
|
||||
// Yay for gotos!
|
||||
|
||||
if(!mappings) {
|
||||
if(!warn_noexist) {
|
||||
VFSInfo vinfo = vfs_query(vpath);
|
||||
|
@ -92,36 +94,70 @@ static void gamepad_load_all_mappings(void) {
|
|||
static const char* gamepad_device_name_unmapped(int idx) {
|
||||
const char *name = SDL_GameControllerNameForIndex(idx);
|
||||
|
||||
if(name == NULL) {
|
||||
return "Unknown device";
|
||||
}
|
||||
|
||||
if(!strcasecmp(name, "Xinput Controller")) {
|
||||
// HACK: let's try to get a more descriptive name...
|
||||
const char *prev_name = name;
|
||||
name = SDL_JoystickNameForIndex(idx);
|
||||
|
||||
if(name == NULL) {
|
||||
name = prev_name;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
const char* gamepad_device_name(int num) {
|
||||
if(num < 0 || num >= gamepad.devices.count) {
|
||||
static inline GamepadDevice* gamepad_get_device(int num) {
|
||||
if(num < 0 || num >= gamepad.num_devices) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gamepad_device_name_unmapped(gamepad.devices.id_map[num]);
|
||||
return gamepad.devices + num;
|
||||
}
|
||||
|
||||
const char* gamepad_device_name(int num) {
|
||||
GamepadDevice *dev = gamepad_get_device(num);
|
||||
|
||||
if(dev) {
|
||||
return gamepad_device_name_unmapped(dev->sdl_id);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool gamepad_update_device_list(void) {
|
||||
int cnt = SDL_NumJoysticks();
|
||||
log_info("Updating gamepad devices list");
|
||||
|
||||
free(gamepad.devices.id_map);
|
||||
memset(&gamepad.devices, 0, sizeof(gamepad.devices));
|
||||
if(gamepad.num_devices > 0) {
|
||||
assume(gamepad.devices != NULL);
|
||||
|
||||
for(uint i = 0; i < gamepad.num_devices; ++i) {
|
||||
SDL_GameControllerClose(gamepad.devices[i].controller);
|
||||
}
|
||||
}
|
||||
|
||||
gamepad.num_devices = 0;
|
||||
|
||||
if(!cnt) {
|
||||
free(gamepad.devices);
|
||||
gamepad.devices = NULL;
|
||||
gamepad.num_devices_allocated = 0;
|
||||
log_info("No joysticks attached");
|
||||
return false;
|
||||
}
|
||||
|
||||
int *idmap_ptr = gamepad.devices.id_map = malloc(sizeof(int) * cnt);
|
||||
if(gamepad.num_devices_allocated != cnt) {
|
||||
free(gamepad.devices);
|
||||
gamepad.devices = calloc(cnt, sizeof(GamepadDevice));
|
||||
gamepad.num_devices_allocated = cnt;
|
||||
}
|
||||
|
||||
GamepadDevice *dev = gamepad.devices;
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(i);
|
||||
|
@ -140,15 +176,28 @@ static bool gamepad_update_device_list(void) {
|
|||
continue;
|
||||
}
|
||||
|
||||
*idmap_ptr = i;
|
||||
int num = (int)(uintmax_t)(idmap_ptr - gamepad.devices.id_map);
|
||||
++idmap_ptr;
|
||||
gamepad.devices.count = (uintptr_t)(idmap_ptr - gamepad.devices.id_map);
|
||||
dev->sdl_id = i;
|
||||
dev->controller = SDL_GameControllerOpen(i);
|
||||
|
||||
log_info("Found device '%s' (#%i): %s", guid_str, num, gamepad_device_name_unmapped(i));
|
||||
if(dev->controller == NULL) {
|
||||
log_sdl_error(LOG_WARN, "SDL_GameControllerOpen");
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->joy_instance = SDL_JoystickGetDeviceInstanceID(i);
|
||||
|
||||
if(dev->joy_instance < 0) {
|
||||
log_sdl_error(LOG_WARN, "SDL_JoystickGetDeviceInstanceID");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_info("Found device '%s' (#%i): %s", guid_str, DEVNUM(dev), gamepad_device_name_unmapped(i));
|
||||
|
||||
++gamepad.num_devices;
|
||||
++dev;
|
||||
}
|
||||
|
||||
if(!gamepad.devices.count) {
|
||||
if(!gamepad.num_devices) {
|
||||
log_info("No usable devices");
|
||||
return false;
|
||||
}
|
||||
|
@ -156,46 +205,86 @@ static bool gamepad_update_device_list(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static int gamepad_find_device_by_guid(const char *guid_str, char *guid_out, size_t guid_out_sz, int *out_localdevnum) {
|
||||
for(int i = 0; i < gamepad.devices.count; ++i) {
|
||||
static GamepadDevice* gamepad_find_device_by_guid(const char *guid_str, char *guid_out, size_t guid_out_sz, int *out_localdevnum) {
|
||||
if(gamepad.num_devices < 1) {
|
||||
*out_localdevnum = GAMEPAD_DEVNUM_INVALID;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!strcasecmp(guid_str, "any")) {
|
||||
*out_localdevnum = GAMEPAD_DEVNUM_ANY;
|
||||
strlcpy(guid_out, "any", guid_out_sz);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(int i = 0; i < gamepad.num_devices; ++i) {
|
||||
*guid_out = 0;
|
||||
|
||||
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(gamepad.devices.id_map[i]);
|
||||
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(gamepad.devices[i].sdl_id);
|
||||
SDL_JoystickGetGUIDString(guid, guid_out, guid_out_sz);
|
||||
|
||||
if(!strcasecmp(guid_str, guid_out) || !strcasecmp(guid_str, "default")) {
|
||||
*out_localdevnum = i;
|
||||
return gamepad.devices.id_map[i];
|
||||
return gamepad.devices + i;
|
||||
}
|
||||
}
|
||||
|
||||
*out_localdevnum = -1;
|
||||
return -1;
|
||||
*out_localdevnum = GAMEPAD_DEVNUM_INVALID;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int gamepad_find_device(char *guid_out, size_t guid_out_sz, int *out_localdevnum) {
|
||||
int dev;
|
||||
static GamepadDevice* gamepad_find_device(char *guid_out, size_t guid_out_sz, int *out_localdevnum) {
|
||||
GamepadDevice *dev;
|
||||
const char *want_guid = config_get_str(CONFIG_GAMEPAD_DEVICE);
|
||||
|
||||
dev = gamepad_find_device_by_guid(want_guid, guid_out, guid_out_sz, out_localdevnum);
|
||||
|
||||
if(dev >= 0) {
|
||||
if(dev || *out_localdevnum == GAMEPAD_DEVNUM_ANY) {
|
||||
return dev;
|
||||
}
|
||||
|
||||
if(strcasecmp(want_guid, "default")) {
|
||||
log_warn("Requested device '%s' is not available, trying first usable", want_guid);
|
||||
dev = gamepad_find_device_by_guid("default", guid_out, guid_out_sz, out_localdevnum);
|
||||
|
||||
if(dev >= 0) {
|
||||
return dev;
|
||||
}
|
||||
log_warn("Requested device '%s' is not available", want_guid);
|
||||
return gamepad_find_device_by_guid("any", guid_out, guid_out_sz, out_localdevnum);
|
||||
}
|
||||
|
||||
*out_localdevnum = -1;
|
||||
*out_localdevnum = GAMEPAD_DEVNUM_INVALID;
|
||||
strcpy(guid_out, want_guid);
|
||||
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int gamepad_get_active_device(void) {
|
||||
if(gamepad.initialized) {
|
||||
return gamepad.active_dev_num;
|
||||
}
|
||||
|
||||
return GAMEPAD_DEVNUM_INVALID;
|
||||
}
|
||||
|
||||
bool gamepad_update_devices(void) {
|
||||
gamepad.update_needed = false;
|
||||
|
||||
if(!gamepad_update_device_list()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char guid[33];
|
||||
GamepadDevice *dev = gamepad_find_device(guid, sizeof(guid), &gamepad.active_dev_num);
|
||||
|
||||
if(gamepad.active_dev_num == GAMEPAD_DEVNUM_ANY) {
|
||||
log_info("Using all available devices (%zu)", gamepad.num_devices);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(dev == NULL) {
|
||||
log_info("No devices available");
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Using device '%s' (#%i): %s", guid, gamepad.active_dev_num, gamepad_device_name(gamepad.active_dev_num));
|
||||
config_set_str(CONFIG_GAMEPAD_DEVICE, guid);
|
||||
return true;
|
||||
}
|
||||
|
||||
void gamepad_init(void) {
|
||||
|
@ -209,44 +298,17 @@ void gamepad_init(void) {
|
|||
}
|
||||
|
||||
gamepad.initialized = true;
|
||||
gamepad_load_all_mappings();
|
||||
|
||||
if(!gamepad_update_device_list()) {
|
||||
gamepad_shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
char guid[33];
|
||||
int dev = gamepad_find_device(guid, sizeof(guid), &gamepad.current_devnum);
|
||||
|
||||
if(dev < 0) {
|
||||
log_warn("Device '%s' is not available", guid);
|
||||
gamepad_shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
gamepad.device = SDL_GameControllerOpen(dev);
|
||||
|
||||
if(!gamepad.device) {
|
||||
log_warn("Failed to open device '%s' (#%i): %s", guid, dev, SDL_GetError());
|
||||
gamepad_shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Joystick *joy = SDL_GameControllerGetJoystick(gamepad.device);
|
||||
gamepad.instance = SDL_JoystickInstanceID(joy);
|
||||
gamepad.axes = calloc(GAMEPAD_AXIS_MAX, sizeof(GamepadAxisState));
|
||||
gamepad.buttons = calloc(GAMEPAD_BUTTON_MAX + GAMEPAD_EMULATED_BUTTON_MAX, sizeof(GamepadButtonState));
|
||||
|
||||
log_info("Using device '%s' (#%i): %s", guid, dev, gamepad_device_name(dev));
|
||||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
|
||||
config_set_str(CONFIG_GAMEPAD_DEVICE, guid);
|
||||
gamepad.active_dev_num = GAMEPAD_DEVNUM_INVALID;
|
||||
gamepad_load_all_mappings();
|
||||
|
||||
events_register_handler(&(EventHandler){
|
||||
.proc = gamepad_event_handler,
|
||||
.priority = EPRIO_TRANSLATION,
|
||||
});
|
||||
|
||||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
}
|
||||
|
||||
void gamepad_shutdown(void) {
|
||||
|
@ -256,13 +318,16 @@ void gamepad_shutdown(void) {
|
|||
|
||||
log_info("Disabled the gamepad subsystem");
|
||||
|
||||
if(gamepad.device) {
|
||||
SDL_GameControllerClose(gamepad.device);
|
||||
}
|
||||
|
||||
free(gamepad.axes);
|
||||
free(gamepad.buttons);
|
||||
free(gamepad.devices.id_map);
|
||||
|
||||
for(uint i = 0; i < gamepad.num_devices; ++i) {
|
||||
if(gamepad.devices[i].controller) {
|
||||
SDL_GameControllerClose(gamepad.devices[i].controller);
|
||||
}
|
||||
}
|
||||
|
||||
free(gamepad.devices);
|
||||
|
||||
SDL_GameControllerEventState(SDL_IGNORE);
|
||||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||
|
@ -613,12 +678,15 @@ static void gamepad_handle_button_repeat(GamepadButton btn, hrtime_t time) {
|
|||
}
|
||||
}
|
||||
|
||||
#define INSTANCE_IS_VALID(inst) \
|
||||
(gamepad.active_dev_num == GAMEPAD_DEVNUM_ANY || (gamepad.active_dev_num >= 0 && (inst) == gamepad.devices[gamepad.active_dev_num].joy_instance))
|
||||
|
||||
static bool gamepad_event_handler(SDL_Event *event, void *arg) {
|
||||
assert(gamepad.initialized);
|
||||
|
||||
switch(event->type) {
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
if(event->caxis.which == gamepad.instance) {
|
||||
if(INSTANCE_IS_VALID(event->caxis.which)) {
|
||||
GamepadAxis axis = gamepad_axis_from_sdl_axis(event->caxis.axis);
|
||||
|
||||
if(axis != GAMEPAD_AXIS_INVALID) {
|
||||
|
@ -629,7 +697,7 @@ static bool gamepad_event_handler(SDL_Event *event, void *arg) {
|
|||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
if(event->cbutton.which == gamepad.instance) {
|
||||
if(INSTANCE_IS_VALID(event->cbutton.which)) {
|
||||
GamepadButton btn = gamepad_button_from_sdl_button(event->cbutton.button);
|
||||
|
||||
if(btn != GAMEPAD_BUTTON_INVALID) {
|
||||
|
@ -637,9 +705,19 @@ static bool gamepad_event_handler(SDL_Event *event, void *arg) {
|
|||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_CONTROLLERDEVICEREMAPPED:
|
||||
gamepad.update_needed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->type == MAKE_TAISEI_EVENT(TE_FRAME)) {
|
||||
if(gamepad.update_needed) {
|
||||
gamepad_update_devices();
|
||||
}
|
||||
|
||||
hrtime_t time = time_get();
|
||||
|
||||
for(GamepadButton btn = 0; btn < GAMEPAD_BUTTON_MAX; ++btn) {
|
||||
|
@ -655,20 +733,29 @@ static bool gamepad_event_handler(SDL_Event *event, void *arg) {
|
|||
}
|
||||
|
||||
int gamepad_device_count(void) {
|
||||
return gamepad.devices.count;
|
||||
return gamepad.num_devices;
|
||||
}
|
||||
|
||||
void gamepad_device_guid(int num, char *guid_str, size_t guid_str_sz) {
|
||||
if(num < 0 || num >= gamepad.devices.count) {
|
||||
if(num == GAMEPAD_DEVNUM_ANY) {
|
||||
strlcpy(guid_str, "any", guid_str_sz);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(gamepad.devices.id_map[num]);
|
||||
SDL_JoystickGetGUIDString(guid, guid_str, guid_str_sz);
|
||||
GamepadDevice *dev = gamepad_get_device(num);
|
||||
|
||||
if(dev) {
|
||||
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(dev->sdl_id);
|
||||
SDL_JoystickGetGUIDString(guid, guid_str, guid_str_sz);
|
||||
}
|
||||
}
|
||||
|
||||
int gamepad_device_num_from_guid(const char *guid_str) {
|
||||
for(int i = 0; i < gamepad.devices.count; ++i) {
|
||||
if(strcasecmp(guid_str, "any")) {
|
||||
return GAMEPAD_DEVNUM_ANY;
|
||||
}
|
||||
|
||||
for(uint i = 0; i < gamepad.num_devices; ++i) {
|
||||
char guid[33] = {0};
|
||||
gamepad_device_guid(i, guid, sizeof(guid));
|
||||
|
||||
|
@ -677,15 +764,7 @@ int gamepad_device_num_from_guid(const char *guid_str) {
|
|||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gamepad_current_device_num(void) {
|
||||
if(gamepad.initialized) {
|
||||
return gamepad.current_devnum;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return GAMEPAD_DEVNUM_INVALID;
|
||||
}
|
||||
|
||||
bool gamepad_button_pressed(GamepadButton btn) {
|
||||
|
|
|
@ -84,6 +84,11 @@ typedef enum GamepadAxis {
|
|||
GAMEPAD_AXIS_MAX
|
||||
} GamepadAxis;
|
||||
|
||||
enum {
|
||||
GAMEPAD_DEVNUM_INVALID = -1,
|
||||
GAMEPAD_DEVNUM_ANY = -2,
|
||||
};
|
||||
|
||||
void gamepad_init(void);
|
||||
void gamepad_shutdown(void);
|
||||
void gamepad_restart(void);
|
||||
|
@ -93,7 +98,8 @@ int gamepad_device_count(void);
|
|||
const char* gamepad_device_name(int);
|
||||
void gamepad_device_guid(int num, char *guid_str, size_t guid_str_sz);
|
||||
int gamepad_device_num_from_guid(const char *guid_str);
|
||||
int gamepad_current_device_num(void);
|
||||
int gamepad_get_active_device(void);
|
||||
bool gamepad_update_devices(void);
|
||||
|
||||
bool gamepad_button_pressed(GamepadButton btn);
|
||||
bool gamepad_game_key_pressed(KeyIndex key);
|
||||
|
|
|
@ -39,7 +39,6 @@ typedef struct OptionBinding {
|
|||
char *strvalue;
|
||||
};
|
||||
bool displaysingle;
|
||||
int valcount;
|
||||
int valrange_min;
|
||||
int valrange_max;
|
||||
float scale_min;
|
||||
|
@ -72,9 +71,13 @@ static OptionBinding* bind_new(void) {
|
|||
static void bind_free(OptionBinding *bind) {
|
||||
int i;
|
||||
|
||||
if(bind->values) {
|
||||
for(i = 0; i < bind->valcount; ++i)
|
||||
if(bind->type == BT_StrValue) {
|
||||
free(bind->strvalue);
|
||||
} else if(bind->values) {
|
||||
assert(bind->valrange_min == 0);
|
||||
for(i = 0; i <= bind->valrange_max; ++i) {
|
||||
free(*(bind->values+i));
|
||||
}
|
||||
free(bind->values);
|
||||
}
|
||||
}
|
||||
|
@ -128,15 +131,32 @@ static OptionBinding* bind_gpaxisbinding(int cfgentry) {
|
|||
}
|
||||
|
||||
static int bind_gpdev_get(OptionBinding *b) {
|
||||
return gamepad_device_num_from_guid(config_get_str(b->configentry));
|
||||
const char *guid = config_get_str(b->configentry);
|
||||
int val = gamepad_device_num_from_guid(guid);
|
||||
|
||||
if(val == GAMEPAD_DEVNUM_ANY) {
|
||||
val = -1;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int bind_gpdev_set(OptionBinding *b, int v) {
|
||||
char guid[33] = {0};
|
||||
|
||||
if(v == -1) {
|
||||
v = GAMEPAD_DEVNUM_ANY;
|
||||
}
|
||||
|
||||
gamepad_device_guid(v, guid, sizeof(guid));
|
||||
|
||||
if(*guid) {
|
||||
config_set_str(b->configentry, guid);
|
||||
|
||||
if(v == GAMEPAD_DEVNUM_ANY) {
|
||||
v = -1;
|
||||
}
|
||||
|
||||
b->selected = v;
|
||||
}
|
||||
|
||||
|
@ -153,7 +173,7 @@ static OptionBinding* bind_gpdevice(int cfgentry) {
|
|||
bind->getter = bind_gpdev_get;
|
||||
bind->setter = bind_gpdev_set;
|
||||
|
||||
bind->valrange_min = 0;
|
||||
bind->valrange_min = -1;
|
||||
bind->valrange_max = 0; // updated later
|
||||
|
||||
bind->selected = gamepad_device_num_from_guid(config_get_str(bind->configentry));
|
||||
|
@ -173,7 +193,8 @@ static OptionBinding* bind_stroption(ConfigIndex cfgentry) {
|
|||
|
||||
// BT_Resolution: super-special binding type for the resolution setting
|
||||
static void bind_resolution_update(OptionBinding *bind) {
|
||||
bind->valcount = video.mcount;
|
||||
bind->valrange_min = 0;
|
||||
bind->valrange_max = video.mcount - 1;
|
||||
|
||||
for(int i = 0; i < video.mcount; ++i) {
|
||||
VideoMode *m = video.modes + i;
|
||||
|
@ -217,14 +238,25 @@ static OptionBinding* bind_getinputblocking(MenuData *m) {
|
|||
|
||||
// Adds a value to a BT_IntValue type binding
|
||||
static int bind_addvalue(OptionBinding *b, char *val) {
|
||||
b->values = realloc(b->values, ++b->valcount * sizeof(char*));
|
||||
b->values[b->valcount-1] = malloc(strlen(val) + 1);
|
||||
strcpy(b->values[b->valcount-1], val);
|
||||
return b->valcount-1;
|
||||
assert(b->valrange_min == 0);
|
||||
|
||||
if(b->values == NULL) {
|
||||
b->values = malloc(sizeof(char*));
|
||||
b->valrange_min = 0;
|
||||
b->valrange_max = 0;
|
||||
} else {
|
||||
assert(b->valrange_min == 0);
|
||||
++b->valrange_max;
|
||||
}
|
||||
|
||||
b->values = realloc(b->values, (1 + b->valrange_max) * sizeof(char*));
|
||||
b->values[b->valrange_max] = strdup(val);
|
||||
return b->valrange_max;
|
||||
}
|
||||
|
||||
attr_unused
|
||||
static void bind_setvaluerange(OptionBinding *b, int vmin, int vmax) {
|
||||
assert(b->values == NULL);
|
||||
b->valrange_min = vmin;
|
||||
b->valrange_max = vmax;
|
||||
}
|
||||
|
@ -240,14 +272,7 @@ static int bind_setvalue(OptionBinding *b, int v) {
|
|||
// Called to get the selected value of a BT_IntValue type binding by index
|
||||
static int bind_getvalue(OptionBinding *b) {
|
||||
if(b->getter) {
|
||||
if(b->selected >= b->valcount && b->valcount) {
|
||||
b->selected = 0;
|
||||
} else {
|
||||
b->selected = b->getter(b);
|
||||
|
||||
if(b->selected >= b->valcount && b->valcount)
|
||||
b->selected = 0;
|
||||
}
|
||||
b->selected = b->getter(b);
|
||||
}
|
||||
|
||||
return b->selected;
|
||||
|
@ -257,11 +282,8 @@ static int bind_getvalue(OptionBinding *b) {
|
|||
static int bind_setnext(OptionBinding *b) {
|
||||
int s = b->selected + 1;
|
||||
|
||||
if(b->valrange_max) {
|
||||
if(s > b->valrange_max)
|
||||
s = b->valrange_min;
|
||||
} else if(s >= b->valcount) {
|
||||
s = 0;
|
||||
if(s > b->valrange_max) {
|
||||
s = b->valrange_min;
|
||||
}
|
||||
|
||||
return bind_setvalue(b, s);
|
||||
|
@ -271,11 +293,8 @@ static int bind_setnext(OptionBinding *b) {
|
|||
static int bind_setprev(OptionBinding *b) {
|
||||
int s = b->selected - 1;
|
||||
|
||||
if(b->valrange_max) {
|
||||
if(s < b->valrange_min)
|
||||
s = b->valrange_max;
|
||||
} else if(s < 0) {
|
||||
s = b->valcount - 1;
|
||||
if(s < b->valrange_min) {
|
||||
s = b->valrange_max;
|
||||
}
|
||||
|
||||
return bind_setvalue(b, s);
|
||||
|
@ -627,7 +646,7 @@ static void destroy_options_menu_gamepad(MenuData *m) {
|
|||
OptionsMenuContext *ctx = m->context;
|
||||
|
||||
if(config_get_int(CONFIG_GAMEPAD_ENABLED) && strcasecmp(config_get_str(CONFIG_GAMEPAD_DEVICE), ctx->data)) {
|
||||
gamepad_restart();
|
||||
gamepad_update_devices();
|
||||
}
|
||||
|
||||
destroy_options_menu(m);
|
||||
|
@ -924,16 +943,7 @@ static void draw_options_menu(MenuData *menu) {
|
|||
case BT_IntValue: {
|
||||
int val = bind_getvalue(bind);
|
||||
|
||||
if(bind->valrange_max) {
|
||||
char tmp[16]; // who'd use a 16-digit number here anyway?
|
||||
snprintf(tmp, 16, "%d", bind_getvalue(bind));
|
||||
|
||||
text_draw(tmp, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
} else if(bind->configentry == CONFIG_PRACTICE_POWER) {
|
||||
if(bind->configentry == CONFIG_PRACTICE_POWER) {
|
||||
Font *fnt_int = get_font("standard");
|
||||
Font *fnt_fract = get_font("small");
|
||||
|
||||
|
@ -948,18 +958,29 @@ static void draw_options_menu(MenuData *menu) {
|
|||
&clr,
|
||||
false
|
||||
);
|
||||
} else for(j = bind->displaysingle? val : bind->valcount-1; (j+1) && (!bind->displaysingle || j == val); --j) {
|
||||
if(j != bind->valcount-1 && !bind->displaysingle) {
|
||||
origin -= text_width(get_font("standard"), bind->values[j+1], 0) + 5;
|
||||
}
|
||||
} else if(bind->values) {
|
||||
for(j = bind->displaysingle? val : bind->valrange_max; (j+1) && (!bind->displaysingle || j == val); --j) {
|
||||
if(j != bind->valrange_max && !bind->displaysingle) {
|
||||
origin -= text_width(get_font("standard"), bind->values[j+1], 0) + 5;
|
||||
}
|
||||
|
||||
if(val == j) {
|
||||
clr = *RGBA_MUL_ALPHA(0.9, 0.6, 0.2, alpha);
|
||||
} else {
|
||||
clr = *RGBA_MUL_ALPHA(0.5, 0.5, 0.5, 0.7 * alpha);
|
||||
}
|
||||
if(val == j) {
|
||||
clr = *RGBA_MUL_ALPHA(0.9, 0.6, 0.2, alpha);
|
||||
} else {
|
||||
clr = *RGBA_MUL_ALPHA(0.5, 0.5, 0.5, 0.7 * alpha);
|
||||
}
|
||||
|
||||
text_draw(bind->values[j], &(TextParams) {
|
||||
text_draw(bind->values[j], &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
char tmp[16]; // who'd use a 16-digit number here anyway?
|
||||
snprintf(tmp, 16, "%d", bind_getvalue(bind));
|
||||
|
||||
text_draw(tmp, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
|
@ -1005,20 +1026,24 @@ static void draw_options_menu(MenuData *menu) {
|
|||
// XXX: I'm not exactly a huge fan of fixing up state in drawing code, but it seems the way to go for now...
|
||||
bind->valrange_max = gamepad_device_count() - 1;
|
||||
|
||||
if(bind->selected < 0 || bind->selected > bind->valrange_max) {
|
||||
bind->selected = gamepad_current_device_num();
|
||||
if(bind->selected < -1 || bind->selected > bind->valrange_max) {
|
||||
bind->selected = gamepad_get_active_device();
|
||||
|
||||
if(bind->selected < 0) {
|
||||
bind->selected = 0;
|
||||
if(bind->selected < -1) {
|
||||
bind->selected = -1;
|
||||
}
|
||||
}
|
||||
|
||||
char *txt;
|
||||
char buf[64];
|
||||
|
||||
if(bind->valrange_max > 0) {
|
||||
snprintf(buf, sizeof(buf), "#%i: %s", bind->selected + 1, gamepad_device_name(bind->selected));
|
||||
txt = buf;
|
||||
if(bind->valrange_max >= 0) {
|
||||
if(bind->selected < 0) {
|
||||
txt = "All devices";
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "#%i: %s", bind->selected + 1, gamepad_device_name(bind->selected));
|
||||
txt = buf;
|
||||
}
|
||||
} else {
|
||||
txt = "No devices available";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue