summaryrefslogtreecommitdiff
path: root/numlib/ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'numlib/ui.c')
-rw-r--r--numlib/ui.c205
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 */