OXYGENHUB
◆ main enhanced-x16-emulator / src / joystick.c
9.9 KiB · C · last changed chore(): initial commit 25 days ago source blame history raw
1
#include "joystick.h"
2
3
#include <SDL.h>
4
#include <stdio.h>
5
#include "io_trace.h"
6
7
struct joystick_info {
8
	int                 instance_id;
9
	SDL_GameController *controller;
10
	uint16_t            button_mask;
11
};
12
13
static const uint16_t button_map[SDL_CONTROLLER_BUTTON_MAX] = {
14
    1 << 0,  //SDL_CONTROLLER_BUTTON_A,
15
    1 << 8,  //SDL_CONTROLLER_BUTTON_B,
16
    1 << 1,  //SDL_CONTROLLER_BUTTON_X,
17
    1 << 9,  //SDL_CONTROLLER_BUTTON_Y,
18
    1 << 2,  //SDL_CONTROLLER_BUTTON_BACK,
19
    0,       //SDL_CONTROLLER_BUTTON_GUIDE,
20
    1 << 3,  //SDL_CONTROLLER_BUTTON_START,
21
    0,       //SDL_CONTROLLER_BUTTON_LEFTSTICK,
22
    0,       //SDL_CONTROLLER_BUTTON_RIGHTSTICK,
23
    1 << 10, //SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
24
    1 << 11, //SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
25
    1 << 4,  //SDL_CONTROLLER_BUTTON_DPAD_UP,
26
    1 << 5,  //SDL_CONTROLLER_BUTTON_DPAD_DOWN,
27
    1 << 6,  //SDL_CONTROLLER_BUTTON_DPAD_LEFT,
28
    1 << 7,  //SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
29
};
30
31
static struct joystick_info *Joystick_controllers     = NULL;
32
static int                   Num_joystick_controllers = 0;
33
34
static void
35
resize_joystick_controllers(int new_size)
36
{
37
	if (new_size == 0) {
38
		free(Joystick_controllers);
39
		Joystick_controllers     = NULL;
40
		Num_joystick_controllers = 0;
41
		return;
42
	}
43
44
	struct joystick_info *old_controllers = Joystick_controllers;
45
	Joystick_controllers                  = (struct joystick_info *)malloc(sizeof(struct joystick_info) * new_size);
46
47
	int min_size = new_size < Num_joystick_controllers ? new_size : Num_joystick_controllers;
48
	if (min_size > 0) {
49
		memcpy(Joystick_controllers, old_controllers, sizeof(struct joystick_info) * min_size);
50
		free(old_controllers);
51
	}
52
53
	for (int i = min_size; i < new_size; ++i) {
54
		Joystick_controllers[i].instance_id = -1;
55
		Joystick_controllers[i].controller  = NULL;
56
		Joystick_controllers[i].button_mask = 0xffff;
57
	}
58
}
59
60
static void
61
add_joystick_controller(struct joystick_info *info)
62
{
63
	int i;
64
	for (i = 0; i < Num_joystick_controllers; ++i) {
65
		if (Joystick_controllers[i].instance_id == -1) {
66
			memcpy(&Joystick_controllers[i], info, sizeof(struct joystick_info));
67
			return;
68
		}
69
	}
70
71
	i = Num_joystick_controllers;
72
	resize_joystick_controllers(Num_joystick_controllers << 1);
73
74
	memcpy(&Joystick_controllers[i], info, sizeof(struct joystick_info));
75
}
76
77
static void
78
remove_joystick_controller(int instance_id)
79
{
80
	for (int i = 0; i < Num_joystick_controllers; ++i) {
81
		if (Joystick_controllers[i].instance_id == instance_id) {
82
			Joystick_controllers[i].instance_id = -1;
83
			Joystick_controllers[i].controller  = NULL;
84
			return;
85
		}
86
	}
87
}
88
89
static struct joystick_info *
90
find_joystick_controller(int instance_id)
91
{
92
	for (int i = 0; i < Num_joystick_controllers; ++i) {
93
		if (Joystick_controllers[i].instance_id == instance_id) {
94
			return &Joystick_controllers[i];
95
		}
96
	}
97
98
	return NULL;
99
}
100
101
bool Joystick_slots_enabled[NUM_JOYSTICKS] = {false, false, false, false};
102
// -1 is "no controller bound". Statically initialised because zero would mean
103
// instance id 0 is bound to every slot, which is what a caller reaching the
104
// joystick state before joystick_init() runs would otherwise see.
105
static int Joystick_slots[NUM_JOYSTICKS] = {-1, -1, -1, -1};
106
107
// A virtual joystick needs no SDL controller, so automation can drive a port on
108
// a machine with no gamepad attached. Active-low, same layout as button_mask.
109
static uint16_t Joystick_virtual_mask[NUM_JOYSTICKS]    = {0xffff, 0xffff, 0xffff, 0xffff};
110
static bool     Joystick_virtual_enabled[NUM_JOYSTICKS] = {false, false, false, false};
111
112
// The shift register is per slot rather than per controller: a virtual joystick
113
// has no controller to hang one off, and a slot is what the hardware clocks.
114
static uint16_t Joystick_slot_shift[NUM_JOYSTICKS] = {0xffff, 0xffff, 0xffff, 0xffff};
115
116
static bool Joystick_latch = false;
117
uint8_t Joystick_data  = 0;
118
119
// Whether anything at all is driving this slot. A slot with no source keeps the
120
// original behaviour of reporting 1s (released) forever rather than clocking a
121
// shift register, so no existing configuration changes behaviour.
122
static bool
123
slot_has_source(int slot)
124
{
125
	return Joystick_virtual_enabled[slot] || Joystick_slots[slot] >= 0;
126
}
127
128
// The button state the slot should latch: the virtual joystick when one is
129
// active, otherwise the controller bound to the slot.
130
static uint16_t
131
slot_button_mask(int slot)
132
{
133
	if (Joystick_virtual_enabled[slot]) {
134
		return Joystick_virtual_mask[slot];
135
	}
136
137
	if (Joystick_slots[slot] >= 0) {
138
		struct joystick_info *joy = find_joystick_controller(Joystick_slots[slot]);
139
		if (joy != NULL) {
140
			return joy->button_mask;
141
		}
142
	}
143
144
	return 0xffff;
145
}
146
147
bool
148
joystick_init(void)
149
{
150
	for (int i = 0; i < NUM_JOYSTICKS; ++i) {
151
		Joystick_slots[i] = -1;
152
	}
153
154
	const int num_joysticks = SDL_NumJoysticks();
155
156
	Num_joystick_controllers = num_joysticks > 16 ? num_joysticks : 16;
157
	Joystick_controllers     = malloc(sizeof(struct joystick_info) * Num_joystick_controllers);
158
159
	for (int i = 0; i < Num_joystick_controllers; ++i) {
160
		Joystick_controllers[i].instance_id = -1;
161
		Joystick_controllers[i].controller  = NULL;
162
		Joystick_controllers[i].button_mask = 0xffff;
163
	}
164
165
	for (int i = 0; i < num_joysticks; ++i) {
166
		joystick_add(i);
167
	}
168
169
	return true;
170
}
171
172
void
173
joystick_add(int index)
174
{
175
	if (!SDL_IsGameController(index)) {
176
		return;
177
	}
178
179
	SDL_GameController *controller = SDL_GameControllerOpen(index);
180
	if (controller == NULL) {
181
		fprintf(stderr, "Could not open controller %d: %s\n", index, SDL_GetError());
182
		return;
183
	}
184
185
	SDL_JoystickID instance_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
186
	bool           exists      = false;
187
	for (int i = 0; i < NUM_JOYSTICKS; ++i) {
188
		if (!Joystick_slots_enabled[i]) {
189
			continue;
190
		}
191
192
		if (Joystick_slots[i] == instance_id) {
193
			exists = true;
194
			break;
195
		}
196
	}
197
198
	if (!exists) {
199
		for (int i = 0; i < NUM_JOYSTICKS; ++i) {
200
			if (!Joystick_slots_enabled[i]) {
201
				continue;
202
			}
203
204
			if (Joystick_slots[i] == -1) {
205
				Joystick_slots[i] = instance_id;
206
				break;
207
			}
208
		}
209
210
		struct joystick_info new_info;
211
		new_info.instance_id = instance_id;
212
		new_info.controller  = controller;
213
		new_info.button_mask = 0xffff;
214
		add_joystick_controller(&new_info);
215
	}
216
}
217
218
void
219
joystick_remove(int instance_id)
220
{
221
	for (int i = 0; i < NUM_JOYSTICKS; ++i) {
222
		if (Joystick_slots[i] == instance_id) {
223
			Joystick_slots[i] = -1;
224
			break;
225
		}
226
	}
227
228
	SDL_GameController *controller = SDL_GameControllerFromInstanceID(instance_id);
229
	if (controller == NULL) {
230
		fprintf(stderr, "Could not find controller from instance_id %d: %s\n", instance_id, SDL_GetError());
231
	} else {
232
		SDL_GameControllerClose(controller);
233
		remove_joystick_controller(instance_id);
234
	}
235
}
236
237
void
238
joystick_button_down(int instance_id, uint8_t button)
239
{
240
	struct joystick_info *joy = find_joystick_controller(instance_id);
241
	if (joy != NULL) {
242
		joy->button_mask &= ~(button_map[button]);
243
	}
244
}
245
246
void
247
joystick_button_up(int instance_id, uint8_t button)
248
{
249
	struct joystick_info *joy = find_joystick_controller(instance_id);
250
	if (joy != NULL) {
251
		joy->button_mask |= button_map[button];
252
	}
253
}
254
255
static void
256
do_shift()
257
{
258
	for (int i = 0; i < NUM_JOYSTICKS; ++i) {
259
		if (slot_has_source(i)) {
260
			Joystick_data |= ((Joystick_slot_shift[i] & 1) ? (0x80 >> i) : 0);
261
			Joystick_slot_shift[i] >>= 1;
262
		} else {
263
			Joystick_data |= 0x80 >> i;
264
		}
265
	}
266
}
267
268
void
269
joystick_set_latch(bool value)
270
{
271
	Joystick_latch = value;
272
	if (value) {
273
		for (int i = 0; i < NUM_JOYSTICKS; ++i) {
274
			Joystick_slot_shift[i] = slot_button_mask(i) | 0xF000;
275
		}
276
		do_shift();
277
		if (io_trace_wants(IO_DEV_JOYSTICK)) {
278
			// Initialised because no slot is enabled unless -joy1..-joy4 was
279
			// passed, which is the default. The loop then writes nothing and
280
			// the %s below would otherwise walk uninitialised stack.
281
			char buf[64] = "";
282
			int  off = 0;
283
			for (int i = 0; i < NUM_JOYSTICKS && off < (int)sizeof(buf) - 10; ++i) {
284
				if (!Joystick_slots_enabled[i]) continue;
285
				off += snprintf(buf + off, sizeof(buf) - off, " j%d=%04X", i, slot_button_mask(i));
286
			}
287
			io_trace_event(IO_DEV_JOYSTICK, "latch%s", buf);
288
		}
289
	}
290
}
291
292
void
293
joystick_set_clock(bool value)
294
{
295
	if (!Joystick_latch && value) {
296
		Joystick_data = 0;
297
		do_shift();
298
	}
299
}
300
301
// ─── Virtual joysticks ───────────────────────────────────────────────────────
302
303
// Enabling the slot here is what lets automation drive a port without the
304
// -joy1..-joy4 flags, which exist to decide where a *physical* pad gets bound.
305
// A caller that has explicitly asked for input on this port has answered that
306
// question, and requiring the flag as well would only fail confusingly.
307
void
308
joystick_set_virtual(int slot, uint16_t button_mask)
309
{
310
	if (slot < 0 || slot >= NUM_JOYSTICKS) {
311
		return;
312
	}
313
314
	Joystick_virtual_mask[slot]    = button_mask;
315
	Joystick_virtual_enabled[slot] = true;
316
	Joystick_slots_enabled[slot]   = true;
317
}
318
319
// Leaves Joystick_slots_enabled alone: that is a user-visible configuration bit,
320
// and a physical pad may bind to the slot now that the virtual one has let go.
321
void
322
joystick_clear_virtual(int slot)
323
{
324
	if (slot < 0 || slot >= NUM_JOYSTICKS) {
325
		return;
326
	}
327
328
	Joystick_virtual_mask[slot]    = 0xffff;
329
	Joystick_virtual_enabled[slot] = false;
330
}
331
332
bool
333
joystick_virtual_active(int slot)
334
{
335
	if (slot < 0 || slot >= NUM_JOYSTICKS) {
336
		return false;
337
	}
338
339
	return Joystick_virtual_enabled[slot];
340
}
341
342
// Side-effect-free snapshot of joystick state for the ImGui debugger.
343
void
344
joystick_debug_get_state(joystick_debug_state_t *out)
345
{
346
	for (int i = 0; i < NUM_JOYSTICKS; ++i) {
347
		joystick_slot_debug_t *s = &out->slots[i];
348
		s->enabled          = Joystick_slots_enabled[i];
349
		s->virtual_active   = Joystick_virtual_enabled[i];
350
		s->controller_bound = Joystick_slots[i] >= 0 && find_joystick_controller(Joystick_slots[i]) != NULL;
351
		s->button_mask      = slot_button_mask(i);
352
		// A slot with no source clocks out 1s rather than draining a shift
353
		// register, so report that instead of a stale value it never uses.
354
		s->shift_mask = slot_has_source(i) ? Joystick_slot_shift[i] : 0xffff;
355
	}
356
	out->latch = Joystick_latch;
357
	out->data  = Joystick_data;
358
}