1/* Copyright (C) 2010, 2011 Chaoji Li */ 2 3#import "GamePadView.h" 4 5 6#define CENTER_OF_RECT(r) CGPointMake(r.size.width/2,r.size.height/2) 7#define DISTANCE_BETWEEN(a,b) sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)) 8 9#define CHOP(x) \ 10if ((x) > 1.0) { \ 11 (x) = 1.0; \ 12} else if ((x) < -1.0) { \ 13 (x) = -1.0; \ 14} else {} 15 16/** DPadView */ 17@implementation DPadView 18@synthesize backgroundImage; 19@synthesize images; 20 21- (id)initWithFrame:(CGRect)frame { 22 if ((self = [super initWithFrame:frame])) { 23 self.backgroundColor = [UIColor clearColor]; 24 self.backgroundImage = [UIImage imageNamed:@"joythumb-glass.png"]; 25 26#if SDL_VERSION_ATLEAST(2,0,13) 27 int vjoy_index = SDL_JoystickAttachVirtual( 28 SDL_JOYSTICK_TYPE_GAMECONTROLLER, 29 SDL_CONTROLLER_AXIS_MAX, 30 SDL_CONTROLLER_BUTTON_MAX, 31 0 32 ); 33 if (vjoy_index < 0) { 34 printf("SDL_JoystickAttachVirtual failed: %s\n", SDL_GetError()); 35 } else { 36 vjoy_controller = SDL_GameControllerOpen(vjoy_index); 37 if (!vjoy_controller) { 38 printf("SDL_GameControllerOpen failed for virtual joystick: %s\n", SDL_GetError()); 39 SDL_JoystickDetachVirtual(vjoy_index); 40 } 41 } 42 [self reset]; 43 // printf("VJOY INIT, controller=%p\n", vjoy_controller); 44#endif 45 } 46 return self; 47} 48 49- (void)drawRect:(CGRect)rect 50{ 51 if (backgroundImage) { 52 [backgroundImage drawInRect:rect]; 53 } 54} 55 56- (void)dealloc { 57#if SDL_VERSION_ATLEAST(2,0,13) 58 if (vjoy_controller) { 59 const SDL_JoystickID vjoy_controller_id = SDL_JoystickInstanceID( 60 SDL_GameControllerGetJoystick(vjoy_controller) 61 ); 62 SDL_GameControllerClose(vjoy_controller); 63 for (int i = 0, n = SDL_NumJoysticks(); i < n; ++i) { 64 const SDL_JoystickID current_id = SDL_JoystickGetDeviceInstanceID(i); 65 if (current_id == vjoy_controller_id) { 66 // printf("detach virtual at id:%d, index:%d\n", current_id, i); 67 SDL_JoystickDetachVirtual(i); 68 break; 69 } 70 } 71 } 72#endif 73 74 [backgroundImage release]; 75 [images release]; 76 [super dealloc]; 77} 78 79- (void)reset 80{ 81 vjoy_is_active = false; 82 SDL_JoystickSetVirtualAxis( 83 SDL_GameControllerGetJoystick(vjoy_controller), 84 SDL_CONTROLLER_AXIS_LEFTX, 85 0 86 ); 87 SDL_JoystickSetVirtualAxis( 88 SDL_GameControllerGetJoystick(vjoy_controller), 89 SDL_CONTROLLER_AXIS_LEFTY, 90 0 91 ); 92 vjoy_center = vjoy_current = CGPointMake(0,0); 93 vjoy_input_source = nil; 94 [self updateViewTransform]; 95} 96 97- (void)updateViewTransform 98{ 99 if (!vjoy_is_active) { 100 self.transform = CGAffineTransformIdentity; 101// printf("updateViewTransform: reset to identity\n"); 102 } else { 103 104 // Calculate the position of vjoy_center within this view's 105 // parent/super-view. This requires first resetting this view's 106 // UIKit 'transform' to an untransformed state, as UIView's 107 // method, 'convertPoint:toView:', will apply any existing 108 // transform. 109 self.transform = CGAffineTransformIdentity; 110 CGPoint vjoy_center_in_parent_view = [self convertPoint:vjoy_center toView:self.superview]; 111 const CGPoint translation = CGPointMake( 112 vjoy_center_in_parent_view.x - self.center.x, 113 vjoy_center_in_parent_view.y - self.center.y 114 ); 115 self.transform = CGAffineTransformMakeTranslation(translation.x, translation.y); 116 } 117 [self setNeedsDisplay]; 118} 119 120 121- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 122{ 123 UITouch *touch = [touches anyObject]; 124 // printf("touchesBegan, %p\n", touch); 125 126#if SDL_VERSION_ATLEAST(2,0,13) 127 if (!vjoy_is_active && touch != nil) { 128 vjoy_input_source = touch; 129 vjoy_center = vjoy_current = [touch locationInView:self]; 130 vjoy_is_active = true; 131 SDL_JoystickSetVirtualAxis( 132 SDL_GameControllerGetJoystick(vjoy_controller), 133 SDL_CONTROLLER_AXIS_LEFTX, 134 0 135 ); 136 SDL_JoystickSetVirtualAxis( 137 SDL_GameControllerGetJoystick(vjoy_controller), 138 SDL_CONTROLLER_AXIS_LEFTY, 139 0 140 ); 141 [self updateViewTransform]; 142 // printf("VJOY START\n"); 143 } 144#endif 145} 146 147- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 148{ 149// printf("touchesMoved, vjoy_input_source:%p, touch:%p\n", vjoy_input_source, touch); 150 151#if SDL_VERSION_ATLEAST(2,0,13) 152 UITouch * __strong current_input_source = vjoy_input_source; 153 if (vjoy_is_active && [touches containsObject:current_input_source]) { 154 155 // Calculate new vjoy_current, but first, reset this view's 156 // UIKit-transform, lest the call to UITouch's 'locationInView:' 157 // method reports incorrect values (due to a previously-applied 158 // transform). 159 self.transform = CGAffineTransformIdentity; 160 vjoy_current = [current_input_source locationInView:self]; 161 162 float dx = vjoy_current.x - vjoy_center.x; 163 float dy = vjoy_current.y - vjoy_center.y; 164 165 // Move the vjoy's center if it's outside of its radius 166 float dlength = sqrt((dx * dx) + (dy * dy)); 167 if (dlength > vjoy_radius) { 168 vjoy_center.x = vjoy_current.x - (dx * (vjoy_radius / dlength)); 169 vjoy_center.y = vjoy_current.y - (dy * (vjoy_radius / dlength)); 170 dx = vjoy_current.x - vjoy_center.x; 171 dy = vjoy_current.y - vjoy_center.y; 172 } 173 174 // Update vjoy state 175 const Sint16 joy_axis_x_raw = (Sint16)((dx / vjoy_radius) * SDL_JOYSTICK_AXIS_MAX); 176 SDL_JoystickSetVirtualAxis( 177 SDL_GameControllerGetJoystick(vjoy_controller), 178 SDL_CONTROLLER_AXIS_LEFTX, 179 joy_axis_x_raw 180 ); 181 const Sint16 joy_axis_y_raw = (Sint16)((dy / vjoy_radius) * SDL_JOYSTICK_AXIS_MAX); 182 SDL_JoystickSetVirtualAxis( 183 SDL_GameControllerGetJoystick(vjoy_controller), 184 SDL_CONTROLLER_AXIS_LEFTY, 185 joy_axis_y_raw 186 ); 187 188 // Update visuals 189 [self updateViewTransform]; 190 191 // printf("VJOY MOVE: %d, %d\n", (int)joy_axis_x_raw, (int)joy_axis_y_raw); 192 } 193#endif 194} 195 196- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 197{ 198 // printf("touchesEnded, vjoy_input_source:%p, touch:%p\n", vjoy_input_source, touch); 199 200#if SDL_VERSION_ATLEAST(2,0,13) 201 if ([touches containsObject:vjoy_input_source]) { 202 // Mark vjoy as inactive 203 [self reset]; 204 } 205#endif 206} 207 208- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 209{ 210 CGPoint ptCenter = CENTER_OF_RECT(self.bounds); 211 return DISTANCE_BETWEEN(point, ptCenter) < self.bounds.size.width/2; 212} 213 214@end 215 216/** GamePadButton */ 217@implementation GamePadButton 218@synthesize pressed; 219@synthesize keyCodes; 220@synthesize style; 221@synthesize title; 222@synthesize images; 223@synthesize textColor; 224@synthesize delegate; 225 226- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 227{ 228 if (style == GamePadButtonStyleCircle) { 229 CGPoint ptCenter = CENTER_OF_RECT(self.bounds); 230 return DISTANCE_BETWEEN(point, ptCenter) < self.bounds.size.width/2; 231 } else { 232 return [super pointInside:point withEvent:event]; 233 } 234} 235 236- (void)setTitle:(NSString *)s 237{ 238 if (title) [title release]; 239 title = s; 240 [title retain]; 241 [self setNeedsDisplay]; 242} 243 244- (void)setPressed:(BOOL)b 245{ 246 if (pressed != b) { 247 pressed = b; 248 if (delegate) { 249 if (b) 250 [delegate buttonDown:self]; 251 else 252 [delegate buttonUp:self]; 253 } 254 [self setNeedsDisplay]; 255 } 256} 257 258- (void)setStyle:(GamePadButtonStyle)s 259{ 260 style = s; 261 [self setNeedsDisplay]; 262} 263 264- (id)initWithFrame:(CGRect)frame { 265 if ((self = [super initWithFrame:frame])) { 266 self.backgroundColor = [UIColor clearColor]; 267 self.textColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1]; 268 keyCodes = [[NSMutableArray alloc] initWithCapacity:2]; 269 } 270 return self; 271} 272 273- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 274{ 275 self.pressed = YES; 276} 277 278- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 279{ 280 self.pressed = NO; 281// [[self superview] touchesEnded:touches withEvent:event]; 282} 283 284-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 285{ 286// [[self superview] touchesMoved:touches withEvent:event]; 287} 288 289- (void)drawRect:(CGRect)rect 290{ 291 int bkgIndex; 292 UIColor *color = nil; 293 294 if (!pressed) { 295 color = [textColor colorWithAlphaComponent:0.3]; 296 bkgIndex = 0; 297 } else { 298 color = [textColor colorWithAlphaComponent:0.8]; 299 bkgIndex = 1; 300 } 301 302 if ([images count] > bkgIndex) { 303 UIImage *image = [images objectAtIndex:bkgIndex]; 304 [image drawInRect:rect]; 305 } 306 307 if (title) { 308 float fontSize = MIN(14, rect.size.height/4); 309 UIFont *fnt = [UIFont systemFontOfSize:fontSize]; 310 CGSize size = [title sizeWithAttributes:@{NSFontAttributeName:fnt}]; 311 CGRect rc = CGRectMake((rect.size.width-size.width)/2, 312 (rect.size.height-size.height)/2, 313 size.width, size.height); 314 [color setFill]; 315 [title drawInRect:rc withAttributes:@{NSFontAttributeName:fnt, 316 NSForegroundColorAttributeName: color}]; 317 } 318} 319 320- (void)dealloc { 321 [images release]; 322 [title release]; 323 [textColor release]; 324 [keyCodes release]; 325 [super dealloc]; 326} 327 328@end 329/** End */ 330