1 /****************************************************************************
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
5 ** This file is part of the QxtNetwork module of the Qt eXTension library
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
21 ** <http://libqxt.sourceforge.net> <foundation@libqxt.org>
23 ****************************************************************************/
25 #include "signalproxy.h"
28 #include <QAbstractSocket>
34 #include <QMetaMethod>
36 class ClassIntrospector: public QObject {
37 // This class MANUALLY implements the necessary parts of QObject.
38 // Do NOT add the Q_OBJECT macro. As this class isn't intended
39 // for direct use, it doesn't offer any sort of useful meta-object.
41 ClassIntrospector(SignalProxy* parent, QObject* source);
42 int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
44 void attachSignal(int methodId, const QByteArray &func);
49 QMultiHash<int, QByteArray> rpcFunction;
52 ClassIntrospector::ClassIntrospector(SignalProxy* parent, QObject* source)
57 QObject::connect(source, SIGNAL(destroyed()), parent, SLOT(detachSender()));
60 int ClassIntrospector::qt_metacall(QMetaObject::Call _c, int _id, void **_a) {
61 _id = QObject::qt_metacall(_c, _id, _a);
64 if(_c == QMetaObject::InvokeMetaMethod) {
65 if(rpcFunction.contains(_id)) {
66 const QList<int> &argTypes = proxy->argTypes(caller, _id);
68 int n = argTypes.size();
69 for(int i=0; i<n; i++)
70 params.append(QVariant(argTypes[i], _a[i+1]));
71 QMultiHash<int, QByteArray>::const_iterator funcIter = rpcFunction.constFind(_id);
72 while(funcIter != rpcFunction.constEnd() && funcIter.key() == _id) {
73 proxy->call(funcIter.value(), params);
82 void ClassIntrospector::attachSignal(int methodId, const QByteArray &func) {
83 // we ride without safetybelts here... all checking for valid method etc pp has to be done by the caller
84 // all connected methodIds are offset by the standard methodCount of QObject
85 if(!rpcFunction.contains(methodId))
86 QMetaObject::connect(caller, methodId, this, QObject::staticMetaObject.methodCount() + methodId);
90 fn = QMetaObject::normalizedSignature(func);
92 fn = QByteArray("2") + caller->metaObject()->method(methodId).signature();
94 rpcFunction.insert(methodId, fn);
96 // ====================
98 // ====================
101 // ====================
103 // ====================
104 SignalProxy::SignalProxy(QObject* parent)
111 SignalProxy::SignalProxy(RPCTypes type, QObject* parent)
118 SignalProxy::SignalProxy(RPCTypes type, QIODevice* device, QObject* parent)
126 SignalProxy::~SignalProxy() {
127 QList<QObject*> senders = _specHash.keys();
128 foreach(QObject* sender, senders)
129 detachObject(sender);
132 void SignalProxy::setRPCType(RPCTypes type) {
133 foreach(QIODevice* peer, _peerByteCount.keys()) {
135 qWarning() << "SignalProxy: Cannot change RPC types while connected";
143 SignalProxy::RPCTypes SignalProxy::rpcType() const {
144 return (RPCTypes)(_rpcType);
147 bool SignalProxy::maxPeersReached() {
148 if(_peerByteCount.empty())
150 if(rpcType() != Server)
152 if(_maxClients == -1)
155 return (_maxClients <= _peerByteCount.count());
158 bool SignalProxy::addPeer(QIODevice* iodev) {
162 if(_peerByteCount.contains(iodev))
165 if(maxPeersReached()) {
166 qWarning("SignalProxy: max peercount reached");
171 qWarning("SignalProxy::the device you passed is not open!");
173 connect(iodev, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
174 connect(iodev, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
176 QAbstractSocket* sock = qobject_cast<QAbstractSocket*>(iodev);
178 connect(sock, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
181 _peerByteCount[iodev] = 0;
183 if(_peerByteCount.count() == 1)
189 void SignalProxy::removePeerBySender() {
190 // OK we're brutal here... but since it's a private slot we know what we've got connected to it...
191 QIODevice *ioDev = (QIODevice *)(sender());
195 void SignalProxy::removePeer(QIODevice* iodev) {
196 if(_peerByteCount.isEmpty()) {
197 qWarning() << "No Peers in use!";
201 if(_rpcType == Server && !iodev) {
203 QList<QIODevice *> peers = _peerByteCount.keys();
204 foreach(QIODevice *peer, peers)
208 if(_rpcType != Server && !iodev)
209 iodev = _peerByteCount.keys().first();
213 if(!_peerByteCount.contains(iodev)) {
214 qWarning() << "SignalProxy: unknown QIODevice" << iodev;
221 if(readDataFromDevice(iodev, _peerByteCount[iodev], var))
222 receivePeerSignal(var);
226 _peerByteCount.remove(iodev);
228 disconnect(iodev, 0, this, 0);
229 emit peerRemoved(iodev);
231 if(_peerByteCount.isEmpty())
235 void SignalProxy::setArgTypes(QObject* obj, int methodId) {
236 QList<QByteArray> p = obj->metaObject()->method(methodId).parameterTypes();
239 for(int i=0; i<ct; i++)
240 argTypes.append(QMetaType::type(p.value(i)));
242 const QByteArray &className(obj->metaObject()->className());
243 Q_ASSERT(_classInfo.contains(className));
244 Q_ASSERT(!_classInfo[className]->argTypes.contains(methodId));
245 _classInfo[className]->argTypes[methodId] = argTypes;
248 const QList<int> &SignalProxy::argTypes(QObject *obj, int methodId) {
249 const QByteArray &className(obj->metaObject()->className());
250 Q_ASSERT(_classInfo.contains(className));
251 if(!_classInfo[className]->argTypes.contains(methodId))
252 setArgTypes(obj, methodId);
253 return _classInfo[className]->argTypes[methodId];
256 void SignalProxy::setMethodName(QObject *obj, int methodId) {
257 const QByteArray &className(obj->metaObject()->className());
258 QByteArray method = obj->metaObject()->method(methodId).signature();
259 method = method.left(method.indexOf('('));
261 Q_ASSERT(_classInfo.contains(className));
262 Q_ASSERT(!_classInfo[className]->methodNames.contains(methodId));
263 _classInfo[className]->methodNames[methodId] = method;
266 const QByteArray &SignalProxy::methodName(QObject *obj, int methodId) {
267 QByteArray className(obj->metaObject()->className());
268 Q_ASSERT(_classInfo.contains(className));
269 if(!_classInfo[className]->methodNames.contains(methodId))
270 setMethodName(obj, methodId);
271 return _classInfo[className]->methodNames[methodId];
275 void SignalProxy::createClassInfo(QObject *obj) {
276 QByteArray className(obj->metaObject()->className());
277 if(!_classInfo.contains(className))
278 _classInfo[className] = new ClassInfo();
281 bool SignalProxy::attachSignal(QObject* sender, const char* signal, const QByteArray& rpcFunction) {
282 const QMetaObject* meta = sender->metaObject();
283 QByteArray sig(meta->normalizedSignature(signal).mid(1));
284 int methodId = meta->indexOfMethod(sig.constData());
285 if(methodId == -1 || meta->method(methodId).methodType() != QMetaMethod::Signal) {
286 qWarning() << "SignalProxy::attachSignal: No such signal " << signal;
290 createClassInfo(sender);
292 ClassIntrospector* spec;
293 if(_specHash.contains(sender))
294 spec = _specHash[sender];
296 spec = _specHash[sender] = new ClassIntrospector(this, sender);
298 spec->attachSignal(methodId, rpcFunction);
304 bool SignalProxy::attachSlot(const QByteArray& rpcFunction, QObject* recv, const char* slot) {
305 const QMetaObject* meta = recv->metaObject();
306 int methodId = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
307 if(methodId == -1 || meta->method(methodId).methodType() == QMetaMethod::Method) {
308 qWarning() << "SignalProxy::attachSlot: No such slot " << slot;
312 createClassInfo(recv);
314 QByteArray funcName = QMetaObject::normalizedSignature(rpcFunction.constData());
315 _attachedSlots.insert(funcName, qMakePair(recv, methodId));
317 QObject::disconnect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
318 QObject::connect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
323 void SignalProxy::detachSender() {
324 // this is a slot so we can bypass the QueuedConnection
325 _detachSignals(sender());
326 _detachSlots(sender());
329 // detachObject/Signals/Slots() can be called as a result of an incomming call
330 // this might destroy our the iterator used for delivery
331 // thus we wrap the actual disconnection by using QueuedConnections
332 void SignalProxy::detachObject(QObject* obj) {
337 void SignalProxy::detachSignals(QObject* sender) {
338 QMetaObject::invokeMethod(this, "_detachSignals",
339 Qt::QueuedConnection,
340 Q_ARG(QObject*, sender));
343 void SignalProxy::_detachSignals(QObject* sender) {
344 if(!_specHash.contains(sender))
346 _specHash.take(sender)->deleteLater();
349 void SignalProxy::detachSlots(QObject* receiver) {
350 QMetaObject::invokeMethod(this, "_detachSlots",
351 Qt::QueuedConnection,
352 Q_ARG(QObject*, receiver));
355 void SignalProxy::_detachSlots(QObject* receiver) {
356 SlotHash::iterator slotIter = _attachedSlots.begin();
357 while(slotIter != _attachedSlots.end()) {
358 if(slotIter.value().first == receiver) {
359 slotIter = _attachedSlots.erase(slotIter);
366 void SignalProxy::call(const char* signal , QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9) {
367 QByteArray sig=QMetaObject::normalizedSignature(signal);
369 params << p1 << p2 << p3 << p4 << p5
370 << p6 << p7 << p8 << p9;
374 void SignalProxy::call(const QByteArray &funcName, const QVariantList ¶ms) {
375 QVariantList packedFunc;
376 packedFunc << funcName;
377 packedFunc << params;
378 foreach(QIODevice* dev, _peerByteCount.keys())
379 writeDataToDevice(dev, QVariant(packedFunc));
382 void SignalProxy::receivePeerSignal(const QVariant &packedFunc) {
383 QVariantList params(packedFunc.toList());
384 QByteArray funcName = params.takeFirst().toByteArray();
385 int numParams, methodId;
388 SlotHash::const_iterator slot = _attachedSlots.constFind(funcName);
389 while(slot != _attachedSlots.constEnd() && slot.key() == funcName) {
390 receiver = (*slot).first;
391 methodId = (*slot).second;
392 numParams = argTypes(receiver, methodId).count();
393 QGenericArgument args[9];
394 for(int i = 0; i < numParams; i++)
395 args[i] = QGenericArgument(params[i].typeName(), params[i].constData());
396 if(!QMetaObject::invokeMethod(receiver, methodName(receiver, methodId),
397 args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])) {
398 qWarning("SignalProxy::receivePeerSignal: invokeMethod for \"%s\" failed ", methodName(receiver, methodId).constData());
404 void SignalProxy::dataAvailable() {
405 QIODevice* ioDev = qobject_cast<QIODevice* >(sender());
407 if(!_peerByteCount.contains(ioDev)) {
408 qWarning() << "SignalProxy: Unrecognized client object connected to dataAvailable";
412 while(readDataFromDevice(ioDev, _peerByteCount[ioDev], var))
413 receivePeerSignal(var);
417 void SignalProxy::writeDataToDevice(QIODevice *dev, const QVariant &item) {
418 QAbstractSocket* sock = qobject_cast<QAbstractSocket*>(dev);
419 if(!dev->isOpen() || (sock && sock->state()!=QAbstractSocket::ConnectedState)) {
420 qWarning("can't call on a closed device");
424 QDataStream out(&block, QIODevice::WriteOnly);
425 out.setVersion(QDataStream::Qt_4_2);
426 out << (quint32)0 << item;
427 out.device()->seek(0);
428 out << (quint32)(block.size() - sizeof(quint32));
432 bool SignalProxy::readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item) {
434 in.setVersion(QDataStream::Qt_4_2);
437 if(dev->bytesAvailable() < (int)sizeof(quint32)) return false;
441 if(dev->bytesAvailable() < blockSize)