Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
9169 turbocat 1
/*
2
 * OpenTyrian: A modern cross-platform port of Tyrian
3
 * Copyright (C) 2007-2009  The OpenTyrian Development Team
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
 */
19
#include "joystick.h"
20
 
21
#include "config.h"
22
#include "config_file.h"
23
#include "file.h"
24
#include "keyboard.h"
25
#include "nortsong.h"
26
#include "opentyr.h"
27
#include "params.h"
28
#include "varz.h"
29
#include "video.h"
30
 
31
#include 
32
#include 
33
#include 
34
 
35
int joystick_axis_threshold( int j, int value );
36
int check_assigned( SDL_Joystick *joystick_handle, const Joystick_assignment assignment[2] );
37
 
38
const char *assignment_to_code( const Joystick_assignment *assignment );
39
void code_to_assignment( Joystick_assignment *assignment, const char *buffer );
40
 
41
int joystick_repeat_delay = 300; // milliseconds, repeat delay for buttons
42
bool joydown = false;            // any joystick buttons down, updated by poll_joysticks()
43
bool ignore_joystick = false;
44
 
45
int joysticks = 0;
46
Joystick *joystick = NULL;
47
 
48
static const int joystick_analog_max = 32767;
49
 
50
// eliminates axis movement below the threshold
51
int joystick_axis_threshold( int j, int value )
52
{
53
	assert(j < joysticks);
54
 
55
	bool negative = value < 0;
56
	if (negative)
57
		value = -value;
58
 
59
	if (value <= joystick[j].threshold * 1000)
60
		return 0;
61
 
62
	value -= joystick[j].threshold * 1000;
63
 
64
	return negative ? -value : value;
65
}
66
 
67
// converts joystick axis to sane Tyrian-usable value (based on sensitivity)
68
int joystick_axis_reduce( int j, int value )
69
{
70
	assert(j < joysticks);
71
 
72
	value = joystick_axis_threshold(j, value);
73
 
74
	if (value == 0)
75
		return 0;
76
 
77
	return value / (3000 - 200 * joystick[j].sensitivity);
78
}
79
 
80
// converts analog joystick axes to an angle
81
// returns false if axes are centered (there is no angle)
82
bool joystick_analog_angle( int j, float *angle )
83
{
84
	assert(j < joysticks);
85
 
86
	float x = joystick_axis_threshold(j, joystick[j].x), y = joystick_axis_threshold(j, joystick[j].y);
87
 
88
	if (x != 0)
89
	{
90
		*angle += atanf(-y / x);
91
		*angle += (x < 0) ? -M_PI_2 : M_PI_2;
92
		return true;
93
	}
94
	else if (y != 0)
95
	{
96
		*angle += y < 0 ? M_PI : 0;
97
		return true;
98
	}
99
 
100
	return false;
101
}
102
 
103
/* gives back value 0..joystick_analog_max indicating that one of the assigned
104
 * buttons has been pressed or that one of the assigned axes/hats has been moved
105
 * in the assigned direction
106
 */
107
int check_assigned( SDL_Joystick *joystick_handle, const Joystick_assignment assignment[2] )
108
{
109
	int result = 0;
110
 
111
	for (int i = 0; i < 2; i++)
112
	{
113
		int temp = 0;
114
 
115
		switch (assignment[i].type)
116
		{
117
		case NONE:
118
			continue;
119
 
120
		case AXIS:
121
			temp = SDL_JoystickGetAxis(joystick_handle, assignment[i].num);
122
 
123
			if (assignment[i].negative_axis)
124
				temp = -temp;
125
			break;
126
 
127
		case BUTTON:
128
			temp = SDL_JoystickGetButton(joystick_handle, assignment[i].num) == 1 ? joystick_analog_max : 0;
129
			break;
130
 
131
		case HAT:
132
			temp = SDL_JoystickGetHat(joystick_handle, assignment[i].num);
133
 
134
			if (assignment[i].x_axis)
135
				temp &= SDL_HAT_LEFT | SDL_HAT_RIGHT;
136
			else
137
				temp &= SDL_HAT_UP | SDL_HAT_DOWN;
138
 
139
			if (assignment[i].negative_axis)
140
				temp &= SDL_HAT_LEFT | SDL_HAT_UP;
141
			else
142
				temp &= SDL_HAT_RIGHT | SDL_HAT_DOWN;
143
 
144
			temp = temp ? joystick_analog_max : 0;
145
			break;
146
		}
147
 
148
		if (temp > result)
149
			result = temp;
150
	}
151
 
152
	return result;
153
}
154
 
