1#import <QuartzCore/QuartzCore.h> 2#import <OpenGLES/EAGLDrawable.h> 3 4#import "EAGLView.h" 5#import "allegroAppDelegate.h" 6#include <pthread.h> 7#include <float.h> 8 9#include "allegro5/allegro_iphone.h" 10#include "allegro5/internal/aintern_iphone.h" 11 12ALLEGRO_DEBUG_CHANNEL("iphone") 13 14typedef struct touch_t 15{ 16 int id; 17 UITouch* touch; 18} touch_t; 19 20/* Every UITouch have associated touch_t structure. This destructor 21 * is used in list which held touch information. While ending touch it will 22 * be called and memory will be freed. 23 */ 24static void touch_item_dtor(void* value, void* userdata) 25{ 26 (void)userdata; 27 al_free(value); 28} 29 30/* Search for touch_t associated with UITouch. 31 */ 32static touch_t* find_touch(_AL_LIST* list, UITouch* nativeTouch) 33{ 34 _AL_LIST_ITEM* item; 35 36 for (item = _al_list_front(list); item; item = _al_list_next(list, item)) { 37 38 touch_t* touch = (touch_t*)_al_list_item_data(item); 39 40 if (touch->touch == nativeTouch) 41 return touch; 42 } 43 44 return NULL; 45} 46 47@implementation EAGLView 48 49@synthesize context; 50@synthesize backingWidth; 51@synthesize backingHeight; 52 53 54// You must implement this method 55+ (Class)layerClass { 56 return [CAEAGLLayer class]; 57} 58 59- (void)set_allegro_display:(ALLEGRO_DISPLAY *)display { 60 ALLEGRO_DISPLAY_IPHONE *d = (ALLEGRO_DISPLAY_IPHONE *)display; 61 62 allegro_display = display; 63 64 // Get the layer 65 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; 66 67 NSString *color_format = kEAGLColorFormatRGBA8; 68 if (display->extra_settings.settings[ALLEGRO_COLOR_SIZE] == 16) 69 color_format = kEAGLColorFormatRGB565; 70 71 eaglLayer.opaque = YES; 72 eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: 73 [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, 74 color_format, kEAGLDrawablePropertyColorFormat, nil]; 75 76 if (display->flags & ALLEGRO_PROGRAMMABLE_PIPELINE) { 77 ALLEGRO_INFO("Attempting to create ES2 context\n"); 78 context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 79 if (context == nil) { 80 ALLEGRO_WARN("ES2 context could not be created. Attempting to create ES1 context instead.\n"); 81 display->flags &= ~ ALLEGRO_PROGRAMMABLE_PIPELINE; 82 context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; 83 } 84 } 85 else { 86 ALLEGRO_INFO("Attempting to create ES1 context.\n"); 87 context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; 88 } 89 90 ALLEGRO_INFO("Context is %p\n", context); 91 92 if (!context || ![EAGLContext setCurrentContext:context]) { 93 ALLEGRO_ERROR("context is nil or setCurrentContext failed.\n"); 94 [self release]; 95 return; 96 } 97 98 /* FIXME: Make this depend on a display setting. */ 99 [self setMultipleTouchEnabled:YES]; 100 101 102 ALLEGRO_INFO("Created EAGLView.\n"); 103} 104 105- (id)initWithFrame:(CGRect)frame { 106 107 ALLEGRO_DEBUG("Creating UIView.\n"); 108 109 self = [super initWithFrame:frame]; 110 111 touch_list = _al_list_create(); 112 113 primary_touch = NULL; 114 115 touch_id_set = [[NSMutableIndexSet alloc] init]; 116 next_free_touch_id = 1; 117 118 return self; 119} 120 121- (void)make_current { 122 [EAGLContext setCurrentContext:context]; 123 glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); 124} 125 126- (void)reset_framebuffer { 127 glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); 128} 129 130- (void)flip { 131 glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); 132 [context presentRenderbuffer:GL_RENDERBUFFER_OES]; 133} 134 135- (void)send_resize_event { 136 ALLEGRO_DISPLAY *display = allegro_display; 137 138 int x = self.frame.origin.x; 139 int y = self.frame.origin.y; 140 int w = self.frame.size.width; 141 int h = self.frame.size.height; 142 143 _al_event_source_lock(&display->es); 144 if (_al_event_source_needs_to_generate_event(&display->es)) { 145 ALLEGRO_EVENT event; 146 event.display.type = ALLEGRO_EVENT_DISPLAY_RESIZE; 147 event.display.timestamp = al_get_time(); 148 event.display.x = x; 149 event.display.y = y; 150 event.display.width = w; 151 event.display.height = h; 152 event.display.orientation = _al_iphone_get_orientation(display); 153 _al_event_source_emit_event(&display->es, &event); 154 } 155 _al_event_source_unlock(&display->es); 156} 157 158- (void)layoutSubviews { 159 [EAGLContext setCurrentContext:context]; 160 if (!viewRenderbuffer) { 161 [self createFramebuffer]; 162 /* Depending on the orientation, the initial framebuffer dimensions may be 163 * rotated so we need to update them. For example 164 * a call to al_create_display(480, 640) will create a display of 165 * 640x480 pixels in landscape mode. 166 */ 167 allegro_display->w = backingWidth; 168 allegro_display->h = backingHeight; 169 } 170 [self send_resize_event]; 171} 172 173- (BOOL)orientation_supported:(UIInterfaceOrientation) o { 174 if (!allegro_display) return NO; 175 ALLEGRO_DISPLAY_IPHONE *d = (ALLEGRO_DISPLAY_IPHONE *)allegro_display; 176 if (d->extra->adapter != 0) return NO; 177 ALLEGRO_EXTRA_DISPLAY_SETTINGS *options = &allegro_display->extra_settings; 178 int supported = options->settings[ALLEGRO_SUPPORTED_ORIENTATIONS]; 179 if (o == UIInterfaceOrientationPortrait) return supported & ALLEGRO_DISPLAY_ORIENTATION_0_DEGREES; 180 if (o == UIInterfaceOrientationLandscapeLeft) return supported & ALLEGRO_DISPLAY_ORIENTATION_90_DEGREES; 181 if (o == UIInterfaceOrientationPortraitUpsideDown) return supported & ALLEGRO_DISPLAY_ORIENTATION_180_DEGREES; 182 if (o == UIInterfaceOrientationLandscapeRight) return supported & ALLEGRO_DISPLAY_ORIENTATION_270_DEGREES; 183 return NO; 184} 185 186- (BOOL)createFramebuffer { 187 ALLEGRO_DISPLAY_IPHONE *d = (ALLEGRO_DISPLAY_IPHONE *)allegro_display; 188 189 if (d->extra->adapter == 0 && [self respondsToSelector:@selector(contentScaleFactor)]) { 190 scale = self.contentScaleFactor = [[UIScreen mainScreen] scale]; 191 ALLEGRO_INFO("Screen scale is %f\n", self.contentScaleFactor); 192 } 193 else { 194 scale = 1.0f; 195 } 196 197 glGenFramebuffersOES(1, &viewFramebuffer); 198 glGenRenderbuffersOES(1, &viewRenderbuffer); 199 200 glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); 201 glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); 202 [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; 203 glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); 204 205 glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); 206 glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); 207 208 ALLEGRO_INFO("Creating GL framebuffer %dx%d.\n", backingWidth, backingHeight); 209 210 if (allegro_display->extra_settings.settings[ALLEGRO_DEPTH_SIZE]) { 211 GLint depth_stencil_format; 212 if (allegro_display->extra_settings.settings[ALLEGRO_STENCIL_SIZE]) { 213 depth_stencil_format = GL_DEPTH24_STENCIL8_OES; 214 } 215 else { 216 depth_stencil_format = GL_DEPTH_COMPONENT16_OES; 217 } 218 glGenRenderbuffersOES(1, &depthRenderbuffer); 219 glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); 220 glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depth_stencil_format, backingWidth, backingHeight); 221 glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); 222 if (allegro_display->extra_settings.settings[ALLEGRO_STENCIL_SIZE]) { 223 glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); 224 } 225 } 226 227 if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { 228 NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); 229 return NO; 230 } 231 232 return YES; 233} 234 235 236- (void)destroyFramebuffer { 237 238 glDeleteFramebuffersOES(1, &viewFramebuffer); 239 viewFramebuffer = 0; 240 glDeleteRenderbuffersOES(1, &viewRenderbuffer); 241 viewRenderbuffer = 0; 242 243 if (depthRenderbuffer) { 244 glDeleteRenderbuffersOES(1, &depthRenderbuffer); 245 depthRenderbuffer = 0; 246 } 247} 248 249- (void)dealloc { 250 if (touch_list) 251 _al_list_destroy(touch_list); 252 253 [touch_id_set release]; 254 255 if ([EAGLContext currentContext] == context) { 256 [EAGLContext setCurrentContext:nil]; 257 } 258 259 [context release]; 260 [super dealloc]; 261} 262 263/* Handling of touch events. */ 264 265-(NSArray*)getSortedTouches:(NSSet*)touches 266{ 267 NSArray* unsorted = [NSArray arrayWithArray: [touches allObjects]]; 268 NSArray* sorted = [unsorted sortedArrayUsingComparator: ^(id obj1, id obj2) 269 { 270 if ([obj1 timestamp] > [obj2 timestamp]) 271 return (NSComparisonResult)NSOrderedDescending; 272 else if ([obj1 timestamp] < [obj2 timestamp]) 273 return (NSComparisonResult)NSOrderedAscending; 274 else 275 return (NSComparisonResult)NSOrderedSame; 276 }]; 277 return sorted; 278} 279 280// Handles the start of a touch 281-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 282{ 283 (void)event; 284 285 // TODO: handle double-clicks (send two events?) 286 // NSUInteger numTaps = [[touches anyObject] tapCount]; 287 // Enumerate through all the touch objects. 288 289 for (UITouch *nativeTouch in touches) { 290 /* Create new touch_t and associate ID with UITouch. */ 291 touch_t* touch = al_malloc(sizeof(touch_t)); 292 293 touch->touch = nativeTouch; 294 295 if ([touch_id_set count] != 0) { 296 touch->id = [touch_id_set firstIndex]; 297 [touch_id_set removeIndex:touch->id]; 298 } 299 else 300 touch->id = next_free_touch_id++; 301 302 _al_list_push_back_ex(touch_list, touch, touch_item_dtor); 303 304 CGPoint p = [nativeTouch locationInView:[nativeTouch view]]; 305 306 if (NULL == primary_touch) 307 primary_touch = nativeTouch; 308 309 _al_iphone_touch_input_handle_begin(touch->id, al_get_time(), 310 p.x*scale, p.y*scale, primary_touch == nativeTouch, allegro_display); 311 } 312} 313 314// Handles the continuation of a touch. 315-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 316{ 317 (void)event; 318 319 touch_t* touch; 320 321 // Enumerates through all touch objects 322 for (UITouch *nativeTouch in touches) { 323 if ((touch = find_touch(touch_list, nativeTouch))) { 324 325 CGPoint p = [nativeTouch locationInView:[nativeTouch view]]; 326 327 _al_iphone_touch_input_handle_move(touch->id, al_get_time(), 328 p.x*scale, p.y*scale, primary_touch == nativeTouch, allegro_display); 329 } 330 } 331} 332 333// Handles the end of a touch event. 334-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 335{ 336 (void)event; 337 338 touch_t* touch; 339 340 // Enumerates through all touch objects 341 for (UITouch *nativeTouch in touches) { 342 if ((touch = find_touch(touch_list, nativeTouch))) { 343 344 CGPoint p = [nativeTouch locationInView:[nativeTouch view]]; 345 346 _al_iphone_touch_input_handle_end(touch->id, al_get_time(), 347 p.x*scale, p.y*scale, primary_touch == nativeTouch, allegro_display); 348 349 [touch_id_set addIndex:touch->id]; 350 _al_list_remove(touch_list, touch); 351 352 if (primary_touch == nativeTouch) 353 primary_touch = NULL; 354 } 355 } 356} 357 358// Quoting Apple docs: 359// "The system cancelled tracking for the touch, as when (for example) the user 360// puts the device to his or her face." 361-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 362{ 363 (void)event; 364 365 touch_t* touch; 366 367 // Enumerates through all touch objects 368 for (UITouch *nativeTouch in touches) { 369 if ((touch = find_touch(touch_list, nativeTouch))) { 370 371 CGPoint p = [nativeTouch locationInView:[nativeTouch view]]; 372 373 _al_iphone_touch_input_handle_cancel(touch->id, al_get_time(), 374 p.x*scale, p.y*scale, primary_touch == nativeTouch, allegro_display); 375 376 if (primary_touch == nativeTouch) 377 primary_touch = NULL; 378 379 [touch_id_set addIndex:touch->id]; 380 _al_list_remove(touch_list, touch); 381 } 382 } 383} 384 385-(BOOL)canBecomeFirstResponder { 386 return YES; 387} 388 389-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event 390{ 391 (void)motion; 392} 393 394@end 395 396