[PATCH] Iterate the main loop in a separate thread while in a modal operation gdkeventloop-quartz.c: * Define the interaction between the "select thread" and the main thread as an explict state machine. * Use a CFRunLoopObserver to detect when a modal operation is running; while the modal operation is running, use the select thread to do the prepare/poll/check stages of the main loop, then once we have something wake the main thread back up to dispatch. * Use GLib thread primitives instead of pthread since we are counting on GLib threading. GdkQuartzWindow.c: Call g_main_context_wakeup() after adding an event to the event queue on resize.



Background: http://mail.gnome.org/archives/gtk-devel-list/2008-September/msg00008.html
---
 gdk/quartz/GdkQuartzWindow.c     |    3 +
 gdk/quartz/gdkeventloop-quartz.c |  608 ++++++++++++++++++++++++++++++--------
 2 files changed, 495 insertions(+), 116 deletions(-)

diff --git a/gdk/quartz/GdkQuartzWindow.c b/gdk/quartz/GdkQuartzWindow.c
index 3f16815..49c9e39 100644
--- a/gdk/quartz/GdkQuartzWindow.c
+++ b/gdk/quartz/GdkQuartzWindow.c
@@ -180,6 +180,7 @@
 
   [[self contentView] setFrame:NSMakeRect (0, 0, impl->width, impl->height)];
 
+  GDK_THREADS_ENTER();
   /* Synthesize a configure event */
   event = gdk_event_new (GDK_CONFIGURE);
   event->configure.window = g_object_ref (window);
@@ -189,6 +190,8 @@
   event->configure.height = impl->height;
 
   _gdk_event_queue_append (gdk_display_get_default (), event);
