1
/* -*- mode: C; c-file-style: "gnu" -*- */
2
/* dbus-gmain.c GLib main loop integration
3
 *
4
 * Copyright (C) 2002, 2003 CodeFactory AB
5
 * Copyright (C) 2005 Red Hat, Inc.
6
 *
7
 * Licensed under the Academic Free License version 2.1
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
 *
23
 * This file is based on dbus-gmain.c from dbus-glib with functions renamed
24
 * and unnecessary code removed.
25
 */
26

            
27
#include "glib.h"
28
#include <config.h>
29
#include <dbus/dbus.h>
30
#include <string.h>
31

            
32
#include <libintl.h>
33
#define _(x) dgettext (GETTEXT_PACKAGE, x)
34
#define N_(x) x
35

            
36
/*
37
 * DBusGMessageQueue:
38
 * A GSource subclass for dispatching DBusConnection messages.
39
 * We need this on top of the IO handlers, because sometimes
40
 * there are messages to dispatch queued up but no IO pending.
41
 */
42
typedef struct
43
{
44
  GSource source;             /* the parent GSource */
45
  DBusConnection *connection; /* the connection to dispatch */
46
} DBusGMessageQueue;
47

            
48
static gboolean message_queue_prepare (GSource *source,
49
                                       gint *timeout);
50
static gboolean message_queue_check (GSource *source);
51
static gboolean message_queue_dispatch (GSource *source,
52
                                        GSourceFunc callback,
53
                                        gpointer user_data);
54

            
55
static const GSourceFuncs message_queue_funcs = {
56
  message_queue_prepare,
57
  message_queue_check,
58
  message_queue_dispatch,
59
  NULL
60
};
61

            
62
static gboolean
63
67110
message_queue_prepare (GSource *source,
64
                       gint *timeout)
65
{
66
67110
  DBusConnection *connection = ((DBusGMessageQueue *) source)->connection;
67

            
68
67110
  *timeout = -1;
69

            
70
67110
  return (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_DATA_REMAINS);
71
}
72

            
73
static gboolean
74
61982
message_queue_check (GSource *source)
75
{
76
61982
  return FALSE;
77
}
78

            
79
static gboolean
80
5128
message_queue_dispatch (GSource *source,
81
                        GSourceFunc callback,
82
                        gpointer user_data)
83
{
84
5128
  DBusConnection *connection = ((DBusGMessageQueue *) source)->connection;
85

            
86
5128
  dbus_connection_ref (connection);
87

            
88
  /* Only dispatch once - we don't want to starve other GSource */
89
5128
  dbus_connection_dispatch (connection);
90

            
91
5128
  dbus_connection_unref (connection);
92

            
93
5128
  return TRUE;
94
}
95

            
96
typedef struct
97
{
98
  GMainContext *context;         /* the main context */
99
  GSList *ios;                   /* all IOHandler */
100
  GSList *timeouts;              /* all TimeoutHandler */
101
  DBusConnection *connection;    /* NULL if this is really for a server not a connection */
102
  GSource *message_queue_source; /* DBusGMessageQueue */
103
} ConnectionSetup;
104

            
105
typedef struct
106
{
107
  ConnectionSetup *cs;
108
  GSource *source;
109
  DBusWatch *watch;
110
} IOHandler;
111

            
112
typedef struct
113
{
114
  ConnectionSetup *cs;
115
  GSource *source;
116
  DBusTimeout *timeout;
117
} TimeoutHandler;
118

            
119
dbus_int32_t _dbus_gmain_connection_slot = -1;
120
static dbus_int32_t server_slot = -1;
121

            
122
static ConnectionSetup *
123
672
connection_setup_new (GMainContext *context,
124
                      DBusConnection *connection)