155
// updates joystick state
156
void poll_joystick( int j )
157
{
158
	assert(j < joysticks);
159
 
160
	if (joystick[j].handle == NULL)
161
		return;
162
 
163
	SDL_JoystickUpdate();
164
 
165
	// indicates that a direction/action was pressed since last poll
166
	joystick[j].input_pressed = false;
167
 
168
	// indicates that an direction/action has been held long enough to fake a repeat press
169
	bool repeat = joystick[j].joystick_delay < SDL_GetTicks();
170
 
171
	// update direction state
172
	for (uint d = 0; d < COUNTOF(joystick[j].direction); d++)
173
	{
174
		bool old = joystick[j].direction[d];
175
 
176
		joystick[j].analog_direction[d] = check_assigned(joystick[j].handle, joystick[j].assignment[d]);
177
		joystick[j].direction[d] = joystick[j].analog_direction[d] > (joystick_analog_max / 2);
178
		joydown |= joystick[j].direction[d];
179
 
180
		joystick[j].direction_pressed[d] = joystick[j].direction[d] && (!old || repeat);
181
		joystick[j].input_pressed |= joystick[j].direction_pressed[d];
182
	}
183
 
184
	joystick[j].x = -joystick[j].analog_direction[3] + joystick[j].analog_direction[1];
185
	joystick[j].y = -joystick[j].analog_direction[0] + joystick[j].analog_direction[2];
186
 
187
	// update action state
188
	for (uint d = 0; d < COUNTOF(joystick[j].action); d++)
189
	{
190
		bool old = joystick[j].action[d];
191
 
192
		joystick[j].action[d] = check_assigned(joystick[j].handle, joystick[j].assignment[d + COUNTOF(joystick[j].direction)]) > (joystick_analog_max / 2);
193
		joydown |= joystick[j].action[d];
194
 
195
		joystick[j].action_pressed[d] = joystick[j].action[d] && (!old || repeat);
196
		joystick[j].input_pressed |= joystick[j].action_pressed[d];
197
	}
198
 
199
	joystick[j].confirm = joystick[j].action[0] || joystick[j].action[4];
200
	joystick[j].cancel = joystick[j].action[1] || joystick[j].action[5];
201
 
202
	// if new input, reset press-repeat delay
203
	if (joystick[j].input_pressed)
204
		joystick[j].joystick_delay = SDL_GetTicks() + joystick_repeat_delay;
205
}
206
 
207
// updates all joystick states
208
void poll_joysticks( void )
209
{
210
	joydown = false;
211
 
212
	for (int j = 0; j < joysticks; j++)
213
		poll_joystick(j);
214
}
215
 
216
// sends SDL KEYDOWN and KEYUP events for a key
217
void push_key( SDLKey key )
218
{
219
	SDL_Event e;
220
 
221
	memset(&e.key.keysym, 0, sizeof(e.key.keysym));
222
 
223
	e.key.keysym.sym = key;
224
	e.key.keysym.unicode = key;
225
 
226
	e.key.state = SDL_RELEASED;
227
 
228
	e.type = SDL_KEYDOWN;
229
	SDL_PushEvent(&e);
230
 
231
	e.type = SDL_KEYUP;
232
	SDL_PushEvent(&e);
233
}
234
 
235
// helps us be lazy by pretending joysticks are a keyboard (useful for menus)
236
void push_joysticks_as_keyboard( void )
237
{
238
	const SDLKey confirm = SDLK_RETURN, cancel = SDLK_ESCAPE;
239
	const SDLKey direction[4] = { SDLK_UP, SDLK_RIGHT, SDLK_DOWN, SDLK_LEFT };
240
 
241
	poll_joysticks();
242
 
243
	for (int j = 0; j < joysticks; j++)
244
	{
245
		if (!joystick[j].input_pressed)
246
			continue;
247
 
248
		if (joystick[j].confirm)
249
			push_key(confirm);
250
		if (joystick[j].cancel)
251
			push_key(cancel);
252
 
253
		for (uint d = 0; d < COUNTOF(joystick[j].direction_pressed); d++)
254
		{
255
			if (joystick[j].direction_pressed[d])
256
				push_key(direction[d]);
257
		}
258
	}
259
}
260
 
