Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
OOJoystickManager.m
Go to the documentation of this file.
1/*
2
3OOJoystickManager.m
4By Dylan Smith
5modified by Alex Smith
6
7Oolite
8Copyright (C) 2004-2013 Giles C Williams and contributors
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23MA 02110-1301, USA.
24
25*/
26
27#import "OOJoystickManager.h"
28#import "OOLogging.h"
30
31
32static Class sStickHandlerClass = Nil;
34
35
36@interface OOJoystickManager (Private)
37
38// Setting button and axis functions
39- (void) setFunctionForAxis:(int)axis
40 function:(int)function
41 stick:(int)stickNum;
42
43- (void) setFunctionForButton:(int)button
44 function:(int)function
45 stick:(int)stickNum;
46
47@end
48
49
50
51@implementation OOJoystickManager
52
53+ (id) sharedStickHandler
54{
56 {
58 sSharedStickHandler = [[sStickHandlerClass alloc] init];
59 }
61}
62
63
64+ (BOOL) setStickHandlerClass:(Class)aClass
65{
66 NSAssert(sStickHandlerClass == nil, @"Can't set joystick handler class after joystick handler is initialized.");
67 NSParameterAssert(aClass == Nil || [aClass isSubclassOfClass:[OOJoystickManager class]]);
68
69 sStickHandlerClass = aClass;
70 return YES;
71}
72
73
74- (id) init
75{
76 if ((self = [super init]))
77 {
78 // set initial values for stick buttons/axes (NO for buttons,
79 // STICK_AXISUNASSIGNED for axes). Caution: calling this again
80 // after axes have been assigned will set all the axes to
81 // STICK_AXISUNASSIGNED so if there is a need to do something
82 // like this, then do it some other way, or change this method
83 // so it doesn't do that.
84 [self clearStickStates];
85
86 // Make some sensible mappings. This also ensures unassigned
87 // axes and buttons are set to unassigned (STICK_NOFUNCTION).
88 [self loadStickSettings];
89 invertPitch = NO;
90 precisionMode = NO;
91 }
92 return self;
93}
94
95
96
97- (NSPoint) rollPitchAxis
98{
99 return NSMakePoint([self getAxisState:AXIS_ROLL], [self getAxisState:AXIS_PITCH]);
100}
101
102
103- (NSPoint) viewAxis
104{
105 return NSMakePoint(axstate[AXIS_VIEWX], axstate[AXIS_VIEWY]);
106}
107
108
109- (BOOL) getButtonState: (int)function
110{
111 return butstate[function];
112}
113
114
115- (const BOOL *)getAllButtonStates
116{
117 return butstate;
118}
119
120- (BOOL) isButtonDown:(int)button stick:(int)stickNum
121{
122 return true_butstate[stickNum][button];
123}
124
125- (double) getAxisState: (int)function
126{
127 if (axstate[function] == STICK_AXISUNASSIGNED)
128 {
130 }
131 switch (function)
132 {
133 case AXIS_ROLL:
134 if (precisionMode)
135 {
136 return [roll_profile value:axstate[function]] / STICK_PRECISIONFAC;
137 }
138 else
139 {
140 return [roll_profile value:axstate[function]];
141 }
142 case AXIS_PITCH:
143 if (precisionMode)
144 {
145 return [pitch_profile value:axstate[function]] / STICK_PRECISIONFAC;
146 }
147 else
148 {
149 return [pitch_profile value:axstate[function]];
150 }
151 case AXIS_YAW:
152 if (precisionMode)
153 {
154 return [yaw_profile value:axstate[function]] / STICK_PRECISIONFAC;
155 }
156 else
157 {
158 return [yaw_profile value:axstate[function]];
159 }
160 default:
161 return axstate[function];
162 }
163}
164
165
166- (double) getSensitivity
167{
168 return precisionMode ? STICK_PRECISIONFAC : 1.0;
169}
170
171- (void) setProfile: (OOJoystickAxisProfile *) profile forAxis: (int) axis
172{
173 switch (axis)
174 {
175 case AXIS_ROLL:
176 [roll_profile release];
177 roll_profile = [profile retain];
178 break;
179
180 case AXIS_PITCH:
181 [pitch_profile release];
182 pitch_profile = [profile retain];
183 break;
184
185 case AXIS_YAW:
186 [yaw_profile release];
187 yaw_profile = [profile retain];
188 break;
189 }
190 return;
191}
192
193- (OOJoystickAxisProfile *) getProfileForAxis: (int) axis
194{
195 switch (axis)
196 {
197 case AXIS_ROLL:
198 return roll_profile;
199 case AXIS_PITCH:
200 return pitch_profile;
201 case AXIS_YAW:
202 return yaw_profile;
203 }
204 return nil;
205}
206
207
208- (void) saveProfileForAxis: (int) axis
209{
210 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
211 NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
212 OOJoystickAxisProfile *profile;
213 OOJoystickStandardAxisProfile *standard_profile;
214 OOJoystickSplineAxisProfile *spline_profile;
215 NSArray *controlPoints;
216 NSMutableArray *points;
217 NSPoint point;
218 NSUInteger i;
219
220 profile = [self getProfileForAxis: axis];
221 if (!profile) return;
222 [dict setObject: [NSNumber numberWithDouble: [profile deadzone]] forKey: @"Deadzone"];
223 if ([profile isKindOfClass: [OOJoystickStandardAxisProfile class]])
224 {
225 standard_profile = (OOJoystickStandardAxisProfile *) profile;
226 [dict setObject: @"Standard" forKey: @"Type"];
227 [dict setObject: [NSNumber numberWithDouble: [standard_profile power]] forKey: @"Power"];
228 [dict setObject: [NSNumber numberWithDouble: [standard_profile parameter]] forKey: @"Parameter"];
229 }
230 else if ([profile isKindOfClass: [OOJoystickSplineAxisProfile class]])
231 {
232 spline_profile = (OOJoystickSplineAxisProfile *) profile;
233 [dict setObject: @"Spline" forKey: @"Type"];
234 controlPoints = [NSArray arrayWithArray: [spline_profile controlPoints]];
235 points = [[NSMutableArray alloc] initWithCapacity: [controlPoints count]];
236 for (i = 0; i < [controlPoints count]; i++)
237 {
238 point = [[controlPoints objectAtIndex: i] pointValue];
239 [points addObject: [NSArray arrayWithObjects:
240 [NSNumber numberWithFloat: point.x],
241 [NSNumber numberWithFloat: point.y],
242 nil ]];
243 }
244 [dict setObject: points forKey: @"ControlPoints"];
245 }
246 else
247 {
248 [dict setObject: @"Standard" forKey: @"Type"];
249 }
250 if (axis == AXIS_ROLL)
251 {
252 [defaults setObject: dict forKey: STICK_ROLL_AXIS_PROFILE_SETTING];
253 }
254 else if (axis == AXIS_PITCH)
255 {
256 [defaults setObject: dict forKey: STICK_PITCH_AXIS_PROFILE_SETTING];
257 }
258 else if (axis == AXIS_YAW)
259 {
260 [defaults setObject: dict forKey: STICK_YAW_AXIS_PROFILE_SETTING];
261 }
262 return;
263}
264
265
266
267- (void) loadProfileForAxis: (int) axis
268{
269 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
270 NSDictionary *dict;
271 OOJoystickStandardAxisProfile *standard_profile;
272 OOJoystickSplineAxisProfile *spline_profile;
273
274 if (axis == AXIS_ROLL)
275 {
276 dict = [defaults objectForKey: STICK_ROLL_AXIS_PROFILE_SETTING];
277 }
278 else if (axis == AXIS_PITCH)
279 {
280 dict = [defaults objectForKey: STICK_PITCH_AXIS_PROFILE_SETTING];
281 }
282 else if (axis == AXIS_YAW)
283 {
284 dict = [defaults objectForKey: STICK_YAW_AXIS_PROFILE_SETTING];
285 }
286 else
287 {
288 return;
289 }
290
291 NSString *type = [dict objectForKey: @"Type"];
292 if ([type isEqualToString: @"Standard"])
293 {
294 standard_profile = [[OOJoystickStandardAxisProfile alloc] init];
295 [standard_profile setDeadzone: [[dict objectForKey: @"Deadzone"] doubleValue]];
296 [standard_profile setPower: [[dict objectForKey: @"Power"] doubleValue]];
297 [standard_profile setParameter: [[dict objectForKey: @"Parameter"] doubleValue]];
298 [self setProfile: [standard_profile autorelease] forAxis: axis];
299 }
300 else if([type isEqualToString: @"Spline"])
301 {
302 spline_profile = [[OOJoystickSplineAxisProfile alloc] init];
303 [spline_profile setDeadzone: [[dict objectForKey: @"Deadzone"] doubleValue]];
304 NSArray *points = [dict objectForKey: @"ControlPoints"], *pointArray;
305 NSPoint point;
306 NSUInteger i;
307
308 for (i = 0; i < [points count]; i++)
309 {
310 pointArray = [points objectAtIndex: i];
311 if ([pointArray count] >= 2)
312 {
313 point = NSMakePoint([[pointArray objectAtIndex: 0] floatValue], [[pointArray objectAtIndex: 1] floatValue]);
314 [spline_profile addControl: point];
315 }
316 }
317 [self setProfile: [spline_profile autorelease] forAxis: axis];
318 }
319 else
320 {
321 [self setProfile: [[[OOJoystickStandardAxisProfile alloc] init] autorelease] forAxis: axis];
322 }
323}
324
325- (NSArray *)listSticks
326{
327 NSUInteger i, stickCount = [self joystickCount];
328
329 NSMutableArray *stickList = [NSMutableArray array];
330 for (i = 0; i < stickCount; i++)
331 {
332 [stickList addObject:[self nameOfJoystick:i]];
333 }
334 return stickList;
335}
336
337
338- (NSDictionary *) axisFunctions
339{
340 int i,j;
341 NSMutableDictionary *fnList = [NSMutableDictionary dictionary];
342
343 // Add axes
344 for (i = 0; i < MAX_AXES; i++)
345 {
346 for (j = 0; j < MAX_STICKS; j++)
347 {
348 if(axismap[j][i] >= 0)
349 {
350 NSDictionary *fnDict=[NSDictionary dictionaryWithObjectsAndKeys:
351 [NSNumber numberWithBool:YES], STICK_ISAXIS,
352 [NSNumber numberWithInt:j], STICK_NUMBER,
353 [NSNumber numberWithInt:i], STICK_AXBUT,
354 nil];
355 [fnList setValue: fnDict
356 forKey: ENUMKEY(axismap[j][i])];
357 }
358 }
359 }
360 return fnList;
361}
362
363
364- (NSDictionary *)buttonFunctions
365{
366 int i, j;
367 NSMutableDictionary *fnList = [NSMutableDictionary dictionary];
368
369 // Add buttons
370 for (i = 0; i < MAX_BUTTONS; i++)
371 {
372 for (j = 0; j < MAX_STICKS; j++)
373 {
374 if(buttonmap[j][i] >= 0)
375 {
376 NSDictionary *fnDict = [NSDictionary dictionaryWithObjectsAndKeys:
377 [NSNumber numberWithBool:NO], STICK_ISAXIS,
378 [NSNumber numberWithInt:j], STICK_NUMBER,
379 [NSNumber numberWithInt:i], STICK_AXBUT,
380 nil];
381 [fnList setValue:fnDict
382 forKey:ENUMKEY(buttonmap[j][i])];
383 }
384 }
385 }
386 return fnList;
387}
388
389
390- (void) setFunction:(int)function withDict:(NSDictionary *)stickFn
391{
392 BOOL isAxis = [stickFn oo_boolForKey:STICK_ISAXIS];
393 int stickNum = [stickFn oo_intForKey:STICK_NUMBER];
394 int stickAxBt = [stickFn oo_intForKey:STICK_AXBUT];
395
396 if (isAxis)
397 {
398 [self setFunctionForAxis:stickAxBt
399 function:function
400 stick:stickNum];
401 }
402 else
403 {
404 [self setFunctionForButton:stickAxBt
405 function:function
406 stick:stickNum];
407 }
408}
409
410
411- (void) setFunctionForAxis:(int)axis
412 function:(int)function
413 stick:(int)stickNum
414{
415 NSParameterAssert(axis < MAX_AXES && stickNum < MAX_STICKS);
416
417 int16_t axisvalue = [self getAxisWithStick:stickNum axis:axis];
418 [self unsetAxisFunction:function];
419 axismap[stickNum][axis] = function;
420
421 // initialize the throttle to what it's set to now (or else the
422 // commander has to waggle the throttle to wake it up). Other axes
423 // set as default.
424 if(function == AXIS_THRUST)
425 {
426 axstate[function] = (float)(65536 - (axisvalue + 32768)) / 65536;
427 }
428 else
429 {
430 axstate[function] = (float)axisvalue / STICK_NORMALDIV;
431 }
432}
433
434
435- (void) setFunctionForButton:(int)button
436 function:(int)function
437 stick:(int)stickNum
438{
439 NSParameterAssert(button < MAX_BUTTONS && stickNum < MAX_STICKS);
440
441 int i, j;
442 for (i = 0; i < MAX_BUTTONS; i++)
443 {
444 for (j = 0; j < MAX_STICKS; j++)
445 {
446 if (buttonmap[j][i] == function)
447 {
448 buttonmap[j][i] = STICK_NOFUNCTION;
449 break;
450 }
451 }
452 }
453 buttonmap[stickNum][button] = function;
454}
455
456
457- (void) unsetAxisFunction:(int)function
458{
459 int i, j;
460 for (i = 0; i < MAX_AXES; i++)
461 {
462 for (j = 0; j < MAX_STICKS; j++)
463 {
464 if (axismap[j][i] == function)
465 {
466 axismap[j][i] = STICK_NOFUNCTION;
467 axstate[function] = STICK_AXISUNASSIGNED;
468 break;
469 }
470 }
471 }
472}
473
474
475- (void) unsetButtonFunction:(int)function
476{
477 int i,j;
478 for (i = 0; i < MAX_BUTTONS; i++)
479 {
480 for (j = 0; j < MAX_STICKS; j++)
481 {
482 if(buttonmap[j][i] == function)
483 {
484 buttonmap[j][i] = STICK_NOFUNCTION;
485 break;
486 }
487 }
488 }
489}
490
491
492- (void) setDefaultMapping
493{
494 // assign the simplest mapping: stick 0 having
495 // axis 0/1 being roll/pitch and button 0 being fire, 1 being missile
496 // All joysticks should at least have two axes and two buttons.
497 axismap[0][0] = AXIS_ROLL;
498 axismap[0][1] = AXIS_PITCH;
499 buttonmap[0][0] = BUTTON_FIRE;
500 buttonmap[0][1] = BUTTON_LAUNCHMISSILE;
501}
502
503
504- (void) clearMappings
505{
506 memset(axismap, STICK_NOFUNCTION, sizeof axismap);
507 memset(buttonmap, STICK_NOFUNCTION, sizeof buttonmap);
508}
509
510
511- (void) clearStickStates
512{
513 int i, j;
514 for (i = 0; i < AXIS_end; i++)
515 {
516 axstate[i] = STICK_AXISUNASSIGNED;
517 }
518 for (i = 0; i < BUTTON_end; i++)
519 {
520 butstate[i] = 0;
521 }
522 for (i = 0; i < MAX_BUTTONS; i++)
523 {
524 for (j = 0; j < MAX_STICKS; j++)
525 {
526 true_butstate[j][i] = NO;
527 }
528 }
529}
530
531
532- (void) clearStickButtonState:(int)stickButton
533{
534 if (stickButton >= 0 && stickButton < BUTTON_end)
535 {
536 butstate[stickButton] = 0;
537 }
538}
539
540
541- (void)setCallback:(SEL) selector
542 object:(id) obj
543 hardware:(char)hwflags
544{
545 cbObject = obj;
546 cbSelector = selector;
547 cbHardware = hwflags;
548}
549
550
551- (void)clearCallback
552{
553 cbObject = nil;
554 cbHardware = 0;
555}
556
557
558- (void)decodeAxisEvent: (JoyAxisEvent *)evt
559{
560 // Which axis moved? Does the value need to be made to fit a
561 // certain function? Convert axis value to a double.
562 double axisvalue = (double)evt->value;
563
564 // First check if there is a callback and...
565 if(cbObject && (cbHardware & HW_AXIS))
566 {
567 // ...then check if axis moved more than AXCBTHRESH - (fix for BUG #17482)
568 if(axisvalue > AXCBTHRESH)
569 {
570 NSDictionary *fnDict = [NSDictionary dictionaryWithObjectsAndKeys:
571 [NSNumber numberWithBool: YES], STICK_ISAXIS,
572 [NSNumber numberWithInt: evt->which], STICK_NUMBER,
573 [NSNumber numberWithInt: evt->axis], STICK_AXBUT,
574 nil];
575 cbHardware = 0;
576 [cbObject performSelector:cbSelector withObject:fnDict];
577 cbObject = nil;
578 }
579
580 // we are done.
581 return;
582 }
583
584 // SDL seems to have some bizarre (perhaps a bug) behaviour when
585 // events get queued up because the game isn't ready to handle
586 // them (perhaps it's loading a commander and initializing the
587 // universe, and the main event loop is blocked).
588 // What happens is SDL lies about the axis that was triggered. For
589 // each queued event it adds 1 to the axis number!! This does
590 // not seem to happen with buttons.
591 int function;
592 if (evt->axis < MAX_AXES)
593 {
594 function = axismap[evt->which][evt->axis];
595 }
596 else
597 {
598 OOLog(@"decodeAxisEvent", @"Stick axis out of range - axis was %d", evt->axis);
599 return;
600 }
601 switch (function)
602 {
603 case STICK_NOFUNCTION:
604 // do nothing
605 break;
606 case AXIS_THRUST:
607 // Normalize the thrust setting.
608 axstate[function] = (float)(65536 - (axisvalue + 32768)) / 65536;
609 break;
610 case AXIS_ROLL:
611 case AXIS_PITCH:
612 case AXIS_YAW:
613 case AXIS_VIEWX:
614 case AXIS_VIEWY:
615 axstate[function] = axisvalue / STICK_NORMALDIV;
616 break;
617 // TODO AXIS_FIELD_OF_VIEW
618 default:
619 // set the state with no modification.
620 axstate[function] = axisvalue / 32768;
621 }
622 if ((function == AXIS_PITCH) && invertPitch) axstate[function] = -1.0*axstate[function];
623}
624
625
626- (void) decodeButtonEvent:(JoyButtonEvent *)evt
627{
628 BOOL bs = NO;
629
630 // Is there a callback we need to make?
631 if(cbObject && (cbHardware & HW_BUTTON))
632 {
633 NSDictionary *fnDict = [NSDictionary dictionaryWithObjectsAndKeys:
634 [NSNumber numberWithBool: NO], STICK_ISAXIS,
635 [NSNumber numberWithInt: evt->which], STICK_NUMBER,
636 [NSNumber numberWithInt: evt->button], STICK_AXBUT,
637 nil];
638 cbHardware = 0;
639 [cbObject performSelector:cbSelector withObject:fnDict];
640 cbObject = nil;
641
642 // we are done.
643 return;
644 }
645
646 // Defensive measure - see comments in the axis handler for why.
647 int function;
648 if (evt->button < MAX_BUTTONS)
649 {
650 function = buttonmap[evt->which][evt->button];
651 }
652 else
653 {
654 OOLog(@"decodeButtonEvent", @"Joystick button out of range: %d", evt->button);
655 return;
656 }
657 if (evt->type == JOYBUTTONDOWN)
658 {
659 bs = YES;
660 if(function == BUTTON_PRECISION)
661 precisionMode = !precisionMode;
662 }
663 true_butstate[evt->which][evt->button] = bs;
664 if (function >= 0)
665 {
666 butstate[function]=bs;
667 }
668
669}
670
671
672- (void) decodeHatEvent:(JoyHatEvent *)evt
673{
674 // HACK: handle this as a set of buttons
675 int i;
676 JoyButtonEvent btn;
677
678 btn.which = evt->which;
679
680 for (i = 0; i < 4; ++i)
681 {
682 if ((evt->value ^ hatstate[evt->which][evt->hat]) & (1 << i))
683 {
684 btn.type = (evt->value & (1 << i)) ? JOYBUTTONDOWN : JOYBUTTONUP;
685 btn.button = MAX_REAL_BUTTONS + i + evt->which * 4;
686 btn.state = (evt->value & (1 << i)) ? JOYBUTTON_PRESSED : JOYBUTTON_RELEASED;
687 [self decodeButtonEvent:&btn];
688 }
689 }
690
691 hatstate[evt->which][evt->hat] = evt->value;
692}
693
694
695- (NSUInteger) joystickCount
696{
697 return 0;
698}
699
700
701- (void) saveStickSettings
702{
703 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
704
705 [defaults setObject:[self axisFunctions]
706 forKey:AXIS_SETTINGS];
707 [defaults setObject:[self buttonFunctions]
708 forKey:BUTTON_SETTINGS];
709 [self saveProfileForAxis: AXIS_ROLL];
710 [self saveProfileForAxis: AXIS_PITCH];
711 [self saveProfileForAxis: AXIS_YAW];
712 [defaults synchronize];
713}
714
715
716- (void) loadStickSettings
717{
718 unsigned i;
719 [self clearMappings];
720 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
721 NSDictionary *axisSettings = [defaults objectForKey: AXIS_SETTINGS];
722 NSDictionary *buttonSettings = [defaults objectForKey: BUTTON_SETTINGS];
723 if(axisSettings)
724 {
725 NSArray *keys = [axisSettings allKeys];
726 for (i = 0; i < [keys count]; i++)
727 {
728 NSString *key = [keys objectAtIndex: i];
729 [self setFunction: [key intValue]
730 withDict: [axisSettings objectForKey: key]];
731 }
732 }
733 if(buttonSettings)
734 {
735 NSArray *keys = [buttonSettings allKeys];
736 for (i = 0; i < [keys count]; i++)
737 {
738 NSString *key = [keys objectAtIndex: i];
739 [self setFunction:[key intValue]
740 withDict:[buttonSettings objectForKey: key]];
741 }
742 }
743 else
744 {
745 // Nothing to load - set useful defaults
746 [self setDefaultMapping];
747 }
748 [self loadProfileForAxis: AXIS_ROLL];
749 [self loadProfileForAxis: AXIS_PITCH];
750 [self loadProfileForAxis: AXIS_YAW];
751}
752
753// These get overidden by subclasses
754
755- (NSString *) nameOfJoystick:(NSUInteger)stickNumber
756{
757 return @"Dummy joystick";
758}
759
760- (int16_t) getAxisWithStick:(NSUInteger)stickNum axis:(NSUInteger)axisNum
761{
762 return 0;
763}
764
765@end
@ BUTTON_end
@ BUTTON_LAUNCHMISSILE
@ BUTTON_FIRE
#define MAX_AXES
#define MAX_STICKS
@ AXIS_VIEWX
@ AXIS_PITCH
@ AXIS_YAW
@ AXIS_end
@ AXIS_THRUST
@ AXIS_ROLL
@ AXIS_VIEWY
@ JOYBUTTONDOWN
#define STICK_NOFUNCTION
#define HW_BUTTON
#define MAX_BUTTONS
#define STICK_PRECISIONFAC
#define AXCBTHRESH
#define STICK_NORMALDIV
#define STICK_AXISUNASSIGNED
#define HW_AXIS
static Class sStickHandlerClass
static id sSharedStickHandler
unsigned count
return nil
void setDeadzone:(double newValue)
void setParameter:(double newValue)