diff options
Diffstat (limited to 'numlib/ui.c')
-rw-r--r-- | numlib/ui.c | 205 |
1 files changed, 184 insertions, 21 deletions
diff --git a/numlib/ui.c b/numlib/ui.c index a3cdbe8..390a332 100644 --- a/numlib/ui.c +++ b/numlib/ui.c @@ -32,6 +32,8 @@ main thread, spawn a secondary thread to run the application main(), and then do nothing but service the events in the main thread. + We can also pass functions back to be run in the main thread, + and wait for events in the main thread to be processed. Note though that Cocoa has poor thread safety :- ie. NSRunLoop can't be used to access events - use CFRunLoop @@ -45,6 +47,7 @@ # include <stdio.h> # include <stdlib.h> # include <pthread.h> +# include "ui.h" # include <Foundation/Foundation.h> # include <AppKit/AppKit.h> @@ -66,6 +69,10 @@ static char **g_argv; pthread_t ui_thid = 0; /* Thread ID of main thread running io run loop */ pthread_t ui_main_thid = 0; /* Thread ID of thread running application main() */ +static pthread_mutex_t ui_lock1, ui_lock2; /* Protect wait code */ +static pthread_cond_t ui_cond1, ui_cond2; /* Signal to waiting thread */ +static int ui_event1 = 0, ui_event2 = 0; /* Sync event was received */ + extern int uimain(int argc, char *argv[]); /* Thread that calls the real application main() */ @@ -79,12 +86,21 @@ static void *callMain(void *p) { /* Turn App Nap off */ osx_userinitiated_start(); + /* Should we catch and report exceptions ? */ + rv = uimain(g_argc, g_argv); + /* Restore App Nap state */ osx_userinitiated_end(); [tpool release]; + /* Cleanup, since main thread won't return */ + pthread_cond_destroy(&ui_cond1); + pthread_cond_destroy(&ui_cond2); + pthread_mutex_destroy(&ui_lock1); + pthread_mutex_destroy(&ui_lock2); + exit(rv); } @@ -101,37 +117,24 @@ static void *callMain(void *p) { int main(int argc, char ** argv) { + pthread_mutex_init(&ui_lock1, NULL); + pthread_mutex_init(&ui_lock2, NULL); + pthread_cond_init(&ui_cond1, NULL); + pthread_cond_init(&ui_cond2, NULL); + ui_thid = pthread_self(); /* Create an NSApp */ static NSAutoreleasePool *pool = nil; - ProcessSerialNumber psn = { 0, 0 }; - - /* Transform the process so that the desktop interacts with it properly. */ - /* We don't need resources or a bundle if we do this. */ - if (GetCurrentProcess(&psn) == noErr) { - OSStatus stat; - if (psn.lowLongOfPSN != 0 && (stat = TransformProcessType(&psn, - kProcessTransformToForegroundApplication)) != noErr) { -// fprintf(stderr,"TransformProcess failed with code %d\n",stat); - } else { -// fprintf(stderr,"TransformProcess suceeded\n"); - } -// if ((stat = SetFrontProcess(&psn)) != noErr) { -// fprintf(stderr,"SetFrontProcess returned error %d\n",stat); -// } - } pool = [NSAutoreleasePool new]; [NSApplication sharedApplication]; /* Creates NSApp */ - [NSApp finishLaunching]; - /* We seem to need this, because otherwise we don't get focus automatically */ - [NSApp activateIgnoringOtherApps: YES]; + [NSApp finishLaunching]; /* We need to create at least one NSThread to tell Cocoa that we are using */ - /* threads, and to protect Cococa objects. */ + /* threads, and to protect Cococa objects. (We don't actually have to start the thread.) */ [NSThread detachNewThreadSelector:@selector(dummyfunc:) toTarget:[MainClass class] withObject:nil]; /* Call the real main() in another thread */ @@ -156,13 +159,163 @@ int main(int argc, char ** argv) { } /* Service the run queue */ - [NSApp run]; + { + NSEvent *event; + NSDate *to; + + /* Process events, looking for application events. */ + for (;;) { + /* Hmm. Assume to is autorelease */ + to = [NSDate dateWithTimeIntervalSinceNow:1.0]; + /* Hmm. Assume event is autorelease */ + if ((event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:to inMode:NSDefaultRunLoopMode dequeue:YES]) != nil) { + + /* call function message */ + if ([event type] == NSApplicationDefined + && [event subtype] == 1) { + void *cntx = (void *)[event data1]; + void (*function)(void *cntx) = (void (*)(void *)) [event data2]; + + function(cntx); + + pthread_mutex_lock(&ui_lock1); + ui_event1 = 1; + pthread_cond_signal(&ui_cond1); + pthread_mutex_unlock(&ui_lock1); + [event release]; + + /* event flush message */ + } else if ([event type] == NSApplicationDefined + && [event subtype] == 2) { + pthread_mutex_lock(&ui_lock2); + ui_event2 = 1; + pthread_cond_signal(&ui_cond2); + pthread_mutex_unlock(&ui_lock2); + [event release]; + + /* Everything else */ + } else { + [NSApp sendEvent:event]; + } + } + } + } /* Note that we don't actually clean this up on exit - */ /* possibly we can't. */ // [NSApp terminate: nil]; } +/* Call this if we decide we are actually going to display something in the GUI. */ +/* We switch to "interact with the Dock" mode. */ +void ui_UsingGUI() { + static int attached = 0; + ProcessSerialNumber psn = { 0, 0 }; + + if (!attached) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + /* Make the application appear in the Dock, and interact with the desktop properly. */ + /* (Unbundled applications default to NSApplicationActivationPolicyProhibited) */ + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + +#else +# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 + /* Make the application appear in the Dock, and interact with the desktop properly. */ + /* We don't need resources or a bundle if we do this. */ + if (GetCurrentProcess(&psn) == noErr) { + OSStatus stat; + if (psn.lowLongOfPSN != 0 && (stat = TransformProcessType(&psn, + kProcessTransformToForegroundApplication)) != noErr) { + fprintf(stderr,"TransformProcess failed with code %d\n",stat); + + /* An older trick uses an undocumented API: + CPSEnableForegroundOperation(&processSerialNum, 0, 0, 0, 0); + */ + } else { + // fprintf(stderr,"TransformProcess suceeded\n"); + } + } +# endif /* OS X 10.3 */ +#endif /* !OS X 10.6 */ + + /* We seem to need this, because otherwise we don't get focus automatically */ + [NSApp activateIgnoringOtherApps: YES]; + + attached = 1; + } +} + +/* Run a function in the main thread and return when it is complete. */ +/* (It's up to the function to record it's result status in its context) */ +void ui_runInMainThreadAndWait(void *cntx, void (*function)(void *cntx)) { + + NSEvent *event; + NSPoint point = { 0.0, 0.0 }; + int rv; + + pthread_mutex_lock(&ui_lock1); + ui_event1 = 0; + + event = [NSEvent otherEventWithType:NSApplicationDefined + location:point + modifierFlags:0 + timestamp:0.0 + windowNumber:0 + context:nil + subtype:1 + data1:(long)cntx /* long same size as * */ + data2:(long)function]; + [NSApp postEvent:event atStart:NO]; + + // unlock and wait for signal + for (;;) { /* Ignore spurious wakeups */ + if ((rv = pthread_cond_wait(&ui_cond1, &ui_lock1)) != 0) { + break; // Hmm. + } + if (ui_event1) /* Got what we were waiting for */ + break; + } + pthread_mutex_unlock(&ui_lock1); +} + + +/* We are about to change the UI */ +void ui_aboutToWait() { + + pthread_mutex_lock(&ui_lock2); + ui_event2 = 0; +} + +/* Wait until we are sure our UI change is complete, */ +/* because our event has trickled through. */ +void ui_waitForEvents() { + NSEvent *event; + NSPoint point = { 0.0, 0.0 }; + int rv; + + event = [NSEvent otherEventWithType:NSApplicationDefined + location:point + modifierFlags:0 + timestamp:0.0 + windowNumber:0 + context:nil + subtype:2 + data1:0 + data2:0]; + [NSApp postEvent:event atStart:NO]; + + // unlock and wait for signal + for (;;) { /* Ignore spurious wakeups */ + if ((rv = pthread_cond_wait(&ui_cond2, &ui_lock2)) != 0) { + break; // Hmm. + } + if (ui_event2) /* Got what we were waiting for */ + break; + } + pthread_mutex_unlock(&ui_lock2); +} + #else /* !APPLE */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -171,6 +324,11 @@ int main(int argc, char ** argv) { /* This is a mechanism to force libui to link */ int ui_initialized = 1; /* Nothing needs initializing */ +/* Call this if we decide we are actually going to display */ +/* something in the GUI */ +void ui_UsingGUI() { +} + #endif /* !APPLE */ #endif /* UNIX */ @@ -262,4 +420,9 @@ int ui_initialized = 1; /* Nothing needs initializing */ #endif /* !NEVER */ +/* Call this if we decide we are actually going to display */ +/* something in the GUI */ +void ui_UsingGUI() { +} + #endif /* NT */ |