261
// initializes SDL joystick system and loads assignments for joysticks found
262
void init_joysticks( void )
263
{
264
	if (ignore_joystick)
265
		return;
266
 
267
	if (SDL_InitSubSystem(SDL_INIT_JOYSTICK))
268
	{
269
		fprintf(stderr, "warning: failed to initialize joystick system: %s\n", SDL_GetError());
270
		ignore_joystick = true;
271
		return;
272
	}
273
 
274
	SDL_JoystickEventState(SDL_IGNORE);
275
 
276
	joysticks = SDL_NumJoysticks();
277
	joystick = malloc(joysticks * sizeof(*joystick));
278
 
279
	for (int j = 0; j < joysticks; j++)
280
	{
281
		memset(&joystick[j], 0, sizeof(*joystick));
282
 
283
		joystick[j].handle = SDL_JoystickOpen(j);
284
		if (joystick[j].handle != NULL)
285
		{
286
			printf("joystick detected: %s ", SDL_JoystickName(j));
287
			printf("(%d axes, %d buttons, %d hats)\n",
288
			       SDL_JoystickNumAxes(joystick[j].handle),
289
			       SDL_JoystickNumButtons(joystick[j].handle),
290
			       SDL_JoystickNumHats(joystick[j].handle));
291
 
292
			if (!load_joystick_assignments(&opentyrian_config, j))
293
				reset_joystick_assignments(j);
294
		}
295
	}
296
 
297
	if (joysticks == 0)
298
		printf("no joysticks detected\n");
299
}
300
 
301
// deinitializes SDL joystick system and saves joystick assignments
302
void deinit_joysticks( void )
303
{
304
	if (ignore_joystick)
305
		return;
306
 
307
	for (int j = 0; j < joysticks; j++)
308
	{
309
		if (joystick[j].handle != NULL)
310
		{
311
			save_joystick_assignments(&opentyrian_config, j);
312
			SDL_JoystickClose(joystick[j].handle);
313
		}
314
	}
315
 
316
	free(joystick);
317
 
318
	SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
319
}
320
 
321
void reset_joystick_assignments( int j )
322
{
323
	assert(j < joysticks);
324
 
325
	// defaults: first 2 axes, first hat, first 6 buttons
326
	for (uint a = 0; a < COUNTOF(joystick[j].assignment); a++)
327
	{
328
		// clear assignments
329
		for (uint i = 0; i < COUNTOF(joystick[j].assignment[a]); i++)
330
			joystick[j].assignment[a][i].type = NONE;
331
 
332
		if (a < 4)
333
		{
334
			if (SDL_JoystickNumAxes(joystick[j].handle) >= 2)
335
			{
336
				joystick[j].assignment[a][0].type = AXIS;
337
				joystick[j].assignment[a][0].num = (a + 1) % 2;
338
				joystick[j].assignment[a][0].negative_axis = (a == 0 || a == 3);
339
			}
340
 
341
			if (SDL_JoystickNumHats(joystick[j].handle) >= 1)
342
			{
343
				joystick[j].assignment[a][1].type = HAT;
344
				joystick[j].assignment[a][1].num = 0;
345
				joystick[j].assignment[a][1].x_axis = (a == 1 || a == 3);
346
				joystick[j].assignment[a][1].negative_axis = (a == 0 || a == 3);
347
			}
348
		}
349
		else
350
		{
351
			if (a - 4 < (unsigned)SDL_JoystickNumButtons(joystick[j].handle))
352
			{
353
				joystick[j].assignment[a][0].type = BUTTON;
354
				joystick[j].assignment[a][0].num = a - 4;
355
			}
356
		}
357
	}
358
 
359
	joystick[j].analog = false;
360
	joystick[j].sensitivity = 5;
361
	joystick[j].threshold = 5;
362
}
363
 
364
static const char* const assignment_names[] =
365
{
366
	"up",
367
	"right",
368
	"down",
369
	"left",
370
	"fire",
371
	"change fire",
372
	"left sidekick",
373
	"right sidekick",
374
	"menu",
375
	"pause",
376
};
377
 
378
bool load_joystick_assignments( Config *config, int j )
379
{
380
	ConfigSection *section = config_find_section(config, "joystick", SDL_JoystickName(j));
381
	if (section == NULL)
382
		return false;
383
 
384
	if (!config_get_bool_option(section, "analog", &joystick[j].analog))
385
		joystick[j].analog = false;
386
 
387
	joystick[j].sensitivity = config_get_or_set_int_option(section, "sensitivity", 5);
388
 
389
	joystick[j].threshold = config_get_or_set_int_option(section, "threshold", 5);
390
 
391
	for (size_t a = 0; a < COUNTOF(assignment_names); ++a)
392
	{
393
		for (unsigned int i = 0; i < COUNTOF(joystick[j].assignment[a]); ++i)
394
			joystick[j].assignment[a][i].type = NONE;
395
 
396
		ConfigOption *option = config_get_option(section, assignment_names[a]);
397
		if (option == NULL)
398
			continue;
399
 
400
		foreach_option_i_value(i, value, option)
401
		{
402
			if (i >= COUNTOF(joystick[j].assignment[a]))
403
				break;
404
 
405
			code_to_assignment(&joystick[j].assignment[a][i], value);
406
		}
407
	}
408
 
409
	return true;
410
}
411
 