+  g_main_context_wakeup (NULL);
+  GDK_THREADS_LEAVE();
 }
 
 -(id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
diff --git a/gdk/quartz/gdkeventloop-quartz.c b/gdk/quartz/gdkeventloop-quartz.c
index 50b2c7e..96e8535 100644
--- a/gdk/quartz/gdkeventloop-quartz.c
+++ b/gdk/quartz/gdkeventloop-quartz.c
@@ -8,26 +8,81 @@
 
 #include "gdkprivate-quartz.h"
 
+typedef enum {
+  BEFORE_START,
+  
+  /**** Select thread is waiting for main thread ****/
+  WAITING,
+  WAITING_FOR_DISPATCH,
+  
+  /**** Main thread is waiting for select thread ****/
+  POLLING_DESCRIPTORS,
+  POLLING_INTERRUPTED,
+  ITERATING_MAIN_LOOP,
+  ITERATING_INTERRUPTED
+} SelectThreadState;
+
+static const char *const state_names[]  = {
+  "BEFORE_START",
+  "WAITING",
+  "WAITING_FOR_DISPATCH",
+  "POLLING_DESCRIPTORS",
+  "POLLING_INTERRUPTED",
+  "ITERATING_MAIN_LOOP",
+  "ITERATING_INTERRUPTED"
+};
+
+static SelectThreadState select_thread_state = BEFORE_START;
+static GMutex *select_thread_mutex;
+static GCond *select_thread_cond;
+static int modal_operation_count;
+
+#define LOCK() g_mutex_lock (select_thread_mutex)
+#define UNLOCK() g_mutex_unlock (select_thread_mutex)
+#define SIGNAL() g_cond_signal (select_thread_cond)
+#define WAIT() g_cond_wait (select_thread_cond, select_thread_mutex)
+
 static GPollFD event_poll_fd;
 static GQueue *current_events;
 
 static GPollFunc old_poll_func;
 
-static gboolean select_fd_waiting = FALSE, ready_for_poll = FALSE;
-static pthread_t select_thread = 0;
 static int wakeup_pipe[2];
-static pthread_mutex_t pollfd_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
+
+/* This is the poll array that is used to communicate between the main thread
+ * and the select thread when the select thread is polling file descriptors.
+ *
+ * If select_thread_state == POLLING_DESCRIPTORS or POLLING_INTERRUPTED, then
+ * the select thread is allowed to read and modify these. Otherwise, only
+ * the main loop is allowed to access them.
+ */
 static GPollFD *pollfds;
 static guint n_pollfds;
+
+/* This is the poll array used internal to the select thread when the
+ * select thread is iterating the main loop.
+ */
+#define CACHED_POLLFDS_INITIAL_SIZE 16
+static GPollFD *cached_pollfds;
+static guint n_cached_pollfds;
+
 static CFRunLoopSourceRef select_main_thread_source;
+static GThread *main_thread;
 static CFRunLoopRef main_thread_run_loop;
 static NSAutoreleasePool *autorelease_pool;
 
+gboolean getting_events;
+
 gboolean
 _gdk_quartz_event_loop_check_pending (void)
 {
-  return current_events && current_events->head;
+  gboolean retval;
+
+  LOCK ();
+  retval = current_events && current_events->head;
+  UNLOCK ();
+
+  return retval;
 }
 
 NSEvent*
@@ -35,9 +90,13 @@ _gdk_quartz_event_loop_get_pending (void)
 {
   NSEvent *event = NULL;
 
+  LOCK ();
+  
   if (current_events)
     event = g_queue_pop_tail (current_events);
 
+  UNLOCK ();
+
   return event;
 }
 
@@ -55,17 +114,24 @@ gdk_event_prepare (GSource *source,
   gboolean retval;
 
   GDK_THREADS_ENTER ();
-  
-  *timeout = -1;
 
-  event = [NSApp nextEventMatchingMask: NSAnyEventMask
-	                     untilDate: [NSDate distantPast]
-	                        inMode: NSDefaultRunLoopMode
-	                       dequeue: NO];
+  *timeout = -1;
 
-  retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
-	    (current_events && current_events->head) ||
-            event != NULL);
+  if (g_thread_self () == main_thread)
+    {
+      getting_events = TRUE;
+      event = [NSApp nextEventMatchingMask: NSAnyEventMask
+				 untilDate: [NSDate distantPast]
+				    inMode: NSDefaultRunLoopMode
+				   dequeue: NO];
+      getting_events = FALSE;
+    }
+  else
+    event = NULL;
+  
+  retval = (event != NULL ||
+	    _gdk_quartz_event_loop_check_pending () ||
+	    _gdk_event_queue_non_empty (_gdk_display));
 
   GDK_THREADS_LEAVE ();
 
@@ -83,7 +149,7 @@ gdk_event_check (GSource *source)
     [autorelease_pool release];
   autorelease_pool = [[NSAutoreleasePool alloc] init];
 
-  if (_gdk_event_queue_find_first (_gdk_display) != NULL ||
+  if (_gdk_event_queue_non_empty (_gdk_display) ||
       _gdk_quartz_event_loop_check_pending ())
     retval = TRUE;
   else
@@ -127,97 +193,286 @@ static GSourceFuncs event_funcs = {
   NULL
 };
 
+static void
+select_thread_set_state(SelectThreadState new_state)
+{
+  gboolean old_waits_for_thread = !(select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH);
+  gboolean new_waits_for_thread = !(new_state == WAITING || new_state == WAITING_FOR_DISPATCH);
+
+  GDK_NOTE(MISC, g_print("ST: %s => %s\n", state_names[select_thread_state], state_names[new_state]));
+
+  g_assert (!(!old_waits_for_thread && new_waits_for_thread));
+
+  select_thread_state = new_state;
+  if (old_waits_for_thread && !new_waits_for_thread)
+    SIGNAL ();
+}
+
+static void
+select_thread_wait(void)
+{
+  g_assert (select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH);
+  WAIT ();
+}
+
+static void
+main_thread_set_state(SelectThreadState new_state)
+{
+  gboolean old_waits_for_thread = !(select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH);
+  gboolean new_waits_for_thread = !(new_state == WAITING || new_state == WAITING_FOR_DISPATCH);
+
+  GDK_NOTE(MISC, g_print("MT: %s => %s\n", state_names[select_thread_state], state_names[new_state]));
+
+  g_assert (!(old_waits_for_thread && !new_waits_for_thread));
+  
+  select_thread_state = new_state;
+  if (!old_waits_for_thread && new_waits_for_thread)
+    SIGNAL ();
+}
+
+static void
+main_thread_wait(void)
+{
+  g_assert (!(select_thread_state == WAITING || select_thread_state == WAITING_FOR_DISPATCH));
+  g_assert (!(select_thread_state == POLLING_DESCRIPTORS || select_thread_state == ITERATING_MAIN_LOOP));
+  WAIT ();
+}
+
 static void 
 got_fd_activity (void *info)
 {
   NSEvent *event;
 
-  /* Post a message so we'll break out of the message loop */
-  event = [NSEvent otherEventWithType: NSApplicationDefined
-	                     location: NSZeroPoint
-	                modifierFlags: 0
-	                    timestamp: 0
-	                 windowNumber: 0
-	                      context: nil
-                              subtype: GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP
-	                        data1: 0 
-	                        data2: 0];
-
-  [NSApp postEvent:event atStart:YES];
+  LOCK ();
+
+  if (select_thread_state == WAITING_FOR_DISPATCH)
+    {
+      UNLOCK ();
+      GDK_NOTE(MISC, g_print("Dispatching from main thread\n"));
+      g_main_context_dispatch (g_main_context_default ());
+      LOCK ();
+      main_thread_set_state (ITERATING_MAIN_LOOP);
+    }
+  else if (select_thread_state != ITERATING_MAIN_LOOP)
+    {
+      /* Post a message so we'll break out of the message loop */
+      event = [NSEvent otherEventWithType: NSApplicationDefined
+				 location: NSZeroPoint
+			    modifierFlags: 0
+				timestamp: 0
+			     windowNumber: 0
+				  context: nil
+				  subtype: GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP
+				    data1: 0 
+				    data2: 0];
+
+      [NSApp postEvent:event atStart:YES];
+    }
+  
+  UNLOCK ();
 }
 
-static void *
-select_thread_func (void *arg)
+static void
+signal_main_thread(void)
 {
-  int n_active_fds;
+  GDK_NOTE(MISC, g_print("Signalling\n"));
+  CFRunLoopSourceSignal (select_main_thread_source);
+  if (CFRunLoopIsWaiting (main_thread_run_loop))
+    CFRunLoopWakeUp (main_thread_run_loop);
+}
 
-  pthread_mutex_lock (&pollfd_mutex);
+static void
+select_thread_poll_descriptors()
+{
+  char c;
+  
+  UNLOCK ();
+  old_poll_func (pollfds, n_pollfds, -1);
+  read (wakeup_pipe[0], &c, 1);
+  LOCK ();
+}
 
-  while (1)
+static void
+select_thread_iterate_main_loop()
+{
+  gint max_priority;
+  gint timeout;
+  gint nfds, allocated_nfds;
+  GPollFD *fds = NULL;
+  GMainContext *context = g_main_context_default ();
+  gboolean interrupted;
+  
+  if (!cached_pollfds)
     {
-      char c;
-      int n;
-
-      ready_for_poll = TRUE;
-      pthread_cond_signal (&ready_cond);
-      pthread_cond_wait (&ready_cond, &pollfd_mutex);
-      ready_for_poll = FALSE;
-
-      select_fd_waiting = TRUE;
-      pthread_cond_signal (&ready_cond);
-      pthread_mutex_unlock (&pollfd_mutex);
-      n_active_fds = old_poll_func (pollfds, n_pollfds, -1);
-      pthread_mutex_lock (&pollfd_mutex);
-      select_fd_waiting = FALSE;
-      n = read (wakeup_pipe[0], &c, 1);
-      if (n == 1)
-        {
-	  g_assert (c == 'A');
+      n_cached_pollfds = CACHED_POLLFDS_INITIAL_SIZE;
+      cached_pollfds = g_new (GPollFD, n_cached_pollfds);
+    }
 
-	  n_active_fds --;
-	}
-      pthread_mutex_unlock (&pollfd_mutex);
+  allocated_nfds = n_cached_pollfds;
+  fds = cached_pollfds;
+  
+  UNLOCK ();
 
-      if (n_active_fds)
+  g_main_context_prepare (context, &max_priority); 
+  
+  while ((nfds = g_main_context_query (context, max_priority, &timeout, fds, 
+				       allocated_nfds)) > allocated_nfds)
+    {
+      g_free (fds);
+      n_cached_pollfds = allocated_nfds = nfds;
+      cached_pollfds = fds = g_new (GPollFD, nfds);
+    }
+
+  /* Checking that we weren't interrupted is just an optimzation .. the
+   * other thread has to call g_main_context_wakeup() unconditionally
+   * since there otherwise is an unavoidable race: we can't atomically
+   * drop the lock and start polling.
+   */
+  LOCK ();
+  interrupted = (select_thread_state == ITERATING_INTERRUPTED);
+  UNLOCK ();
+
+  if (!interrupted && (nfds || timeout != 0))
+    old_poll_func (fds, nfds, interrupted ? timeout : 0);
+  
+  g_main_context_check (context, max_priority, fds, nfds);
+
+  LOCK ();
+}
+
+static void *
+select_thread_func (void *arg)
+{
+  LOCK ();
+
+  while (TRUE)
+    {
+      GDK_NOTE(MISC, g_print("ST: %s\n", state_names[select_thread_state]));
+
+      switch (select_thread_state)
 	{
-	  /* We have active fds, signal the main thread */
-	  CFRunLoopSourceSignal (select_main_thread_source);
-	  if (CFRunLoopIsWaiting (main_thread_run_loop))
-	    CFRunLoopWakeUp (main_thread_run_loop);
+	case BEFORE_START:
+	  g_assert_not_reached();
+	  
+	case WAITING:
+	case WAITING_FOR_DISPATCH:
+	  select_thread_wait ();
+	  break;
+	  
+	case POLLING_DESCRIPTORS:
+	  select_thread_poll_descriptors();
+	  if (select_thread_state != POLLING_INTERRUPTED)
+	    signal_main_thread();
+	  select_thread_set_state (WAITING);
+	  break;
+	case POLLING_INTERRUPTED:
+	  /* Interrupted immediately */
+	  select_thread_set_state (WAITING);
+	  break;
+	case ITERATING_MAIN_LOOP:
+	  select_thread_iterate_main_loop();
+	  if (select_thread_state != ITERATING_INTERRUPTED)
+	      signal_main_thread();
+	  select_thread_set_state (WAITING_FOR_DISPATCH);
+	  break;
+	case ITERATING_INTERRUPTED:
+	  /* Interrupted immediately */
+	  select_thread_set_state (WAITING);
+	  break;
 	}
+    }
+}
+
+static void
+select_thread_start(void)
+{
+  g_return_if_fail (select_thread_state == BEFORE_START);
+  
+  pipe (wakeup_pipe);
+  fcntl(wakeup_pipe[0], F_SETFL, O_NONBLOCK);
+
+  CFRunLoopSourceContext source_context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, got_fd_activity };
+  select_main_thread_source = CFRunLoopSourceCreate (NULL, 0, &source_context);
+  
+  CFRunLoopAddSource (main_thread_run_loop, select_main_thread_source, kCFRunLoopCommonModes);
+
+  select_thread_state = WAITING;
+  select_thread_cond = g_cond_new ();
+  
+  while (TRUE)
+    {
+      if (g_thread_create (select_thread_func, NULL, FALSE, NULL))
+	  break;
 
-      pthread_mutex_lock (&pollfd_mutex);
+      g_warning ("Failed to create select thread, sleeping and trying again");
+      sleep (1);
     }
 }
 
+static void
+interrupt_polling (void)
+{
+  g_return_if_fail (select_thread_state == POLLING_DESCRIPTORS);
+  
+  if (wakeup_pipe[1])
+    {
+      char c = 'A';
+      
+      write (wakeup_pipe[1], &c, 1);
+    }
+  
+  main_thread_set_state (POLLING_INTERRUPTED);
+}
+
+static void
+interrupt_iteration (void)
+{
+  g_return_if_fail (select_thread_state == ITERATING_MAIN_LOOP);
+
+  g_main_context_wakeup (NULL);
+  main_thread_set_state (ITERATING_INTERRUPTED);
+}
+
 static gint
 poll_func (GPollFD *ufds, guint nfds, gint timeout_)
 {
   NSEvent *event;
   NSDate *limit_date;
-  gboolean poll_event_fd = FALSE;
-  int n_active = 0;
+  int poll_event_fd = FALSE;
+  gboolean return_to_iterating = FALSE;
   int i;
 
   if (nfds > 1 || ufds[0].fd != -1)
     {
-      if (!select_thread) {
-        /* Create source used for signalling the main thread */
-        main_thread_run_loop = CFRunLoopGetCurrent ();
-        CFRunLoopSourceContext source_context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, got_fd_activity };
-        select_main_thread_source = CFRunLoopSourceCreate (NULL, 0, &source_context);
-        CFRunLoopAddSource (main_thread_run_loop, select_main_thread_source, kCFRunLoopDefaultMode);
+      if (select_thread_state == BEFORE_START)
+	select_thread_start();
 
-        pipe (wakeup_pipe);
-        fcntl(wakeup_pipe[0], F_SETFL, O_NONBLOCK);
+      LOCK ();
 
-        pthread_mutex_lock (&pollfd_mutex);
-        pthread_create (&select_thread, NULL, select_thread_func, NULL);
-      } else
-        pthread_mutex_lock (&pollfd_mutex);
+      if (select_thread_state == ITERATING_MAIN_LOOP)
+	{
+	  /* While we were in a blocking modal operation, someone recursed
+	   * the main loop. gmain.c probably already spewed a warning
+	   * because of unexpected reentrancy. We can't do much here, but
+	   * wake the select thread up so we don't hang.
+	   */
+	  g_warning("Poll function called while were were iterating from a thread\n");
+	  interrupt_iteration();
+	}
 
-      while (!ready_for_poll)
-        pthread_cond_wait (&ready_cond, &pollfd_mutex);
+      GDK_NOTE(MISC, g_print("Asking thread to poll on our behalf\n"));
+      
+      while (select_thread_state != WAITING && select_thread_state != WAITING_FOR_DISPATCH)
+	main_thread_wait ();
+
+      if (select_thread_state == WAITING_FOR_DISPATCH)
+	{
+	  /* If we were in a blocking modal operation, then we need to return
+	   * continue with that when we are done
+	   */
+	  GDK_NOTE(MISC, g_print("Will return to thread iteration when done\n"));
+	  return_to_iterating = TRUE;
+	}
 
       /* We cheat and use the fake fd (if it's polled) for our pipe */
 
@@ -245,10 +500,8 @@ poll_func (GPollFD *ufds, guint nfds, gint timeout_)
       pollfds[i].fd = wakeup_pipe[0];
       pollfds[i].events = G_IO_IN;
 
-      /* Start our thread */
-      pthread_cond_signal (&ready_cond);
-      pthread_cond_wait (&ready_cond, &pollfd_mutex);
-      pthread_mutex_unlock (&pollfd_mutex);
+      main_thread_set_state (POLLING_DESCRIPTORS);
+      UNLOCK ();
     }
 
   if (timeout_ == -1)
@@ -258,18 +511,19 @@ poll_func (GPollFD *ufds, guint nfds, gint timeout_)
   else
     limit_date = [NSDate dateWithTimeIntervalSinceNow:timeout_/1000.0];
 
+  getting_events = TRUE;
   event = [NSApp nextEventMatchingMask: NSAnyEventMask
 	                     untilDate: limit_date
 	                        inMode: NSDefaultRunLoopMode
                                dequeue: YES];
-  
-  if (event)
+  getting_events = FALSE;
+
+  if (nfds > 0)
     {
-      if ([event type] == NSApplicationDefined &&
-          [event subtype] == GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP)
-        {
-          pthread_mutex_lock (&pollfd_mutex);
+      LOCK ();
 
+      if (select_thread_state == WAITING) /* The poll completed */
+	{
           for (i = 0; i < nfds; i++)
             {
               if (ufds[i].fd == -1)
@@ -281,58 +535,166 @@ poll_func (GPollFD *ufds, guint nfds, gint timeout_)
               if (pollfds[i].revents)
                 {
                   ufds[i].revents = pollfds[i].revents;
-                  n_active ++;
                 }
             }
+	}
+      else
+	{
+	  interrupt_polling ();
+	}
 
-          pthread_mutex_unlock (&pollfd_mutex);
 
-          /* Try to get a Cocoa event too, if requested */
-          if (poll_event_fd)
-            event = [NSApp nextEventMatchingMask: NSAnyEventMask
-                                       untilDate: [NSDate distantPast]
-                                          inMode: NSDefaultRunLoopMode
-                                         dequeue: YES];
-          else
-            event = NULL;
-        }
-    }
+      if (return_to_iterating)
+	{
+	  while (select_thread_state != WAITING)
+	    main_thread_wait ();
 
-  /* There were no active fds, break out of the other thread's poll() */
-  pthread_mutex_lock (&pollfd_mutex);
-  if (select_fd_waiting && wakeup_pipe[1])
+	  main_thread_set_state (ITERATING_MAIN_LOOP);
+	}
+      
+      UNLOCK ();
+    }
+      
+  if (event &&
+      [event type] == NSApplicationDefined &&
+      [event subtype] == GDK_QUARTZ_EVENT_SUBTYPE_EVENTLOOP)
     {
-      char c = 'A';
-
-      write (wakeup_pipe[1], &c, 1);
+      /* Just used to wake us up; if an event and a FD arrived at the same
+       * time, could have come from a previous iteration, in which case
+       * we'll wake up spuriously, which is a little inefficient, but harmless.
+       */
+      event = NULL;
     }
