DBus-1-TQt 1.0
tqdbusintegrator.cpp
Go to the documentation of this file.
1/* qdbusintegrator.cpp TQT_DBusConnection private implementation
2 *
3 * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
4 * Copyright (C) 2005 Kevin Krammer <kevin.krammer@gmx.at>
5 *
6 * Licensed under the Academic Free License version 2.1
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 * USA.
22 *
23 */
24
25#include <tqapplication.h>
26#include <tqevent.h>
27#include <tqmetaobject.h>
28#include <tqsocketnotifier.h>
29#include <tqtimer.h>
30
31#include "tqdbusconnection_p.h"
32#include "tqdbusmessage.h"
33
34Atomic::Atomic(int value) : m_value(value)
35{
36}
37
39{
40 m_value++;
41}
42
44{
45 m_value--;
46 return m_value > 0;
47}
48
50
51static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
52{
53 Q_ASSERT(timeout);
54 Q_ASSERT(data);
55
56 // tqDebug("addTimeout %d", dbus_timeout_get_interval(timeout));
57
59
60 if (!dbus_timeout_get_enabled(timeout))
61 return true;
62
63 if (!tqApp) {
64 d->pendingTimeouts.append(timeout);
65 return true;
66 }
67 int timerId = d->startTimer(dbus_timeout_get_interval(timeout));
68 if (!timerId)
69 return false;
70
71 d->timeouts[timerId] = timeout;
72 return true;
73}
74
75static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
76{
77 Q_ASSERT(timeout);
78 Q_ASSERT(data);
79
80 // tqDebug("removeTimeout");
81
84 it != d->pendingTimeouts.end();) {
85 if ((*it) == timeout) {
86 it = d->pendingTimeouts.erase(it);
87 }
88 else
89 ++it;
90 }
91
92 TQT_DBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
93 while (it != d->timeouts.end()) {
94 if (it.data() == timeout) {
95 d->killTimer(it.key());
96 TQT_DBusConnectionPrivate::TimeoutHash::iterator copyIt = it;
97 ++it;
98 d->timeouts.erase(copyIt);
99 } else {
100 ++it;
101 }
102 }
103}
104
105static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
106{
107 Q_ASSERT(timeout);
108 Q_ASSERT(data);
109
110 //tqDebug("ToggleTimeout");
111
112 qDBusRemoveTimeout(timeout, data);
113 qDBusAddTimeout(timeout, data);
114}
115
116static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
117{
118 Q_ASSERT(watch);
119 Q_ASSERT(data);
120
122
123 int flags = dbus_watch_get_flags(watch);
124 int fd = dbus_watch_get_unix_fd(watch);
125
127 if (flags & DBUS_WATCH_READABLE) {
128 bool enabled = dbus_watch_get_enabled(watch);
129 //tqDebug("addReadWatch %d %s", fd, (enabled ? "enabled" : "disabled"));
130 watcher.watch = watch;
131 if (tqApp) {
132 watcher.read = new TQSocketNotifier(fd, TQSocketNotifier::Read, d);
133 if (!enabled) watcher.read->setEnabled(false);
134 d->connect(watcher.read, TQ_SIGNAL(activated(int)), TQ_SLOT(socketRead(int)));
135 }
136 }
137 if (flags & DBUS_WATCH_WRITABLE) {
138 bool enabled = dbus_watch_get_enabled(watch);
139 //tqDebug("addWriteWatch %d %s", fd, (enabled ? "enabled" : "disabled"));
140 watcher.watch = watch;
141 if (tqApp) {
142 watcher.write = new TQSocketNotifier(fd, TQSocketNotifier::Write, d);
143 if (!enabled) watcher.write->setEnabled(false);
144 d->connect(watcher.write, TQ_SIGNAL(activated(int)), TQ_SLOT(socketWrite(int)));
145 }
146 }
147 // FIXME-QT4 d->watchers.insertMulti(fd, watcher);
148 TQT_DBusConnectionPrivate::WatcherHash::iterator it = d->watchers.find(fd);
149 if (it == d->watchers.end())
150 {
152 }
153 it.data().append(watcher);
154
155 return true;
156}
157
158static void qDBusRemoveWatch(DBusWatch *watch, void *data)
159{
160 Q_ASSERT(watch);
161 Q_ASSERT(data);
162
163 //tqDebug("remove watch");
164
166 int fd = dbus_watch_get_unix_fd(watch);
167
168 TQT_DBusConnectionPrivate::WatcherHash::iterator it = d->watchers.find(fd);
169 if (it != d->watchers.end())
170 {
172 for (TQT_DBusConnectionPrivate::WatcherList::iterator wit = list.begin();
173 wit != list.end(); ++wit)
174 {
175 if ((*wit).watch == watch)
176 {
177 // migth be called from a function triggered by a socket listener
178 // so just disconnect them and schedule their delayed deletion.
179
180 d->removedWatches.append(*wit);
181 if ((*wit).read)
182 {
183 (*wit).read->disconnect(d);
184 (*wit).read = 0;
185 }
186 if ((*wit).write)
187 {
188 (*wit).write->disconnect(d);
189 (*wit).write = 0;
190 }
191 (*wit).watch = 0;
192 }
193 }
194 }
195
196 if (d->removedWatches.count() > 0)
197 TQTimer::singleShot(0, d, TQ_SLOT(purgeRemovedWatches()));
198}
199
200static void qDBusToggleWatch(DBusWatch *watch, void *data)
201{
202 Q_ASSERT(watch);
203 Q_ASSERT(data);
204
205 //tqDebug("toggle watch");
206
208 int fd = dbus_watch_get_unix_fd(watch);
209
210 TQT_DBusConnectionPrivate::WatcherHash::iterator it = d->watchers.find(fd);
211 if (it != d->watchers.end()) {
213 for (TQT_DBusConnectionPrivate::WatcherList::iterator wit = list.begin(); wit != list.end();
214 ++wit)
215 {
216 if ((*wit).watch == watch) {
217 bool enabled = dbus_watch_get_enabled(watch);
218 int flags = dbus_watch_get_flags(watch);
219
220// tqDebug("toggle watch %d to %d (write: %d, read: %d)",
221// dbus_watch_get_unix_fd(watch), enabled,
222// flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);
223
224 if (flags & DBUS_WATCH_READABLE && (*wit).read)
225 (*wit).read->setEnabled(enabled);
226 if (flags & DBUS_WATCH_WRITABLE && (*wit).write)
227 (*wit).write->setEnabled(enabled);
228 return;
229 }
230 }
231 }
232}
233
234static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
235{
236 Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c);
237
238 tqDebug("SERVER: GOT A NEW CONNECTION"); // TODO
239}
240
241static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection,
242 DBusMessage *message, void *data)
243{
244 Q_ASSERT(data);
245 Q_UNUSED(connection);
246
249 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
250
251 int msgType = dbus_message_get_type(message);
252 bool handled = false;
253
254 //TQT_DBusMessage amsg = TQT_DBusMessage::fromDBusMessage(message);
255 //tqDebug() << "got message: " << dbus_message_get_type(message) << amsg;
256
257 if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
258 handled = d->handleSignal(message);
259 } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
260 handled = d->handleObjectCall(message);
261 }
262
263 return handled ? DBUS_HANDLER_RESULT_HANDLED :
264 DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
265}
266
268{
269 // FIXME-QT4 int tp = messageMetaType = qRegisterMetaType<TQT_DBusMessage>("TQT_DBusMessage");
270 int tp = 0;
271 return tp;
272}
273
275 : TQObject(parent), ref(1), mode(InvalidMode), connection(0), server(0),
276 dispatcher(0), inDispatch(false)
277{
278 static const int msgType = registerMessageMetaType();
279 Q_UNUSED(msgType);
280
281 dbus_error_init(&error);
282
283 dispatcher = new TQTimer(this);
284 TQObject::connect(dispatcher, TQ_SIGNAL(timeout()), this, TQ_SLOT(dispatch()));
285
286 m_resultEmissionQueueTimer = new TQTimer(this);
287 TQObject::connect(m_resultEmissionQueueTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(transmitResultEmissionQueue()));
288 m_messageEmissionQueueTimer = new TQTimer(this);
289 TQObject::connect(m_messageEmissionQueueTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(transmitMessageEmissionQueue()));
290}
291
293{
294 for (PendingCallMap::iterator it = pendingCalls.begin(); it != pendingCalls.end();)
295 {
296 PendingCallMap::iterator copyIt = it;
297 ++it;
298 dbus_pending_call_cancel(copyIt.key());
299 dbus_pending_call_unref(copyIt.key());
300 delete copyIt.data();
301 pendingCalls.erase(copyIt);
302 }
303
304 if (dbus_error_is_set(&error))
305 dbus_error_free(&error);
306
308}
309
311{
312 ConnectionMode oldMode = mode;
313 mode = InvalidMode; // prevent reentrancy
314 if (oldMode == ServerMode) {
315 if (server) {
316 dbus_server_disconnect(server);
317 dbus_server_unref(server);
318 server = 0;
319 }
320 } else if (oldMode == ClientMode) {
321 if (connection) {
322 // closing shared connections is forbidden
323#if 0
324 dbus_connection_close(connection);
325 // send the "close" message
326 while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
327#endif
328 dbus_connection_unref(connection);
329 connection = 0;
330 }
331 }
332}
333
335{
337 if (dbus_error_is_set(&error))
338 dbus_error_free(&error);
339 return lastError.isValid();
340}
341
343{
344 bool res = true;
345 WatcherHash::iterator it = watchers.begin();
346 while (it != watchers.end())
347 {
348 WatcherList &list = *it;
349 WatcherList::iterator listIt = list.begin();
350 while (listIt != list.end())
351 {
352 Watcher watcher = *listIt;
353 if (watcher.read)
354 {
355 socketRead(watcher.read->socket());
356 res &= (!handleError());
357 }
358 ++listIt;
359 }
360 ++it;
361 }
362 return res;
363}
364
366{
367 emit dbusPendingCallReply(message);
368}
369
371{
372 // Yay, now that we have an application we are in business
373 // Re-add all watchers
374 WatcherHash oldWatchers = watchers;
375 watchers.clear();
376 // FIXME-QT4 TQHashIterator<int, TQT_DBusConnectionPrivate::Watcher> it(oldWatchers);
377 for (WatcherHash::const_iterator it = oldWatchers.begin(); it != oldWatchers.end(); ++it)
378 {
379 const WatcherList& list = *it;
380 for (WatcherList::const_iterator wit = list.begin(); wit != list.end(); ++wit)
381 {
382 if (!(*wit).read && !(*wit).write) {
383 qDBusAddWatch((*wit).watch, this);
384 }
385 }
386 }
387
388 // Re-add all timeouts
389 while (!pendingTimeouts.isEmpty()) {
390 qDBusAddTimeout(pendingTimeouts.first(), this);
391 pendingTimeouts.pop_front();
392 }
393}
394
396{
397 // FIXME-QT4 TQHashIterator<int, TQT_DBusConnectionPrivate::Watcher> it(watchers);
398 WatcherHash::const_iterator it = watchers.find(fd);
399 if (it != watchers.end()) {
400 const WatcherList& list = *it;
401 for (WatcherList::const_iterator wit = list.begin(); wit != list.end(); ++wit) {
402 if ((*wit).read && (*wit).read->isEnabled()) {
403 if (!dbus_watch_handle((*wit).watch, DBUS_WATCH_READABLE))
404 tqDebug("OUT OF MEM");
405 }
406 }
407 }
408 if (mode == ClientMode)
410}
411
413{
414 // FIXME-QT4 TQHashIterator<int, TQT_DBusConnectionPrivate::Watcher> it(watchers);
415 WatcherHash::const_iterator it = watchers.find(fd);
416 if (it != watchers.end()) {
417 const WatcherList& list = *it;
418 for (WatcherList::const_iterator wit = list.begin(); wit != list.end(); ++wit) {
419 if ((*wit).write && (*wit).write->isEnabled()) {
420 if (!dbus_watch_handle((*wit).watch, DBUS_WATCH_WRITABLE))
421 tqDebug("OUT OF MEM");
422 }
423 }
424 }
425}
426
428{
429 //tqDebug("Object destroyed");
430 for (PendingCallMap::iterator it = pendingCalls.begin(); it != pendingCalls.end();)
431 {
432 TQObject* receiver = (TQObject*) it.data()->receiver;
433 if (receiver == object || receiver == 0)
434 {
435 PendingCallMap::iterator copyIt = it;
436 ++it;
437
438 dbus_pending_call_cancel(copyIt.key());
439 dbus_pending_call_unref(copyIt.key());
440 delete copyIt.data();
441 pendingCalls.erase(copyIt);
442 }
443 else
444 ++it;
445 }
446}
447
449{
450 if (removedWatches.isEmpty()) return;
451
452 WatcherList::iterator listIt = removedWatches.begin();
453 for (; listIt != removedWatches.end(); ++listIt)
454 {
455 delete (*listIt).read;
456 delete (*listIt).write;
457 }
458 removedWatches.clear();
459
460 uint count = 0;
461 WatcherHash::iterator it = watchers.begin();
462 while (it != watchers.end())
463 {
464 WatcherList& list = *it;
465 listIt = list.begin();
466 while (listIt != list.end())
467 {
468 if (!((*listIt).read) && !((*listIt).write))
469 {
470 listIt = list.erase(listIt);
471 ++count;
472 }
473 }
474
475 if (list.isEmpty())
476 {
477 WatcherHash::iterator copyIt = it;
478 ++it;
479 watchers.erase(copyIt);
480 }
481 else
482 ++it;
483 }
484}
485
487{
488 dispatcher->start(0);
489}
490
492{
493 // dbus_connection_dispatch will hang if called recursively
494 if (inDispatch) {
495 printf("[dbus-1-tqt] WARNING: Attempt to call dispatch() recursively was silently ignored to prevent lockup!\n\r"); fflush(stdout);
496 return;
497 }
498 inDispatch = true;
499
500 if (mode == ClientMode)
501 {
502 if (dbus_connection_dispatch(connection) != DBUS_DISPATCH_DATA_REMAINS)
503 {
504 // stop dispatch timer
505 dispatcher->stop();
506 }
507 }
508
509 inDispatch = false;
510}
511
513{
514 TQT_DBusConnectionPrivate::PendingMessagesForEmit::iterator pmfe;
515 pmfe = pendingMessages.begin();
516 while (pmfe != pendingMessages.end()) {
517 TQT_DBusMessage msg = *pmfe;
518 pmfe = pendingMessages.remove(pmfe);
519 dbusSignal(msg);
520 }
521}
522
524{
526
527 ObjectMap::iterator it = registeredObjects.find(msg.path());
528 if (it == registeredObjects.end())
529 return false;
530
531 return it.data()->handleMethodCall(msg);
532}
533
535{
537
538 // yes, it is a single "|" below...
539 // FIXME-QT4
540 //return handleSignal(TQString(), msg) | handleSignal(msg.path(), msg);
541
542 // If dbusSignal(msg) were called here, it could easily cause a lockup as it would enter the TQt3 event loop,
543 // which could result in arbitrary methods being called while still inside dbus_connection_dispatch.
544 // Instead, I enqueue the messages here for TQt3 event loop transmission after dbus_connection_dispatch is finished.
545 pendingMessages.append(msg);
546 if (!m_messageEmissionQueueTimer->isActive()) m_messageEmissionQueueTimer->start(0, true);
547
548 return true;
549}
550
551static dbus_int32_t server_slot = -1;
552
554{
555 if (!server) {
556 handleError();
557 return;
558 }
559
560 server = s;
562
563 dbus_server_allocate_data_slot(&server_slot);
564 if (server_slot < 0)
565 return;
566
567 dbus_server_set_watch_functions(server, qDBusAddWatch, qDBusRemoveWatch,
568 qDBusToggleWatch, this, 0); // ### check return type?
569 dbus_server_set_timeout_functions(server, qDBusAddTimeout, qDBusRemoveTimeout,
570 qDBusToggleTimeout, this, 0);
571 dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0);
572
573 dbus_server_set_data(server, server_slot, this, 0);
574}
575
577{
578 if (!dbc) {
579 handleError();
580 return;
581 }
582
583 connection = dbc;
585
586 dbus_connection_set_exit_on_disconnect(connection, false);
587 dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
588 qDBusToggleWatch, this, 0);
589 dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
590 qDBusToggleTimeout, this, 0);
591
592 dbus_bus_add_match(connection, "type='signal'", &error);
593 if (handleError()) {
595 return;
596 }
597
598 const char *service = dbus_bus_get_unique_name(connection);
599 if (service) {
600 TQCString filter;
601 filter += "destination='";
602 filter += service;
603 filter += "'";
604
605 dbus_bus_add_match(connection, filter.data(), &error);
606 if (handleError()) {
608 return;
609 }
610 } else {
611 tqWarning("TQT_DBusConnectionPrivate::SetConnection: Unable to get unique name");
612 }
613
614 dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
615
616 //tqDebug("unique name: %s", service);
617}
618
619static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
620{
621 //tqDebug("Pending Call Result received");
622 TQT_DBusConnectionPrivate* d = reinterpret_cast<TQT_DBusConnectionPrivate*>(user_data);
623 TQT_DBusConnectionPrivate::PendingCallMap::iterator it = d->pendingCalls.find(pending);
624
625 DBusMessage *dbusReply = dbus_pending_call_steal_reply(pending);
626
627 dbus_set_error_from_message(&d->error, dbusReply);
628 d->handleError();
629
630 if (it != d->pendingCalls.end())
631 {
633
634 TQT_DBusResultInfo dbusResult;
635 dbusResult.message = reply;
636 dbusResult.receiver = it.data()->receiver;
637 dbusResult.method = it.data()->method.data();
638 d->m_resultEmissionQueue.append(dbusResult);
640 }
641
642 dbus_message_unref(dbusReply);
643 dbus_pending_call_unref(pending);
644 delete it.data();
645
646 d->pendingCalls.erase(it);
647}
648
650 const char *method)
651{
652 if (!receiver || !method)
653 return 0;
654
655 if (!TQObject::connect(receiver, TQ_SIGNAL(destroyed(TQObject*)),
656 this, TQ_SLOT(objectDestroyed(TQObject*))))
657 return false;
658
659 DBusMessage *msg = message.toDBusMessage();
660 if (!msg)
661 return 0;
662
663 int msg_serial = 0;
664 DBusPendingCall *pending = 0;
665 if (dbus_connection_send_with_reply(connection, msg, &pending, message.timeout())) {
667 pcall->receiver = receiver;
668 pcall->method = method;
669 pcall->pending = pending;
670 pendingCalls.insert(pcall->pending, pcall);
671
672 dbus_pending_call_set_notify(pending, qDBusResultReceived, this, 0);
673
674 msg_serial = dbus_message_get_serial(msg);
675 }
676
677 dbus_message_unref(msg);
678 return msg_serial;
679}
680
682{
683 if (!connection) return;
684
685 dbus_connection_flush(connection);
686}
687
689{
690 if (!m_resultEmissionQueueTimer->isActive()) m_resultEmissionQueueTimer->start(0, true);
691}
692
694{
695 if (!m_resultEmissionQueue.isEmpty()) {
696 TQT_DBusResultInfoList::Iterator it;
697 it = m_resultEmissionQueue.begin();
698 while (it != m_resultEmissionQueue.end()) {
699 TQT_DBusResultInfo dbusResult = (*it);
700 m_resultEmissionQueue.remove(it);
701 it = m_resultEmissionQueue.begin();
702
703 TQObject::connect(this, TQ_SIGNAL(dbusPendingCallReply(const TQT_DBusMessage&)), dbusResult.receiver, dbusResult.method.data());
704 emitPendingCallReply(dbusResult.message);
705 TQObject::disconnect(this, TQ_SIGNAL(dbusPendingCallReply(const TQT_DBusMessage&)), dbusResult.receiver, dbusResult.method.data());
706 }
707 }
708}
709
710#include "tqdbusconnection_p.moc"
int m_value
Definition: tqdbusatomic.h:37
Atomic(int value)
bool deref()
TQT_DBusResultInfoList m_resultEmissionQueue
TQValueList< DBusTimeout * > pendingTimeouts
TQT_DBusConnectionPrivate(TQObject *parent=0)
void setConnection(DBusConnection *connection)
PendingMessagesForEmit pendingMessages
TQMap< int, WatcherList > WatcherHash
void objectDestroyed(TQObject *object)
void emitPendingCallReply(const TQT_DBusMessage &message)
void dbusSignal(const TQT_DBusMessage &message)
bool handleObjectCall(DBusMessage *message)
void setServer(DBusServer *server)
int sendWithReplyAsync(const TQT_DBusMessage &message, TQObject *receiver, const char *method)
void dbusPendingCallReply(const TQT_DBusMessage &message)
bool handleSignal(DBusMessage *msg)
Class for transporting D-Bus errors.
Definition: tqdbuserror.h:41
bool isValid() const
Returns whether the error object is valid.
A message converts and transports data over D-Bus.
int timeout() const
Returns the message's timeout.
TQString path() const
Returns the message's object path.
DBusMessage * toDBusMessage() const
Creates a raw D-Bus message from this TQt3-bindings message.
static TQT_DBusMessage fromDBusMessage(DBusMessage *dmsg)
Creates a TQt3-bindings message from the given raw D-Bus message.
TQT_DBusMessage message
static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
static void qDBusRemoveWatch(DBusWatch *watch, void *data)
static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
static void qDBusToggleWatch(DBusWatch *watch, void *data)
static dbus_int32_t server_slot