125
{
126
  ConnectionSetup *cs;
127

            
128
672
  cs = g_new0 (ConnectionSetup, 1);
129

            
130
672
  g_assert (context != NULL);
131

            
132
672
  cs->context = context;
133
672
  g_main_context_ref (cs->context);
134

            
135
672
  if (connection)
136
    {
137
511
      cs->connection = connection;
138

            
139
511
      cs->message_queue_source = g_source_new ((GSourceFuncs *) &message_queue_funcs,
140
                                               sizeof (DBusGMessageQueue));
141
511
      ((DBusGMessageQueue *) cs->message_queue_source)->connection = connection;
142
511
      g_source_attach (cs->message_queue_source, cs->context);
143
    }
144

            
145
672
  return cs;
146
}
147

            
148
static void
149
1819
io_handler_source_finalized (gpointer data)
150
{
151
  IOHandler *handler;
152

            
153
1819
  handler = data;
154

            
155
1819
  if (handler->watch)
156
1711
    dbus_watch_set_data (handler->watch, NULL, NULL);
157

            
158
1819
  g_free (handler);
159
1819
}
160

            
161
static void
162
3638
io_handler_destroy_source (void *data)
163
{
164
  IOHandler *handler;
165

            
166
3638
  handler = data;
167

            
168
3638
  if (handler->source)
169
    {
170
1819
      GSource *source = handler->source;
171
1819
      handler->source = NULL;
172
1819
      handler->cs->ios = g_slist_remove (handler->cs->ios, handler);
173
1819
      g_source_destroy (source);
174
1819
      g_source_unref (source);
175
    }
176
3638
}
177

            
178
static void
179
1819
io_handler_watch_freed (void *data)
180
{
181
  IOHandler *handler;
182

            
183
1819
  handler = data;
184

            
185
1819
  handler->watch = NULL;
186

            
187
1819
  io_handler_destroy_source (handler);
188
1819
}
189

            
190
static gboolean
191
4286
io_handler_dispatch (GIOChannel *source,
192
                     GIOCondition condition,
193
                     gpointer data)
194
{
195
  IOHandler *handler;
196
4286
  guint dbus_condition = 0;
197
  DBusConnection *connection;
198

            
199
4286
  handler = data;
200

            
201
4286
  connection = handler->cs->connection;
202

            
203
4286
  if (connection)
204
4127
    dbus_connection_ref (connection);
205

            
206
4286
  if (condition & G_IO_IN)
207
3976
    dbus_condition |= DBUS_WATCH_READABLE;
208
4286
  if (condition & G_IO_OUT)
209
310
    dbus_condition |= DBUS_WATCH_WRITABLE;
210
4286
  if (condition & G_IO_ERR)
211
8
    dbus_condition |= DBUS_WATCH_ERROR;
212
4286
  if (condition & G_IO_HUP)
213
109
    dbus_condition |= DBUS_WATCH_HANGUP;
214

            
215
  /* Note that we don't touch the handler after this, because
216
   * dbus may have disabled the watch and thus killed the
217
   * handler.
218
   */
219
4286
  dbus_watch_handle (handler->watch, dbus_condition);
220
4286
  handler = NULL;
221

            
222
4286
  if (connection)
223
4127
    dbus_connection_unref (connection);
224

            
225
4286
  return TRUE;
226
}
227

            
228
/* Attach the connection setup to the given watch, removing any
229
 * previously-attached connection setup.
230
 */
231
static void
232
2492
connection_setup_add_watch (ConnectionSetup *cs,
233
                            DBusWatch *watch)
234
{
235
  guint flags;
236
  GIOCondition condition;
237
  GIOChannel *channel;
238
  IOHandler *handler;
239

            
240
2492
  if (!dbus_watch_get_enabled (watch))
241
511
    return;
242

            
243
1981
  flags = dbus_watch_get_flags (watch);
244

            
245
1981
  condition = G_IO_ERR | G_IO_HUP;
246
1981
  if (flags & DBUS_WATCH_READABLE)
247
1242
    condition |= G_IO_IN;
248
1981
  if (flags & DBUS_WATCH_WRITABLE)
249
739
    condition |= G_IO_OUT;
250

            
251
1981
  handler = g_new0 (IOHandler, 1);
252
1981
  handler->cs = cs;
253
1981
  handler->watch = watch;
254

            
255
1981
  channel = g_io_channel_unix_new (dbus_watch_get_unix_fd (watch));
256

            
257
1981
  handler->source = g_io_create_watch (channel, condition);
258
1981
  g_source_set_callback (handler->source, (GSourceFunc) io_handler_dispatch, handler,
259
                         io_handler_source_finalized);
260
1981
  g_source_attach (handler->source, cs->context);
261

            
262
1981
  cs->ios = g_slist_prepend (cs->ios, handler);
263

            
264
1981
  dbus_watch_set_data (watch, handler, io_handler_watch_freed);
265
1981
  g_io_channel_unref (channel);
266
}
267

            
268
static void
269
2329
connection_setup_remove_watch (ConnectionSetup *cs,
270
                               DBusWatch *watch)