-  pthread_mutex_unlock (&pollfd_mutex);
 
   if (event) 
     {
-      for (i = 0; i < nfds; i++)
-        {
-          if (ufds[i].fd == -1)
-            {
-              ufds[i].revents = G_IO_IN;
-              break;
-            }
-        }
-
+      LOCK ();
+      
       if (!current_events)
         current_events = g_queue_new ();
       g_queue_push_head (current_events, [event retain]);
 
-      n_active ++;
+      UNLOCK ();
+    }
+
+  /* We count on the fact that GMain only cares about >= 0 (no error) vs
+   * -1 (error), and doesn't do anything about the -1 case except warn.
+   */ 
+  return 0;
+}
+
+static void
+_gdk_quartz_begin_iterating_in_thread (void)
+{
+  modal_operation_count++;
+  if (modal_operation_count == 1)
+    {
+      GDK_NOTE (MISC, g_print("Starting processing GLib main loop from a thread\n"));
+      
+      if (select_thread_state == BEFORE_START)
+	select_thread_start();
+
+      if (!g_main_context_acquire (NULL))
+	{
+	  /* Ugh, someone is iterating the main thread from another thread. (This is
+	   * pathological unless gdk_threads_init() was called.) We'll just wait until
+	   * they are done and hope that it doesn't take long.
+	   */
+	  LOCK ();
+	  while (!g_main_context_wait (NULL, select_thread_cond, select_thread_mutex))
+	    ;
+	}
+      else
+	LOCK ();
+
+      if (select_thread_state == POLLING_DESCRIPTORS)
+	interrupt_polling();
+
+      while (select_thread_state != WAITING && select_thread_state != WAITING_FOR_DISPATCH)
+	main_thread_wait ();
+
+      main_thread_set_state (ITERATING_MAIN_LOOP);
+      
+      UNLOCK ();
+    }
+}
+
+static void
+_gdk_quartz_end_iterating_in_thread (void)
+{
+  g_return_if_fail(modal_operation_count > 0);
+  
+  modal_operation_count--;
+  if (modal_operation_count == 0)
+    {
+      GDK_NOTE (MISC, g_print("Stopping processing GLib main loop from a thread\n"));
+      
+      LOCK();
+
+      if (select_thread_state == ITERATING_MAIN_LOOP)
+	{
+	  GDK_NOTE(MISC, g_print("Context is iterating, wake it up!\n"));
+	  interrupt_iteration();
+	}
+
+      while (select_thread_state != WAITING && select_thread_state != WAITING_FOR_DISPATCH)
+	main_thread_wait ();
+
+      if (select_thread_state == WAITING_FOR_DISPATCH)
+	{
+	  UNLOCK ();
+	  g_main_context_dispatch (g_main_context_default ());
+	  LOCK ();
+	  main_thread_set_state (WAITING);
+	}
+            
+      UNLOCK();
+
+      g_main_context_release (NULL);
     }
+}
 
