]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkeventloop-quartz.c
Protect the polling thread setup and shutdown function by mutexes and read
[~andy/gtk] / gdk / quartz / gdkeventloop-quartz.c
1 #include <config.h>
2
3 #include <glib.h>
4 #include <pthread.h>
5 #include <sys/types.h>
6 #include <sys/uio.h>
7 #include <unistd.h>
8
9 #include "gdkprivate-quartz.h"
10
11 static GPollFD event_poll_fd;
12 static NSEvent *current_event;
13
14 static GPollFunc old_poll_func;
15
16 static gboolean select_fd_waiting = FALSE;
17 static pthread_t select_thread = 0;
18 static int wakeup_pipe[2];
19 static pthread_mutex_t pollfd_mutex = PTHREAD_MUTEX_INITIALIZER;
20 static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
21 static GPollFD *pollfds;
22 static GPollFD *pipe_pollfd;
23 static guint n_pollfds;
24 static CFRunLoopSourceRef select_main_thread_source;
25 static CFRunLoopRef main_thread_run_loop;
26
27 static gboolean
28 gdk_event_prepare (GSource *source,
29                    gint    *timeout)
30 {
31   NSEvent *event;
32   gboolean retval;
33   
34   GDK_QUARTZ_ALLOC_POOL;
35
36   *timeout = -1;
37
38   event = [NSApp nextEventMatchingMask: NSAnyEventMask
39                              untilDate: [NSDate distantPast]
40                                 inMode: NSDefaultRunLoopMode
41                                dequeue: NO];
42
43   retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
44             event != NULL);
45
46   GDK_QUARTZ_RELEASE_POOL;
47
48   return retval;
49 }
50
51 static gboolean
52 gdk_event_check (GSource *source)
53 {
54   if (_gdk_event_queue_find_first (_gdk_display) != NULL ||
55       current_event)
56     return TRUE;
57
58   /* FIXME: We should maybe try to fetch an event again here */
59
60   return FALSE;
61 }
62
63 static gboolean
64 gdk_event_dispatch (GSource     *source,
65                     GSourceFunc  callback,
66                     gpointer     user_data)
67 {
68   GdkEvent *event;
69
70   GDK_QUARTZ_ALLOC_POOL;
71
72   _gdk_events_queue (_gdk_display);
73
74   event = _gdk_event_unqueue (_gdk_display);
75
76   if (event)
77     {
78       if (_gdk_event_func)
79         (*_gdk_event_func) (event, _gdk_event_data);
80
81       gdk_event_free (event);
82     }
83
84   GDK_QUARTZ_RELEASE_POOL;
85
86   return TRUE;
87 }
88
89 static GSourceFuncs event_funcs = {
90   gdk_event_prepare,
91   gdk_event_check,
92   gdk_event_dispatch,
93   NULL
94 };
95
96 static void 
97 got_fd_activity (void *info)
98 {
99   NSEvent *event;
100
101   /* Post a message so we'll break out of the message loop */
102   event = [NSEvent otherEventWithType: NSApplicationDefined
103                              location: NSZeroPoint
104                         modifierFlags: 0
105                             timestamp: 0
106                          windowNumber: 0
107                               context: nil
108                               subtype: 0
109                                 data1: 0 
110                                 data2: 0];
111
112   [NSApp postEvent:event atStart:YES];
113 }
114
115 static void *
116 select_thread_func (void *arg)
117 {
118   int n_active_fds;
119
120   pthread_mutex_lock (&pollfd_mutex);
121   pthread_cond_signal (&ready_cond);
122
123   while (1)
124     {
125       char c;
126       int n;
127
128       pthread_cond_wait (&ready_cond, &pollfd_mutex);
129
130       pthread_mutex_unlock (&pollfd_mutex);
131       select_fd_waiting = TRUE;
132       n_active_fds = old_poll_func (pollfds, n_pollfds, -1);
133       select_fd_waiting = FALSE;
134       pthread_mutex_lock (&pollfd_mutex);
135       n = read (pipe_pollfd->fd, &c, 1);
136       if (n == 1)
137         {
138           g_assert (c == 'A');
139
140           n_active_fds --;
141         }
142       pthread_mutex_unlock (&pollfd_mutex);
143
144       if (n_active_fds)
145         {
146           /* We have active fds, signal the main thread */
147           CFRunLoopSourceSignal (select_main_thread_source);
148           if (CFRunLoopIsWaiting (main_thread_run_loop))
149             CFRunLoopWakeUp (main_thread_run_loop);
150         }
151
152       pthread_mutex_lock (&pollfd_mutex);
153     }
154 }
155
156 static gint
157 poll_func (GPollFD *ufds, guint nfds, gint timeout_)
158 {
159   NSEvent *event;
160   NSDate *limit_date;
161   int n_active = 0;
162   int i;
163
164   GDK_QUARTZ_ALLOC_POOL;
165
166   if (nfds > 1)
167     {
168       if (!select_thread) {
169         /* Create source used for signalling the main thread */
170         main_thread_run_loop = CFRunLoopGetCurrent ();
171         CFRunLoopSourceContext source_context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, got_fd_activity };
172         select_main_thread_source = CFRunLoopSourceCreate (NULL, 0, &source_context);
173         CFRunLoopAddSource (main_thread_run_loop, select_main_thread_source, kCFRunLoopDefaultMode);
174
175         pipe (wakeup_pipe);
176         fcntl(wakeup_pipe[0], F_SETFL, O_NONBLOCK);
177
178         pthread_mutex_lock (&pollfd_mutex);
179         pthread_create (&select_thread, NULL, select_thread_func, NULL);
180         pthread_cond_wait (&ready_cond, &pollfd_mutex);
181       } else
182         pthread_mutex_lock (&pollfd_mutex);
183
184       n_pollfds = nfds;
185       g_free (pollfds);
186       pollfds = g_memdup (ufds, sizeof (GPollFD) * nfds);
187
188       /* We cheat and use the fake fd for our pipe */
189       for (i = 0; i < nfds; i++)
190         {
191           if (pollfds[i].fd == -1)
192             {
193               pipe_pollfd = &pollfds[i];
194               pollfds[i].fd = wakeup_pipe[0];
195               pollfds[i].events = G_IO_IN;
196             }
197         }
198
199       /* Start our thread */
200       pthread_cond_signal (&ready_cond);
201       pthread_mutex_unlock (&pollfd_mutex);
202     }
203
204   if (timeout_ == -1)
205     limit_date = [NSDate distantFuture];
206   else if (timeout_ == 0)
207     limit_date = [NSDate distantPast];
208   else
209     limit_date = [NSDate dateWithTimeIntervalSinceNow:timeout_/1000.0];
210
211   event = [NSApp nextEventMatchingMask: NSAnyEventMask
212                              untilDate: limit_date
213                                 inMode: NSDefaultRunLoopMode
214                                dequeue: YES];
215   
216   if (event)
217     {
218       if ([event type] == NSApplicationDefined)
219         {
220           pthread_mutex_lock (&pollfd_mutex);
221
222           for (i = 0; i < n_pollfds; i++)
223             {
224               if (ufds[i].fd == -1)
225                 continue;
226
227               g_assert (ufds[i].fd == pollfds[i].fd);
228               g_assert (ufds[i].events == pollfds[i].events);
229
230               if (pollfds[i].revents)
231                 {
232                   ufds[i].revents = pollfds[i].revents;
233                   n_active ++;
234                 }
235             }
236
237           pthread_mutex_unlock (&pollfd_mutex);
238
239           event = [NSApp nextEventMatchingMask: NSAnyEventMask
240             untilDate: [NSDate distantPast]
241             inMode: NSDefaultRunLoopMode
242             dequeue: YES];
243
244         }
245     }
246
247   /* There were no active fds, break out of the other thread's poll() */
248   pthread_mutex_lock (&pollfd_mutex);
249   if (select_fd_waiting && wakeup_pipe[1])
250     {
251       char c = 'A';
252
253       write (wakeup_pipe[1], &c, 1);
254     }
255   pthread_mutex_unlock (&pollfd_mutex);
256
257   if (event) 
258     {
259       ufds[0].revents = G_IO_IN;
260
261       /* FIXME: We can't assert here, but we might need to have a
262        * queue for events instead.
263        */
264       /*g_assert (current_event == NULL);*/
265
266       current_event = [event retain];
267
268       n_active ++;
269     }
270
271   GDK_QUARTZ_RELEASE_POOL;
272
273   return n_active;
274 }
275
276 void
277 _gdk_quartz_event_loop_init (void)
278 {
279   GSource *source;
280
281   event_poll_fd.events = G_IO_IN;
282   event_poll_fd.fd = -1;
283
284   source = g_source_new (&event_funcs, sizeof (GSource));
285   g_source_add_poll (source, &event_poll_fd);
286   g_source_set_priority (source, GDK_PRIORITY_EVENTS);
287   g_source_set_can_recurse (source, TRUE);
288   g_source_attach (source, NULL);
289
290   old_poll_func = g_main_context_get_poll_func (NULL);
291   g_main_context_set_poll_func (NULL, poll_func);  
292  
293 }
294
295 NSEvent *
296 _gdk_quartz_event_loop_get_current (void)
297 {
298   return current_event;
299 }
300
301 void
302 _gdk_quartz_event_loop_release_current (void)
303 {
304   [current_event release];
305   current_event = NULL;
306 }
307