271
{
272
  IOHandler *handler;
273

            
274
2329
  handler = dbus_watch_get_data (watch);
275

            
276
2329
  if (handler == NULL || handler->cs != cs)
277
510
    return;
278

            
279
1819
  io_handler_destroy_source (handler);
280
}
281

            
282
static void
283
2002
timeout_handler_source_finalized (gpointer data)
284
{
285
  TimeoutHandler *handler;
286

            
287
2002
  handler = data;
288

            
289
2002
  if (handler->timeout)
290
2002
    dbus_timeout_set_data (handler->timeout, NULL, NULL);
291

            
292
2002
  g_free (handler);
293
2002
}
294

            
295
static void
296
4004
timeout_handler_destroy_source (void *data)
297
{
298
  TimeoutHandler *handler;
299

            
300
4004
  handler = data;
301

            
302
4004
  if (handler->source)
303
    {
304
2002
      GSource *source = handler->source;
305
2002
      handler->source = NULL;
306
2002
      handler->cs->timeouts = g_slist_remove (handler->cs->timeouts, handler);
307
2002
      g_source_destroy (source);
308
2002
      g_source_unref (source);
309
    }
310
4004
}
311

            
312
static void
313
2002
timeout_handler_timeout_freed (void *data)
314
{
315
  TimeoutHandler *handler;
316

            
317
2002
  handler = data;
318

            
319
2002
  handler->timeout = NULL;
320

            
321
2002
  timeout_handler_destroy_source (handler);
322
2002
}
323

            
324
static gboolean
325
1
timeout_handler_dispatch (gpointer data)
326
{
327
  TimeoutHandler *handler;
328

            
329
1
  handler = data;
330

            
331
1
  dbus_timeout_handle (handler->timeout);
332

            
333
1
  return TRUE;
334
}
335

            
336
static void
337
2002
connection_setup_add_timeout (ConnectionSetup *cs,
338
                              DBusTimeout *timeout)
339
{
340
  TimeoutHandler *handler;
341

            
342
2002
  if (!dbus_timeout_get_enabled (timeout))
343
    return;
344

            
345
2002
  handler = g_new0 (TimeoutHandler, 1);
346
2002
  handler->cs = cs;
347
2002
  handler->timeout = timeout;
348

            
349
2002
  handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
350
2002
  g_source_set_callback (handler->source, timeout_handler_dispatch, handler,
351
                         timeout_handler_source_finalized);
352
2002
  g_source_attach (handler->source, handler->cs->context);
353

            
354
2002
  cs->timeouts = g_slist_prepend (cs->timeouts, handler);
355

            
356
2002
  dbus_timeout_set_data (timeout, handler, timeout_handler_timeout_freed);
357
}
358

            
359
static void
360
2002
connection_setup_remove_timeout (ConnectionSetup *cs,
361
                                 DBusTimeout *timeout)
362
{
363
  TimeoutHandler *handler;
364

            
365
2002
  handler = dbus_timeout_get_data (timeout);
366

            
367
2002
  if (handler == NULL)
368
    return;
369

            
370
2002
  timeout_handler_destroy_source (handler);
371
}
372

            
373
static void
374
485
connection_setup_free (ConnectionSetup *cs)
375
{
376
485
  while (cs->ios)
377
    io_handler_destroy_source (cs->ios->data);
378

            
379
485
  while (cs->timeouts)
380
    timeout_handler_destroy_source (cs->timeouts->data);
381

            
382
485
  if (cs->message_queue_source)
383
    {
384
      GSource *source;
385

            
386
485
      source = cs->message_queue_source;
387
485
      cs->message_queue_source = NULL;
388

            
389
485
      g_source_destroy (source);
390
485
      g_source_unref (source);
391
    }
392

            
393
485
  g_main_context_unref (cs->context);
394
485
  g_free (cs);
395
485
}
396

            
397
static dbus_bool_t
398
2492
add_watch (DBusWatch *watch,
399
           gpointer data)