-  return n_active;
+void
+run_loop_observer_callback (CFRunLoopObserverRef observer,
+			    CFRunLoopActivity    activity,
+			    void                *info)
+{
+  if (getting_events) /* Activity we triggered */
+    return;
+  
+  switch (activity)
+    {
+    case kCFRunLoopEntry:
+      _gdk_quartz_begin_iterating_in_thread();
+      GDK_NOTE(MISC, g_print ("Loop Entry: %d\n", modal_operation_count));
+      break;
+    case kCFRunLoopBeforeTimers:
+      GDK_NOTE(MISC, g_print ("Before Timers\n"));
+      break;
+    case kCFRunLoopBeforeSources:
+      GDK_NOTE(MISC, g_print ("Before Sources\n"));
+      break;
+    case kCFRunLoopBeforeWaiting:
+      GDK_NOTE(MISC, g_print ("Before Waiting\n"));
+      break;
+    case kCFRunLoopAfterWaiting:
+      GDK_NOTE(MISC, g_print ("After Waiting\n"));
+      break;
+    case kCFRunLoopExit:
+      GDK_NOTE(MISC, g_print ("Loop Exit: %d\n", modal_operation_count));
+      _gdk_quartz_end_iterating_in_thread();
+      break;
+    default:
+      GDK_NOTE(MISC, g_print ("Unexpected activity: %d\n", activity));
+      break;
+    }
 }
 
 void
 _gdk_quartz_event_loop_init (void)
 {
   GSource *source;
+  CFRunLoopObserverRef observer;
 
   event_poll_fd.events = G_IO_IN;
   event_poll_fd.fd = -1;
@@ -343,9 +705,23 @@ _gdk_quartz_event_loop_init (void)
   g_source_set_can_recurse (source, TRUE);
   g_source_attach (source, NULL);
 
+  main_thread = g_thread_self ();
+  main_thread_run_loop = CFRunLoopGetCurrent ();
+
+  observer = CFRunLoopObserverCreate(NULL, /* default allocator */
+				     kCFRunLoopEntry | kCFRunLoopExit,
+				     // kCFRunLoopAllActivities,
+				     true, /* repeats: not one-shot */
+				     0, /* order (priority) */
+				     run_loop_observer_callback,
+				     NULL);
+				     
+  CFRunLoopAddObserver(main_thread_run_loop, observer, kCFRunLoopCommonModes);
+  
   old_poll_func = g_main_context_get_poll_func (NULL);
   g_main_context_set_poll_func (NULL, poll_func);  
 
   autorelease_pool = [[NSAutoreleasePool alloc] init];
+  
+  select_thread_mutex = g_mutex_new ();
 }
-
-- 
1.6.0


--=-XJ7NXCzldB2WxV4OEwkx--



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]