412
bool save_joystick_assignments( Config *config, int j )
413
{
414
	ConfigSection *section = config_find_or_add_section(config, "joystick", SDL_JoystickName(j));
415
	if (section == NULL)
416
		exit(EXIT_FAILURE);  // out of memory
417
 
418
	config_set_bool_option(section, "analog", joystick[j].analog, NO_YES);
419
 
420
	config_set_int_option(section, "sensitivity", joystick[j].sensitivity);
421
 
422
	config_set_int_option(section, "threshold", joystick[j].threshold);
423
 
424
	for (size_t a = 0; a < COUNTOF(assignment_names); ++a)
425
	{
426
		ConfigOption *option = config_set_option(section, assignment_names[a], NULL);
427
		if (option == NULL)
428
			exit(EXIT_FAILURE);  // out of memory
429
 
430
		option = config_set_value(option, NULL);
431
		if (option == NULL)
432
			exit(EXIT_FAILURE);  // out of memory
433
 
434
		for (size_t i = 0; i < COUNTOF(joystick[j].assignment[a]); ++i)
435
		{
436
			if (joystick[j].assignment[a][i].type == NONE)
437
				continue;
438
 
439
			option = config_add_value(option, assignment_to_code(&joystick[j].assignment[a][i]));
440
			if (option == NULL)
441
				exit(EXIT_FAILURE);  // out of memory
442
		}
443
	}
444
 
445
	return true;
446
}
447
 
448
// fills buffer with comma separated list of assigned joystick functions
449
void joystick_assignments_to_string( char *buffer, size_t buffer_len, const Joystick_assignment *assignments )
450
{
451
	strncpy(buffer, "", buffer_len);
452
 
453
	bool comma = false;
454
	for (uint i = 0; i < COUNTOF(*joystick->assignment); ++i)
455
	{
456
		if (assignments[i].type == NONE)
457
			continue;
458
 
459
		size_t len = snprintf(buffer, buffer_len, "%s%s",
460
		                      comma ? ", " : "",
461
		                      assignment_to_code(&assignments[i]));
462
		buffer += len;
463
		buffer_len -= len;
464
 
465
		comma = true;
466
	}
467
}
468
 
469
// reverse of assignment_to_code()
470
void code_to_assignment( Joystick_assignment *assignment, const char *buffer )
471
{
472
	memset(assignment, 0, sizeof(*assignment));
473
 
474
	char axis = 0, direction = 0;
475
 
476
	if (sscanf(buffer, " AX %d%c", &assignment->num, &direction) == 2)
477
		assignment->type = AXIS;
478
	else if (sscanf(buffer, " BTN %d", &assignment->num) == 1)
479
		assignment->type = BUTTON;
480
	else if (sscanf(buffer, " H %d%c%c", &assignment->num, &axis, &direction) == 3)
481
		assignment->type = HAT;
482
 
483
	if (assignment->num == 0)
484
		assignment->type = NONE;
485
	else
486
		--assignment->num;
487
 
488
	assignment->x_axis = (toupper(axis) == 'X');
489
	assignment->negative_axis = (toupper(direction) == '-');
490
}
491
 
492
/* gives the short (6 or less characters) identifier for a joystick assignment
493
 *
494
 * two of these per direction/action is all that can fit on the joystick config screen,
495
 * assuming two digits for the axis/button/hat number
496
 */
497
const char *assignment_to_code( const Joystick_assignment *assignment )
498
{
499
	static char name[7];
500
 
501
	switch (assignment->type)
502
	{
503
	case NONE:
504
		strcpy(name, "");
505
		break;
506
 
507
	case AXIS:
508
		snprintf(name, sizeof(name), "AX %d%c",
509
		         assignment->num + 1,
510
		         assignment->negative_axis ? '-' : '+');
511
		break;
512
 
513
	case BUTTON:
514
		snprintf(name, sizeof(name), "BTN %d",
515
		         assignment->num + 1);
516
		break;
517
 
518
	case HAT:
519
		snprintf(name, sizeof(name), "H %d%c%c",
520
		         assignment->num + 1,
521
		         assignment->x_axis ? 'X' : 'Y',
522
		         assignment->negative_axis ? '-' : '+');
523
		break;
524
	}
525
 
526
	return name;
527
}
528
 