400
{
401
  ConnectionSetup *cs;
402

            
403
2492
  cs = data;
404

            
405
2492
  connection_setup_add_watch (cs, watch);
406

            
407
2492
  return TRUE;
408
}
409

            
410
static void
411
2329
remove_watch (DBusWatch *watch,
412
              gpointer data)
413
{
414
  ConnectionSetup *cs;
415

            
416
2329
  cs = data;
417

            
418
2329
  connection_setup_remove_watch (cs, watch);
419
2329
}
420

            
421
static void
422
2618
watch_toggled (DBusWatch *watch,
423
               void *data)
424
{
425
  /* Because we just exit on OOM, enable/disable is
426
   * no different from add/remove
427
   */
428
2618
  if (dbus_watch_get_enabled (watch))
429
1309
    add_watch (watch, data);
430
  else
431
1309
    remove_watch (watch, data);
432
2618
}
433

            
434
static dbus_bool_t
435
2002
add_timeout (DBusTimeout *timeout,
436
             void *data)
437
{
438
  ConnectionSetup *cs;
439

            
440
2002
  cs = data;
441

            
442
2002
  if (!dbus_timeout_get_enabled (timeout))
443
    return TRUE;
444

            
445
2002
  connection_setup_add_timeout (cs, timeout);
446

            
447
2002
  return TRUE;
448
}
449

            
450
static void
451
2002
remove_timeout (DBusTimeout *timeout,
452
                void *data)
453
{
454
  ConnectionSetup *cs;
455

            
456
2002
  cs = data;
457

            
458
2002
  connection_setup_remove_timeout (cs, timeout);
459
2002
}
460

            
461
static void
462
timeout_toggled (DBusTimeout *timeout,
463
                 void *data)
464
{
465
  /* Because we just exit on OOM, enable/disable is
466
   * no different from add/remove
467
   */
468
  if (dbus_timeout_get_enabled (timeout))
469
    add_timeout (timeout, data);
470
  else
471
    remove_timeout (timeout, data);
472
}
473

            
474
static void
475
7009
wakeup_main (void *data)
476
{
477
7009
  ConnectionSetup *cs = data;
478

            
479
7009
  g_main_context_wakeup (cs->context);
480
7009
}
481

            
482
/* Move to a new context */
483
static ConnectionSetup *
484
connection_setup_new_from_old (GMainContext *context,
485
                               ConnectionSetup *old)
486
{
487
  ConnectionSetup *cs;
488

            
489
  g_assert (old->context != context);
490

            
491
  cs = connection_setup_new (context, old->connection);
492

            
493
  while (old->ios != NULL)
494
    {
495
      IOHandler *handler = old->ios->data;
496

            
497
      connection_setup_add_watch (cs, handler->watch);
498
      /* The old handler will be removed from old->ios as a side-effect */
499
    }
500

            
501
  while (old->timeouts != NULL)
502
    {
503
      TimeoutHandler *handler = old->timeouts->data;
504

            
505
      connection_setup_add_timeout (cs, handler->timeout);
506
    }
507

            
508
  return cs;
509
}
510

            
511
/* @} */ /* End of GLib bindings internals */
512

            
513
/**
514
 * atspi_dbus_connection_setup_with_g_main: (skip)
515
 * @connection: the connection
516
 * @context: the #GMainContext or #NULL for default context
517
 *
518
 * Sets the watch and timeout functions of a #DBusConnection
519
 * to integrate the connection with the GLib main loop.
520
 * Pass in #NULL for the #GMainContext unless you're
521
 * doing something specialized.
522
 *
523
 * If called twice for the same context, does nothing the second
524
 * time. If called once with context A and once with context B,
525
 * context B replaces context A as the context monitoring the
526
 * connection.
527
 */
528
void
529
511
atspi_dbus_connection_setup_with_g_main (DBusConnection *connection,
530
                                         GMainContext *context)
