changeset 937:9364dad5561a

Added reasonable handling of joystick hotplug
author Michael Pavone <pavone@retrodev.com>
date Tue, 23 Feb 2016 21:17:56 -0800
parents f1a8124ad881
children 4c17c7f46331
files io.c render.h render_sdl.c
diffstat 3 files changed, 102 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/io.c	Sat Feb 20 01:11:18 2016 -0800
+++ b/io.c	Tue Feb 23 21:17:56 2016 -0800
@@ -90,18 +90,26 @@
 } joydpad;
 
 typedef struct {
+	keybinding *buttons;
+	joydpad    *dpads;
+	uint32_t   num_buttons; //number of entries in the buttons array, not necessarily the number of buttons on the device
+	uint32_t   num_dpads;   //number of entries in the dpads array, not necessarily the number of dpads on the device
+} joystick;
+
+typedef struct {
 	io_port    *motion_port;
 	keybinding buttons[MAX_MOUSE_BUTTONS];
 	uint8_t    bind_type;
 } mousebinding;
 
-keybinding * bindings[0x10000];
-keybinding * joybindings[MAX_JOYSTICKS];
-joydpad * joydpads[MAX_JOYSTICKS];
-mousebinding mice[MAX_MICE];
+#define DEFAULT_JOYBUTTON_ALLOC 12
+
+static keybinding * bindings[0x10000];
+static joystick joysticks[MAX_JOYSTICKS];
+static mousebinding mice[MAX_MICE];
 const uint8_t dpadbits[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
-mouse_modes mouse_mode;
-char mouse_captured;
+static mouse_modes mouse_mode;
+static char mouse_captured;
 
 void bind_key(int keycode, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b, uint8_t value)
 {
@@ -122,18 +130,19 @@
 	if (joystick >= MAX_JOYSTICKS) {
 		return;
 	}
-	if (!joybindings[joystick]) {
-		int num = render_joystick_num_buttons(joystick);
-		if (!num) {
-			return;
-		}
-		joybindings[joystick] = malloc(sizeof(keybinding)*num);
-		memset(joybindings[joystick], 0, sizeof(keybinding)*num);
+	if (!joysticks[joystick].buttons) {
+		joysticks[joystick].num_buttons = button < DEFAULT_JOYBUTTON_ALLOC ? DEFAULT_JOYBUTTON_ALLOC : button + 1;
+		joysticks[joystick].buttons = calloc(joysticks[joystick].num_buttons, sizeof(keybinding));
+	} else if (joysticks[joystick].num_buttons <= button) {
+		uint32_t old_capacity = joysticks[joystick].num_buttons;
+		joysticks[joystick].num_buttons *= 2;
+		joysticks[joystick].buttons = realloc(joysticks[joystick].buttons, sizeof(keybinding) * joysticks[joystick].num_buttons);
+		memset(joysticks[joystick].buttons + old_capacity, 0, joysticks[joystick].num_buttons - old_capacity);
 	}
-	joybindings[joystick][button].bind_type = bind_type;
-	joybindings[joystick][button].subtype_a = subtype_a;
-	joybindings[joystick][button].subtype_b = subtype_b;
-	joybindings[joystick][button].value = value;
+	joysticks[joystick].buttons[button].bind_type = bind_type;
+	joysticks[joystick].buttons[button].subtype_a = subtype_a;
+	joysticks[joystick].buttons[button].subtype_b = subtype_b;
+	joysticks[joystick].buttons[button].value = value;
 }
 
 void bind_dpad(int joystick, int dpad, int direction, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b, uint8_t value)
@@ -141,20 +150,22 @@
 	if (joystick >= MAX_JOYSTICKS) {
 		return;
 	}
-	if (!joydpads[joystick]) {
-		int num = render_joystick_num_hats(joystick);
-		if (!num) {
-			return;
-		}
-		joydpads[joystick] = malloc(sizeof(joydpad)*num);
-		memset(joydpads[joystick], 0, sizeof(joydpad)*num);
+	if (!joysticks[joystick].dpads) {
+		//multiple D-pads hats are not common, so don't allocate any extra space
+		joysticks[joystick].dpads = calloc(dpad+1, sizeof(joydpad));
+		joysticks[joystick].num_dpads = dpad+1;
+	} else if (joysticks[joystick].num_dpads <= dpad) {
+		uint32_t old_capacity = joysticks[joystick].num_dpads;
+		joysticks[joystick].num_dpads *= 2;
+		joysticks[joystick].dpads = realloc(joysticks[joystick].dpads, sizeof(joydpad) * joysticks[joystick].num_dpads);
+		memset(joysticks[joystick].dpads + old_capacity, 0, joysticks[joystick].num_dpads - old_capacity);
 	}
 	for (int i = 0; i < 4; i ++) {
 		if (dpadbits[i] & direction) {
-			joydpads[joystick][dpad].bindings[i].bind_type = bind_type;
-			joydpads[joystick][dpad].bindings[i].subtype_a = subtype_a;
-			joydpads[joystick][dpad].bindings[i].subtype_b = subtype_b;
-			joydpads[joystick][dpad].bindings[i].value = value;
+			joysticks[joystick].dpads[dpad].bindings[i].bind_type = bind_type;
+			joysticks[joystick].dpads[dpad].bindings[i].subtype_a = subtype_a;
+			joysticks[joystick].dpads[dpad].bindings[i].subtype_b = subtype_b;
+			joysticks[joystick].dpads[dpad].bindings[i].value = value;
 			break;
 		}
 	}
@@ -258,10 +269,10 @@
 
 void handle_joydown(int joystick, int button)
 {
-	if (!joybindings[joystick]) {
+	if (joystick >= MAX_JOYSTICKS || button >= joysticks[joystick].num_buttons) {
 		return;
 	}
-	keybinding * binding = joybindings[joystick] + button;
+	keybinding * binding = joysticks[joystick].buttons + button;
 	handle_binding_down(binding);
 }
 
@@ -392,19 +403,19 @@
 
 void handle_joyup(int joystick, int button)
 {
-	if (!joybindings[joystick]) {
+	if (joystick >= MAX_JOYSTICKS  || button >= joysticks[joystick].num_buttons) {
 		return;
 	}
-	keybinding * binding = joybindings[joystick] + button;
+	keybinding * binding = joysticks[joystick].buttons + button;
 	handle_binding_up(binding);
 }
 
 void handle_joy_dpad(int joystick, int dpadnum, uint8_t value)
 {
-	if (!joydpads[joystick]) {
+	if (joystick >= MAX_JOYSTICKS  || dpadnum >= joysticks[joystick].num_dpads) {
 		return;
 	}
-	joydpad * dpad = joydpads[joystick] + dpadnum;
+	joydpad * dpad = joysticks[joystick].dpads + dpadnum;
 	uint8_t newdown = (value ^ dpad->state) & value;
 	uint8_t newup = ((~value) ^ (~dpad->state)) & (~value);
 	dpad->state = value;
@@ -931,7 +942,7 @@
 	char numstr[] = "00";
 	tern_node * pads = tern_get_node(tern_find_path(config, "bindings\0pads\0"));
 	if (pads) {
-		for (int i = 0; i < 100 && i < render_num_joysticks(); i++)
+		for (int i = 0; i < MAX_JOYSTICKS; i++)
 		{
 
 			if (i < 10) {
@@ -945,7 +956,7 @@
 			if (pad) {
 				tern_node * dpad_node = tern_find_ptr(pad, "dpads");
 				if (dpad_node) {
-					for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++)
+					for (int dpad = 0; dpad < 10; dpad++)
 					{
 						numstr[0] = dpad + '0';
 						numstr[1] = 0;
@@ -968,7 +979,7 @@
 				}
 				tern_node *button_node = tern_find_ptr(pad, "buttons");
 				if (button_node) {
-					for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++)
+					for (int but = 0; but < 30; but++)
 					{
 						if (but < 10) {
 							numstr[0] = but + '0';
@@ -1023,14 +1034,14 @@
 	}
 	for (int stick = 0; stick < MAX_JOYSTICKS; stick++)
 	{
-		if (joybindings[stick])
+		if (joysticks[stick].buttons) {
+			map_bindings(ports, joysticks[stick].buttons, joysticks[stick].num_buttons);
+		}
+		if (joysticks[stick].dpads)
 		{
-			int numbuttons = render_joystick_num_buttons(stick);
-			map_bindings(ports, joybindings[stick], render_joystick_num_buttons(stick));
-		}
-		if (joydpads[stick])
-		{
-			map_bindings(ports, joydpads[stick]->bindings, 4);
+			for (uint32_t i = 0; i < joysticks[stick].num_dpads; i++) {
+				map_bindings(ports, joysticks[stick].dpads[i].bindings, 4);
+			}
 		}
 	}
 	for (int mouse = 0; mouse < MAX_MICE; mouse++)
--- a/render.h	Sat Feb 20 01:11:18 2016 -0800
+++ b/render.h	Tue Feb 23 21:17:56 2016 -0800
@@ -55,9 +55,6 @@
 void render_debug_mode(uint8_t mode);
 void render_debug_pal(uint8_t pal);
 void process_events();
-int render_joystick_num_buttons(int joystick);
-int render_joystick_num_hats(int joystick);
-int render_num_joysticks();
 int render_width();
 int render_height();
 int render_fullscreen();
--- a/render_sdl.c	Sat Feb 20 01:11:18 2016 -0800
+++ b/render_sdl.c	Tue Feb 23 21:17:56 2016 -0800
@@ -90,13 +90,7 @@
 	SDL_CloseAudio();
 }
 
-SDL_Joystick * joysticks[MAX_JOYSTICKS];
-int num_joysticks;
-
-int render_num_joysticks()
-{
-	return num_joysticks;
-}
+static SDL_Joystick * joysticks[MAX_JOYSTICKS];
 
 int render_width()
 {
@@ -389,17 +383,6 @@
 	sample_rate = actual.freq;
 	printf("Initialized audio at frequency %d with a %d sample buffer\n", actual.freq, actual.samples);
 	SDL_PauseAudio(0);
-	num_joysticks = SDL_NumJoysticks();
-	if (num_joysticks > MAX_JOYSTICKS) {
-		num_joysticks = MAX_JOYSTICKS;
-	}
-	for (int i = 0; i < num_joysticks; i++) {
-		SDL_Joystick * joy = joysticks[i] = SDL_JoystickOpen(i);
-		printf("Joystick %d: %s\n", i, SDL_JoystickName(joy));
-		if (joy) {
-			printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy));
-		}
-	}
 	SDL_JoystickEventState(SDL_ENABLE);
 
 	atexit(render_quit);
@@ -491,22 +474,6 @@
 	}
 }
 
-int render_joystick_num_buttons(int joystick)
-{
-	if (joystick >= num_joysticks) {
-		return 0;
-	}
-	return SDL_JoystickNumButtons(joysticks[joystick]);
-}
-
-int render_joystick_num_hats(int joystick)
-{
-	if (joystick >= num_joysticks) {
-		return 0;
-	}
-	return SDL_JoystickNumHats(joysticks[joystick]);
-}
-
 void render_wait_quit(vdp_context * context)
 {
 	SDL_Event event;
@@ -546,6 +513,26 @@
 	}
 }
 
+int find_joystick_index(SDL_JoystickID instanceID)
+{
+	for (int i = 0; i < MAX_JOYSTICKS; i++) {
+		if (joysticks[i] && SDL_JoystickInstanceID(joysticks[i]) == instanceID) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+int lowest_unused_joystick_index()
+{
+	for (int i = 0; i < MAX_JOYSTICKS; i++) {
+		if (!joysticks[i]) {
+			return i;
+		}
+	}
+	return -1;
+}
+
 int32_t handle_event(SDL_Event *event)
 {
 	switch (event->type) {
@@ -556,14 +543,37 @@
 		handle_keyup(event->key.keysym.sym);
 		break;
 	case SDL_JOYBUTTONDOWN:
-		handle_joydown(event->jbutton.which, event->jbutton.button);
+		handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button);
 		break;
 	case SDL_JOYBUTTONUP:
-		handle_joyup(event->jbutton.which, event->jbutton.button);
+		handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button);
 		break;
 	case SDL_JOYHATMOTION:
-		handle_joy_dpad(event->jbutton.which, event->jhat.hat, event->jhat.value);
+		handle_joy_dpad(find_joystick_index(event->jbutton.which), event->jhat.hat, event->jhat.value);
 		break;
+	case SDL_JOYDEVICEADDED:
+		if (event->jdevice.which < MAX_JOYSTICKS) {
+			int index = lowest_unused_joystick_index();
+			if (index >= 0) {
+				SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which);
+				if (joy) {
+					printf("Joystick %d added: %s\n", index, SDL_JoystickName(joy));
+					printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy));
+				}
+			}
+		}
+		break;
+	case SDL_JOYDEVICEREMOVED: {
+		int index = find_joystick_index(event->jdevice.which);
+		if (index >= 0) {
+			SDL_JoystickClose(joysticks[index]);
+			joysticks[index] = NULL;
+			printf("Joystick %d removed\n", index);
+		} else {
+			printf("Failed to find removed joystick with instance ID: %d\n", index);
+		}
+		break;
+	}
 	case SDL_MOUSEMOTION:
 		handle_mouse_moved(event->motion.which, event->motion.x, event->motion.y, event->motion.xrel, event->motion.yrel);
 		break;