Custom event loop and UIKit controls. What extra magic Apple's event loop does?
- by tequilatango
Does anyone know or have good links that explain what iPhone's event loop does under the hood?
We are using a custom event loop in our OpenGL-based iPhone game framework. It calls our game rendering system, calls presentRenderbuffer and pumps events using CFRunLoopRunInMode. See the code below for details.
It works well when we are not using UIKit controls (as a proof, try Facetap, our first released game).
However, when using UIKit controls, everything almost works, but not quite. Specifically, scrolling of UIKit controls doesn't work properly.
For example, let's consider following scenario.
We show UIImagePickerController on top of our own view.
UIImagePickerController covers our custom view
We also pause our own rendering, but keep on using the custom event loop.
As said, everything works, except scrolling.
Picking photos works.
Drilling down to photo albums works and transition animations are smooth.
When trying to scroll photo album view, the view follows your finger.
Problem: when scrolling, scrolling stops immediately after you lift your finger. Normally, it continues smoothly based on the speed of your movement, but not when we are using the custom event loop. It seems that iPhone's event loop is doing some magic related to UIKit scrolling that we haven't implemented ourselves.
Now, we can get UIKit controls to work just fine and dandy together with our own system by using Apple's event loop and calling our own rendering via NSTimer callbacks. However, I'd still like to understand, what is possibly happening inside iPhone's event loop that is not implemented in our custom event loop.
- (void)customEventLoop { OBJC_METHOD;
float excess = 0.0f;
while(isRunning) {
animationInterval = 1.0f / openGLapp->ticks_per_second();
// Calculate the target time to be used in this run of loop
float wait = max(0.0, animationInterval - excess);
Systemtime target = Systemtime::now().after_seconds(wait);
Scope("event loop");
NSAutoreleasePool* pool = [[ NSAutoreleasePool alloc] init];
// Call our own render system and present render buffer
[self drawView];
// Pump system events
[self handleSystemEvents:target];
[pool release];
excess = target.seconds_to_now();
}
}
- (void)drawView { OBJC_METHOD;
// call our own custom rendering
bool bind = openGLapp->app_render();
// bind the buffer to be THE renderbuffer and present its contents
if (bind) {
opengl::bind_renderbuffer(renderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
}
- (void) handleSystemEvents:(Systemtime)target { OBJC_METHOD;
SInt32 reason = 0;
double time_left = target.seconds_since_now();
if (time_left <= 0.0) {
while((reason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE)) == kCFRunLoopRunHandledSource) {}
} else {
float dt = time_left;
while((reason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, dt, FALSE)) == kCFRunLoopRunHandledSource) {
double time_left = target.seconds_since_now();
if (time_left <= 0.0) break;
dt = (float) time_left;
}
}
}