529
// captures joystick input for configuring assignments
530
// returns false if non-joystick input was detected
531
// TODO: input from joystick other than the one being configured probably should not be ignored
532
bool detect_joystick_assignment( int j, Joystick_assignment *assignment )
533
{
534
	// get initial joystick state to compare against to see if anything was pressed
535
 
536
	const int axes = SDL_JoystickNumAxes(joystick[j].handle);
537
	Sint16 *axis = malloc(axes * sizeof(*axis));
538
	for (int i = 0; i < axes; i++)
539
		axis[i] = SDL_JoystickGetAxis(joystick[j].handle, i);
540
 
541
	const int buttons = SDL_JoystickNumButtons(joystick[j].handle);
542
	Uint8 *button = malloc(buttons * sizeof(*button));
543
	for (int i = 0; i < buttons; i++)
544
		button[i] = SDL_JoystickGetButton(joystick[j].handle, i);
545
 
546
	const int hats = SDL_JoystickNumHats(joystick[j].handle);
547
	Uint8 *hat = malloc(hats * sizeof(*hat));
548
	for (int i = 0; i < hats; i++)
549
		hat[i] = SDL_JoystickGetHat(joystick[j].handle, i);
550
 
551
	bool detected = false;
552
 
553
	do
554
	{
555
		setjasondelay(1);
556
 
557
		SDL_JoystickUpdate();
558
 
559
		for (int i = 0; i < axes; ++i)
560
		{
561
			Sint16 temp = SDL_JoystickGetAxis(joystick[j].handle, i);
562
 
563
			if (abs(temp - axis[i]) > joystick_analog_max * 2 / 3)
564
			{
565
				assignment->type = AXIS;
566
				assignment->num = i;
567
				assignment->negative_axis = temp < axis[i];
568
				detected = true;
569
				break;
570
			}
571
		}
572
 
573
		for (int i = 0; i < buttons; ++i)
574
		{
575
			Uint8 new_button = SDL_JoystickGetButton(joystick[j].handle, i),
576
			      changed = button[i] ^ new_button;
577
 
578
			if (!changed)
579
				continue;
580
 
581
			if (new_button == 0) // button was released
582
			{
583
				button[i] = new_button;
584
			}
585
			else                 // button was pressed
586
			{
587
				assignment->type = BUTTON;
588
				assignment->num = i;
589
				detected = true;
590
				break;
591
			}
592
		}
593
 
594
		for (int i = 0; i < hats; ++i)
595
		{
596
			Uint8 new_hat = SDL_JoystickGetHat(joystick[j].handle, i),
597
			      changed = hat[i] ^ new_hat;
598
 
599
			if (!changed)
600
				continue;
601
 
602
			if ((new_hat & changed) == SDL_HAT_CENTERED) // hat was centered
603
			{
604
				hat[i] = new_hat;
605
			}
606
			else
607
			{
608
				assignment->type = HAT;
609
				assignment->num = i;
610
				assignment->x_axis = changed & (SDL_HAT_LEFT | SDL_HAT_RIGHT);
611
				assignment->negative_axis = changed & (SDL_HAT_LEFT | SDL_HAT_UP);
612
				detected = true;
613
			}
614
		}
615
 
616
		service_SDL_events(true);
617
		JE_showVGA();
618
 
619
		wait_delay();
620
	}
621
	while (!detected && !newkey && !newmouse);
622
 
623
	free(axis);
624
	free(button);
625
	free(hat);
626
 
627
	return detected;
628
}
629
 
630
// compares relevant parts of joystick assignments for equality
631
bool joystick_assignment_cmp( const Joystick_assignment *a, const Joystick_assignment *b )
632
{
633
	if (a->type == b->type)
634
	{
635
		switch (a->type)
636
		{
637
		case NONE:
638
			return true;
639
		case AXIS:
640
			return (a->num == b->num) &&
641
			       (a->negative_axis == b->negative_axis);
642
		case BUTTON:
643
			return (a->num == b->num);
644
		case HAT:
645
			return (a->num == b->num) &&
646
			       (a->x_axis == b->x_axis) &&
647
			       (a->negative_axis == b->negative_axis);
648
		}
649
	}
650
 
651
	return false;
652
}
653