531
{
532
  ConnectionSetup *old_setup;
533
  ConnectionSetup *cs;
534

            
535
  /* FIXME we never free the slot, so its refcount just keeps growing,
536
   * which is kind of broken.
537
   */
538
511
  dbus_connection_allocate_data_slot (&_dbus_gmain_connection_slot);
539
511
  if (_dbus_gmain_connection_slot < 0)
540
    goto nomem;
541

            
542
511
  if (context == NULL)
543
348
    context = g_main_context_default ();
544

            
545
511
  cs = NULL;
546

            
547
511
  old_setup = dbus_connection_get_data (connection, _dbus_gmain_connection_slot);
548
511
  if (old_setup != NULL)
549
    {
550
      if (old_setup->context == context)
551
        return; /* nothing to do */
552

            
553
      cs = connection_setup_new_from_old (context, old_setup);
554

            
555
      /* Nuke the old setup */
556
      dbus_connection_set_data (connection, _dbus_gmain_connection_slot, NULL, NULL);
557
      old_setup = NULL;
558
    }
559

            
560
511
  if (cs == NULL)
561
511
    cs = connection_setup_new (context, connection);
562

            
563
511
  if (!dbus_connection_set_data (connection, _dbus_gmain_connection_slot, cs,
564
                                 (DBusFreeFunction) connection_setup_free))
565
    goto nomem;
566

            
567
511
  if (!dbus_connection_set_watch_functions (connection,
568
                                            add_watch,
569
                                            remove_watch,
570
                                            watch_toggled,
571
                                            cs, NULL))
572
    goto nomem;
573

            
574
511
  if (!dbus_connection_set_timeout_functions (connection,
575
                                              add_timeout,
576
                                              remove_timeout,
577
                                              timeout_toggled,
578
                                              cs, NULL))
579
    goto nomem;
580

            
581
511
  dbus_connection_set_wakeup_main_function (connection,
582
                                            wakeup_main,
583
                                            cs, NULL);
584

            
585
511
  return;
586

            
587
nomem:
588
  g_error ("Not enough memory to set up DBusConnection for use with GLib");
589
}
590

            
591
/**
592
 * atspi_dbus_server_setup_with_g_main: (skip)
593
 * @server: the server
594
 * @context: the #GMainContext or #NULL for default
595
 *
596
 * Sets the watch and timeout functions of a #DBusServer
597
 * to integrate the server with the GLib main loop.
598
 * In most cases the context argument should be #NULL.
599
 *
600
 * If called twice for the same context, does nothing the second
601
 * time. If called once with context A and once with context B,
602
 * context B replaces context A as the context monitoring the
603
 * connection.
604
 */
605
void
606
161
atspi_dbus_server_setup_with_g_main (DBusServer *server,
607
                                     GMainContext *context)
608
{
609
  ConnectionSetup *old_setup;
610
  ConnectionSetup *cs;
611

            
612
  /* FIXME we never free the slot, so its refcount just keeps growing,
613
   * which is kind of broken.
614
   */
615
161
  dbus_server_allocate_data_slot (&server_slot);
616
161
  if (server_slot < 0)
617
    goto nomem;
618

            
619
161
  if (context == NULL)
620
161
    context = g_main_context_default ();
621

            
622
161
  cs = NULL;
623

            
624
161
  old_setup = dbus_server_get_data (server, server_slot);
625
161
  if (old_setup != NULL)
626
    {
627
      if (old_setup->context == context)
628
        return; /* nothing to do */
629

            
630
      cs = connection_setup_new_from_old (context, old_setup);
631

            
632
      /* Nuke the old setup */
633
      if (!dbus_server_set_data (server, server_slot, NULL, NULL))
634
        goto nomem;
635
      old_setup = NULL;
636
    }
637

            
638
161
  if (cs == NULL)
639
161
    cs = connection_setup_new (context, NULL);
640

            
641
161
  if (!dbus_server_set_data (server, server_slot, cs,
642
                             (DBusFreeFunction) connection_setup_free))
643
    goto nomem;
644

            
645
161
  if (!dbus_server_set_watch_functions (server,
646
                                        add_watch,
647
                                        remove_watch,
648
                                        watch_toggled,
649
                                        cs, NULL))
650
    goto nomem;
651

            
652
161
  if (!dbus_server_set_timeout_functions (server,
653
                                          add_timeout,
654
                                          remove_timeout,
655
                                          timeout_toggled,
656
                                          cs, NULL))
657
    goto nomem;
658

            
659
161
  return;
660

            
661
nomem:
662
  g_error ("Not enough memory to set up DBusServer for use with GLib");
663
}