Move gepdump to bin/
[samba-gtk.git] / common / gtk_events.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    main select loop and event handling
5    
6    plugin for using a gtk application's event loop
7
8    Copyright (C) Stefan Metzmacher 2005
9    Copyright (C) Jelmer Vernooij 2009
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <sys/time.h>
32 #include <util.h>
33 #include <tevent.h>
34 #include "tevent_internal.h"
35
36 #include "common/select.h"
37
38 /* as gtk_main() doesn't take a parameter nor return one,
39    we need to have a global event context structure for our
40    gtk-based tools
41  */
42 static struct tevent_context *gtk_event_context_global;
43
44 static int gtk_event_context_destructor(struct tevent_context *ev)
45 {
46         gtk_event_context_global = NULL;
47         return 0;
48 }
49
50 /*
51   create a gtk_event_context structure.
52 */
53 static int gtk_event_context_init(struct tevent_context *ev)
54 {
55         talloc_set_destructor(ev, gtk_event_context_destructor);
56         return 0;
57 }
58
59 struct gtk_tevent_fd {
60         bool running;
61         bool free_after_run;
62         GIOChannel *channel;
63         guint fd_id;
64 };
65
66 static gboolean gtk_event_fd_handler(GIOChannel *source, GIOCondition condition, gpointer data)
67 {
68         struct tevent_fd *fde = talloc_get_type(data, struct tevent_fd);
69         struct gtk_tevent_fd *gtk_fd = talloc_get_type(fde->additional_data,
70                                                       struct gtk_tevent_fd);
71         int flags = 0;
72
73         if (condition & (G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP))
74                 flags |= TEVENT_FD_READ;
75         if (condition & G_IO_OUT)
76                 flags |= TEVENT_FD_WRITE;
77
78         gtk_fd->running = true;
79         fde->handler(fde->event_ctx, fde, flags, fde->private_data);
80         gtk_fd->running = false;
81
82         if (gtk_fd->free_after_run) {
83                 talloc_free(fde);
84                 return gtk_false();
85         }
86
87         return gtk_true();
88 }
89
90 /*
91   destroy an tevent_fd
92 */
93 static int gtk_event_fd_destructor(struct tevent_fd *fde)
94 {
95         struct gtk_tevent_fd *gtk_fd = talloc_get_type(fde->additional_data,
96                                                       struct gtk_tevent_fd);
97
98         if (gtk_fd->running) {
99                 /* the event is running reject the talloc_free()
100                    as it's done by the gtk_event_timed_handler()
101                  */
102                 gtk_fd->free_after_run = true;
103                 return -1;
104         }
105
106         if (fde->flags) {
107                 /* only if any flag is set we have really registered an event */
108                 g_source_remove(gtk_fd->fd_id);
109         }
110         g_io_channel_unref(gtk_fd->channel);
111
112         return 0;
113 }
114
115 /*
116   add a fd based event
117   return NULL on failure (memory allocation error)
118 */
119 static struct tevent_fd *gtk_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
120                                          int fd, uint16_t flags,
121                                          tevent_fd_handler_t handler,
122                                          void *private_data,
123                                          const char *handler_location,
124                                          const char *location)
125 {
126         struct tevent_fd *fde;
127         struct gtk_tevent_fd *gtk_fd;
128         GIOChannel *channel;
129         guint fd_id = 0;
130         GIOCondition condition = 0;
131
132         fde = talloc(mem_ctx?mem_ctx:ev, struct tevent_fd);
133         if (!fde) return NULL;
134
135         gtk_fd = talloc(fde, struct gtk_tevent_fd);
136         if (gtk_fd == NULL) {
137                 talloc_free(fde);
138                 return NULL;
139         }
140
141         fde->event_ctx          = ev;
142         fde->fd                 = fd;
143         fde->flags              = flags;
144         fde->handler            = handler;
145         fde->private_data       = private_data;
146         fde->additional_flags   = 0;
147         fde->additional_data    = gtk_fd;
148
149         channel = g_io_channel_unix_new(fde->fd);
150         if (channel == NULL) {
151                 talloc_free(fde);
152                 return NULL;
153         }
154
155         if (fde->flags & TEVENT_FD_READ)
156                 condition |= (G_IO_IN | G_IO_ERR | G_IO_HUP);
157         if (fde->flags & TEVENT_FD_WRITE)
158                 condition |= G_IO_OUT;
159
160         if (condition) {
161                 /* only register the event when at least one flag is set
162                    as condition == 0 means wait for any event and is not the same
163                    as fde->flags == 0 !
164                 */
165                 fd_id = g_io_add_watch(channel, condition, gtk_event_fd_handler, fde);
166         }
167
168         gtk_fd->running         = false;
169         gtk_fd->free_after_run  = false;
170         gtk_fd->channel         = channel;
171         gtk_fd->fd_id           = fd_id;
172
173         talloc_set_destructor(fde, gtk_event_fd_destructor);
174
175         return fde;
176 }
177
178 /*
179   return the fd event flags
180 */
181 static uint16_t gtk_event_get_fd_flags(struct tevent_fd *fde)
182 {
183         return fde->flags;
184 }
185
186 /*
187   set the fd event flags
188 */
189 static void gtk_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags)
190 {
191         struct gtk_tevent_fd *gtk_fd = talloc_get_type(fde->additional_data,
192                                                       struct gtk_tevent_fd);
193         GIOCondition condition = 0;
194
195         if (fde->flags == flags) return;
196
197         if (flags & TEVENT_FD_READ)
198                 condition |= (G_IO_IN | G_IO_ERR | G_IO_HUP);
199         if (flags & TEVENT_FD_WRITE)
200                 condition |= G_IO_OUT;
201
202         /* only register the event when at least one flag is set
203            as condition == 0 means wait for any event and is not the same
204            as fde->flags == 0 !
205         */
206         if (fde->flags) {
207                 g_source_remove(gtk_fd->fd_id);
208         }
209         if (condition) {
210                 gtk_fd->fd_id = g_io_add_watch(gtk_fd->channel, condition, gtk_event_fd_handler, fde);
211         }
212
213         fde->flags = flags;
214 }
215
216 struct gtk_tevent_timer {
217         guint te_id;
218 };
219
220 /*
221   destroy a timed event
222 */
223 static int gtk_event_timed_destructor(struct tevent_timer *te)
224 {
225         struct gtk_tevent_timer *gtk_te = talloc_get_type(te->additional_data,
226                                                          struct gtk_tevent_timer);
227
228         g_source_remove(gtk_te->te_id);
229
230         return 0;
231 }
232
233 static int gtk_event_timed_deny_destructor(struct tevent_timer *te)
234 {
235         return -1;
236 }
237
238 static gboolean gtk_event_timed_handler(gpointer data)
239 {
240         struct tevent_timer *te = talloc_get_type(data, struct tevent_timer);
241         struct timeval t = timeval_current();
242
243         /* deny the handler to free the event */
244         talloc_set_destructor(te, gtk_event_timed_deny_destructor);
245         te->handler(te->event_ctx, te, t, te->private_data);
246
247         talloc_set_destructor(te, gtk_event_timed_destructor);
248         talloc_free(te);
249
250         /* return FALSE mean this event should be removed */
251         return gtk_false();
252 }
253
254 /*
255   add a timed event
256   return NULL on failure (memory allocation error)
257 */
258 static struct tevent_timer *gtk_event_add_timer(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
259                                                struct timeval next_event, 
260                                                tevent_timer_handler_t handler, 
261                                                void *private_data,
262                                                    const char *handler_name,
263                                                    const char *location) 
264 {
265         struct tevent_timer *te;
266         struct gtk_tevent_timer *gtk_te;
267         struct timeval cur_tv, diff_tv;
268         guint timeout;
269
270         te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer);
271         if (te == NULL) return NULL;
272
273         gtk_te = talloc(te, struct gtk_tevent_timer);
274         if (gtk_te == NULL) {
275                 talloc_free(te);
276                 return NULL;
277         }
278
279         te->event_ctx           = ev;
280         te->next_event          = next_event;
281         te->handler             = handler;
282         te->private_data        = private_data;
283         te->additional_data     = gtk_te;
284
285         cur_tv                  = timeval_current();
286         diff_tv                 = timeval_until(&cur_tv, &next_event);
287         timeout                 = ((diff_tv.tv_usec+999)/1000)+(diff_tv.tv_sec*1000);
288
289         gtk_te->te_id           = g_timeout_add(timeout, gtk_event_timed_handler, te);
290
291         talloc_set_destructor(te, gtk_event_timed_destructor);
292
293         return te;
294 }
295
296 /*
297   do a single event loop
298 */
299
300 static int gtk_event_loop_once(struct tevent_context *ev,
301                                const char *location)
302 {
303         /*
304          * gtk_main_iteration ()
305          *
306          * gboolean    gtk_main_iteration              (void);
307          *
308          * Runs a single iteration of the mainloop. If no events 
309          * are waiting to be processed GTK+ will block until the
310          * next event is noticed. If you don't want to block look
311          * at gtk_main_iteration_do() or check if any events are
312          * pending with gtk_events_pending() first.
313          * 
314          * Returns :    TRUE if gtk_main_quit() has been called for the innermost mainloop.
315          */
316         gboolean ret;
317
318         ret = gtk_main_iteration();
319         if (ret == gtk_true()) {
320                 return -1;
321         }
322
323         return 0;
324 }
325
326 /*
327   return with 0
328 */
329
330 static int gtk_event_loop_wait(struct tevent_context *ev,
331                                const char *location)
332 {
333         /*
334          * gtk_main ()
335          * 
336          * void        gtk_main                        (void);
337          * 
338          * Runs the main loop until gtk_main_quit() is called.
339          * You can nest calls to gtk_main(). In that case
340          * gtk_main_quit() will make the innermost invocation
341          * of the main loop return. 
342          */
343         gtk_main();
344         return 0;
345 }
346
347 static const struct tevent_ops gtk_event_ops = {
348         .context_init   = gtk_event_context_init,
349         .add_fd         = gtk_event_add_fd,
350         .get_fd_flags   = gtk_event_get_fd_flags,
351         .set_fd_flags   = gtk_event_set_fd_flags,
352         .add_timer      = gtk_event_add_timer,
353         .loop_once      = gtk_event_loop_once,
354         .loop_wait      = gtk_event_loop_wait,
355 };
356
357 int gtk_event_loop(void)
358 {
359         int ret;
360
361         tevent_register_backend("gtk", &gtk_event_ops);
362
363         gtk_event_context_global = tevent_context_init_byname(NULL, "gtk");
364         if (!gtk_event_context_global) return -1;
365
366         ret = tevent_loop_wait(gtk_event_context_global);
367
368         talloc_free(gtk_event_context_global);
369
370         return ret;
371 }
372
373 struct tevent_context *gtk_event_context(void)
374 {
375         return gtk_event_context_global;
376 }