1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSystems module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33#include "qsignalintercepter_p.h"
34#include <qmetaobject.h>
35#include <qmetatype.h>
36#include <qobjectdefs.h>
37#include <qdebug.h>
38
39#include <stdlib.h>
40
41/*!
42 \class QSignalIntercepter
43 \inpublicgroup QtBaseModule
44 \internal
45
46 \brief The QSignalIntercepter class provides an interface for intercepting signals as meta-calls
47
48 IPC mechanisms need to intercept signals and convert them into protocol
49 messages, but it is generally impractical to create a slot for every
50 signal that needs to be dispatched. The QSignalIntercepter class allows
51 signals to be intercepted as meta-calls so that IPC dispatching can
52 be implemented in a generic fashion.
53
54 The activated() method is called whenever the signal is emitted,
55 with the arguments in a typed list.
56
57 \sa QSlotInvoker
58
59 \ingroup objectmodel
60*/
61
62QT_BEGIN_NAMESPACE
63
64class QSignalIntercepterPrivate
65{
66public:
67 QObject *sender;
68 QByteArray signal;
69 int signalIndex;
70 int destroyIndex;
71 int slotIndex;
72 int *types;
73 int numArgs;
74
75 ~QSignalIntercepterPrivate()
76 {
77 if ( types )
78 free(ptr: types);
79 }
80};
81
82/*!
83 Create a new signal intercepter which traps \a signal on \a sender.
84 The object will be attached to \a parent, if present.
85*/
86QSignalIntercepter::QSignalIntercepter
87 ( QObject *sender, const QByteArray& signal, QObject *parent )
88 : QObject( parent )
89{
90 // Initialize the private members.
91 d = new QSignalIntercepterPrivate();
92 d->sender = sender;
93 d->signal = signal;
94 d->signalIndex = -1;
95 d->destroyIndex = -1;
96 d->slotIndex = -1;
97 d->types = 0;
98
99 // Resolve the indices of the signals we are interested in.
100 if ( sender && signal.size() > 0 ) {
101 // '2' is QSIGNAL_CODE in Qt 4.4 and below,
102 // '6' is QSIGNAL_CODE in Qt 4.5 and higher.
103 if ( signal[0] != '2' && signal[0] != '6' ) {
104 qWarning( msg: "QSignalIntercepter: `%s' is not a valid signal "
105 "specification", signal.constData() );
106 return;
107 }
108 QByteArray name = QMetaObject::normalizedSignature
109 ( method: signal.constData() + 1 );
110 d->signalIndex
111 = sender->metaObject()->indexOfSignal( signal: name.constData() );
112 if ( d->signalIndex < 0 ) {
113 qWarning( msg: "QSignalIntercepter: no such signal: %s::%s",
114 sender->metaObject()->className(), signal.constData() );
115 return;
116 }
117 d->destroyIndex
118 = sender->metaObject()->indexOfSignal( signal: "destroyed()" );
119 d->types = connectionTypes( member: name, nargs&: d->numArgs );
120 }
121
122 // Derive a fake slot index to use in our manual qt_metacall implementation.
123 d->slotIndex = staticMetaObject.methodCount();
124
125 // Connect up the signals.
126 if ( d->signalIndex >= 0 ) {
127 QMetaObject::connect( sender, signal_index: d->signalIndex,
128 receiver: this, method_index: d->slotIndex,
129 type: Qt::DirectConnection, types: 0 );
130 }
131 if ( d->destroyIndex >= 0 ) {
132 QMetaObject::connect( sender, signal_index: d->destroyIndex,
133 receiver: this, method_index: d->slotIndex + 1,
134 type: Qt::DirectConnection, types: 0 );
135 }
136}
137
138/*!
139 Destroy a signal intercepter.
140*/
141QSignalIntercepter::~QSignalIntercepter()
142{
143 if ( d->signalIndex >= 0 ) {
144 QMetaObject::disconnect( sender: d->sender, signal_index: d->signalIndex,
145 receiver: this, method_index: d->slotIndex );
146 }
147 if ( d->destroyIndex >= 0 ) {
148 QMetaObject::disconnect( sender: d->sender, signal_index: d->destroyIndex,
149 receiver: this, method_index: d->slotIndex + 1 );
150 }
151 delete d;
152}
153
154/*!
155 Returns the sender that this signal interceptor is attached to.
156*/
157QObject *QSignalIntercepter::sender() const
158{
159 return d->sender;
160}
161
162/*!
163 Returns the name of the signal that this signal interceptor is attached to.
164*/
165QByteArray QSignalIntercepter::signal() const
166{
167 return d->signal;
168}
169
170/*!
171 Returns true if this signal intercepter is valid; that is, there was
172 a signal present with the specified parameters when this object
173 was constructed.
174*/
175bool QSignalIntercepter::isValid() const
176{
177 return ( d->signalIndex != -1 );
178}
179
180/*!
181 \internal
182*/
183int QSignalIntercepter::qt_metacall(QMetaObject::Call c, int id, void **a)
184{
185 id = QObject::qt_metacall(c, id, a);
186 if (id < 0)
187 return id;
188 if (c == QMetaObject::InvokeMetaMethod) {
189 switch (id) {
190 case 0: {
191 // The signal we are interested in has been activated.
192 if ( d->types ) {
193 QList<QVariant> args;
194 for ( int i = 0; i < d->numArgs; ++i ) {
195 if ( d->types[i] != QVariantId ) {
196 QVariant arg( d->types[i], a[i + 1] );
197 args.append( t: arg );
198 } else {
199 args.append( t: *((const QVariant *)( a[i + 1] )) );
200 }
201 }
202 activated( args );
203 }
204 }
205 break;
206
207 case 1: {
208 // The sender has been destroyed. Clear the signal indices
209 // so that we don't try to do a manual disconnect when our
210 // own destructor is called.
211 d->signalIndex = -1;
212 d->destroyIndex = -1;
213 }
214 break;
215 }
216 id -= 2;
217 }
218 return id;
219}
220
221/*!
222 \fn void QSignalIntercepter::activated( const QList<QVariant>& args )
223
224 Called when the signal that is being intercepted is activated.
225 The arguments to the signal are passed in the list \a args.
226*/
227
228// Get the QVariant type number for a type name.
229int QSignalIntercepter::typeFromName( const QByteArray& type )
230{
231 int id;
232 if (type.endsWith(c: '*'))
233 return QMetaType::VoidStar;
234 else if ( type.size() == 0 || type == "void" )
235 return QMetaType::Void;
236 id = QMetaType::type( typeName: type.constData() );
237 if (id == QMetaType::QVariant)
238 return QSignalIntercepter::QVariantId;
239 return id;
240}
241
242/*!
243 Returns the connection types associated with a signal or slot \a member
244 specification. The array of types is returned from this function,
245 and the number of arguments is returned in \a nargs. Returns null
246 if \a member is invalid. The return value must be freed with qFree().
247*/
248int *QSignalIntercepter::connectionTypes( const QByteArray& member, int& nargs )
249{
250 // Based on Qt's internal queuedConnectionTypes function.
251 nargs = 0;
252 int *types = 0;
253 const char *s = member.constData();
254 while (*s != '\0' && *s != '(') { ++s; }
255 if ( *s == '\0' )
256 return 0;
257 ++s;
258 const char *e = s;
259 while (*e != ')') {
260 ++e;
261 if (*e == ')' || *e == ',')
262 ++nargs;
263 }
264
265 types = (int *) malloc(size: (nargs + 1) * sizeof(int));
266 types[nargs] = 0;
267 for (int n = 0; n < nargs; ++n) {
268 e = s;
269 while (*s != ',' && *s != ')')
270 ++s;
271 QByteArray type(e, s-e);
272 ++s;
273
274 types[n] = typeFromName(type);
275 if (!types[n]) {
276 qWarning(msg: "QSignalIntercepter::connectionTypes: Cannot marshal arguments of type '%s'", type.data());
277 free(ptr: types);
278 return 0;
279 }
280 }
281 return types;
282}
283
284QT_END_NAMESPACE
285

source code of qtsystems/src/serviceframework/ipc/qsignalintercepter.cpp