1/*
2 Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>
3 Copyright (C) 2011 Harald Sitter <sitter@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), Nokia Corporation
11 (or its successors, if any) and the KDE Free Qt Foundation, which shall
12 act as a proxy defined in Section 6 of version 3 of the license.
13
14 This library 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 GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#ifndef PHONONDEFS_P_H
24#define PHONONDEFS_P_H
25
26#include <QMetaType>
27#include "medianode_p.h"
28#include "phononpimpl_p.h"
29
30#define PHONON_CONCAT_HELPER_INTERNAL(x, y) x ## y
31#define PHONON_CONCAT_HELPER(x, y) PHONON_CONCAT_HELPER_INTERNAL(x, y)
32
33#define PHONON_PRIVATECLASS \
34protected: \
35 virtual bool aboutToDeleteBackendObject() override; \
36 virtual void createBackendObject() override; \
37 /**
38 * \internal
39 * After construction of the Iface object this method is called
40 * throughout the complete class hierarchy in order to set up the
41 * properties that were already set on the public interface.
42 *
43 * An example implementation could look like this:
44 * \code
45 * ParentClassPrivate::setupBackendObject();
46 * m_iface->setPropertyA(d->propertyA);
47 * m_iface->setPropertyB(d->propertyB);
48 * \endcode
49 */ \
50 void setupBackendObject();
51
52#define PHONON_PRIVATEABSTRACTCLASS \
53protected: \
54 virtual bool aboutToDeleteBackendObject() override; \
55 /**
56 * \internal
57 * After construction of the Iface object this method is called
58 * throughout the complete class hierarchy in order to set up the
59 * properties that were already set on the public interface.
60 *
61 * An example implementation could look like this:
62 * \code
63 * ParentClassPrivate::setupBackendObject();
64 * m_iface->setPropertyA(d->propertyA);
65 * m_iface->setPropertyB(d->propertyB);
66 * \endcode
67 */ \
68 void setupBackendObject();
69
70#define PHONON_ABSTRACTBASE_IMPL \
71PHONON_CLASSNAME::PHONON_CLASSNAME(PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) &dd, QObject *parent) \
72 : QObject(parent), \
73 MediaNode(dd) \
74{ \
75}
76
77#define PHONON_OBJECT_IMPL \
78PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
79 : QObject(parent), \
80 MediaNode(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)()) \
81{ \
82} \
83void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \
84{ \
85 if (m_backendObject) \
86 return; \
87 P_Q(PHONON_CLASSNAME); \
88 m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \
89 if (m_backendObject) { \
90 setupBackendObject(); \
91 } \
92}
93
94#define PHONON_HEIR_IMPL(parentclass) \
95PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
96 : parentclass(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private), parent) \
97{ \
98} \
99void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \
100{ \
101 if (m_backendObject) \
102 return; \
103 P_Q(PHONON_CLASSNAME); \
104 m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \
105 if (m_backendObject) { \
106 setupBackendObject(); \
107 } \
108}
109
110#define BACKEND_GET(returnType, returnVar, methodName) \
111QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
112#define BACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
113QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1))
114#define BACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \
115QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2))
116#define BACKEND_CALL(methodName) \
117QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection)
118#define BACKEND_CALL1(methodName, varType1, var1) \
119QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
120#define BACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
121QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
122
123#define pBACKEND_GET(returnType, returnVar, methodName) \
124QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
125#define pBACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
126QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1))
127#define pBACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \
128QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2))
129#define pBACKEND_CALL(methodName) \
130QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection)
131#define pBACKEND_CALL1(methodName, varType1, var1) \
132QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
133#define pBACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
134QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
135
136namespace Phonon
137{
138 namespace
139 {
140 class NoIface;
141
142 /// All template arguments are valid
143 template<typename T> struct IsValid { enum { Result = true }; };
144 /// except NoIface
145 template<> struct IsValid<NoIface> { enum { Result = false }; };
146
147 template<class T> inline T my_cast(QObject *o) { return qobject_cast<T>(o); }
148 template<class T> inline T my_cast(const QObject *o) { return qobject_cast<T>(o); }
149
150 template<> inline NoIface *my_cast<NoIface *>(QObject *) { return nullptr; }
151 template<> inline NoIface *my_cast<NoIface *>(const QObject *) { return nullptr; }
152 } // anonymous namespace
153
154 /**
155 * \internal
156 *
157 * \brief Helper class to cast the backend object to the correct version of the interface.
158 *
159 * Additions to the backend interfaces cannot be done by adding virtual methods as that would
160 * break the binary interface. So the old class is renamed and a new class with the old name
161 * inheriting the old class is added, containing all the new virtual methods.
162 * Example:
163 * \code
164 class FooInterface
165 {
166 public:
167 virtual ~FooInterface() {}
168 virtual oldMethod() = 0;
169 };
170 Q_DECLARE_INTERFACE(FooInterface, "FooInterface0.phonon.kde.org")
171 * \endcode
172 * becomes
173 * \code
174 class FooInterface0
175 {
176 public:
177 virtual ~FooInterface0() {}
178 virtual oldMethod() = 0;
179 };
180 class FooInterface : public FooInterface0
181 {
182 public:
183 virtual newMethod() = 0;
184 };
185 Q_DECLARE_INTERFACE(FooInterface0, "FooInterface0.phonon.kde.org")
186 Q_DECLARE_INTERFACE(FooInterface, "FooInterface1.phonon.kde.org")
187 * \endcode
188 *
189 * With this, backends compiled against the old header can be qobject_casted to FooInterface0,
190 * but not to FooInterface. On the other hand backends compiled against the new header (they first
191 * need to implement newMethod) can only be qobject_casted to FooInterface but not to
192 * FooInterface0. (The qobject_cast relies on the string in Q_DECLARE_INTERFACE and not the
193 * class name which is why it behaves that way.)
194 *
195 * Now, in order to call oldMethod, the code needs to try to cast to both FooInterface and
196 * FooInterface0 (new backends will work with the former, old backends with the latter) and then
197 * if one of them in non-zero call oldMethod on it.
198 *
199 * To call newMethod only a cast to FooInterface needs to be done.
200 *
201 * The Iface class does all this for you for up to three (for now) interface revisions. Just
202 * create an object like this:
203 * \code
204 Iface<FooInterface0, FooInterface> iface0(d);
205 if (iface0) {
206 iface0->oldMethod();
207 }
208 Iface<FooInterface> iface(d);
209 if (iface) {
210 iface->newMethod();
211 }
212 * \endcode
213 *
214 * This becomes a bit more convenient if you add macros like this:
215 * \code
216 #define IFACES1 FooInterface
217 #define IFACES0 FooInterface0, IFACES1
218 * \endcode
219 * which you can use like this:
220 * \code
221 Iface<IFACES0> iface0(d);
222 if (iface0) {
223 iface0->oldMethod();
224 }
225 Iface<IFACES1> iface(d);
226 if (iface) {
227 iface->newMethod();
228 }
229 * \endcode
230 * With the next revision you can then change the macros to
231 * \code
232 #define IFACES2 FooInterface
233 #define IFACES1 FooInterface1, IFACES2
234 #define IFACES0 FooInterface0, IFACES1
235 * \endcode
236 *
237 * \author Matthias Kretz <kretz@kde.org>
238 */
239 template<class T0, class T1 = NoIface, class T2 = NoIface, class T3 = NoIface, class T4 = NoIface>
240 class Iface
241 {
242 public:
243 static inline T0 *cast(MediaNodePrivate *const d)
244 {
245 if (IsValid<T1>::Result) {
246 T0 *ret;
247 if (IsValid<T2>::Result) {
248 ret = reinterpret_cast<T0 *>(my_cast<T2 *>(d->m_backendObject));
249 if (ret) return ret;
250 if (IsValid<T3>::Result) {
251 ret = reinterpret_cast<T0 *>(my_cast<T3 *>(d->m_backendObject));
252 if (ret) return ret;
253 if (IsValid<T4>::Result) {
254 ret = reinterpret_cast<T0 *>(my_cast<T4 *>(d->m_backendObject));
255 if (ret) return ret;
256 }
257 }
258 }
259 ret = reinterpret_cast<T0 *>(my_cast<T1 *>(d->m_backendObject));
260 if (ret) return ret;
261 }
262 return qobject_cast<T0 *>(d->m_backendObject);
263 }
264
265 static inline const T0 *cast(const MediaNodePrivate *const d)
266 {
267 if (IsValid<T1>::Result) {
268 const T0 *ret;
269 if (IsValid<T2>::Result) {
270 ret = reinterpret_cast<const T0 *>(my_cast<T2 *>(d->m_backendObject));
271 if (ret) return ret;
272 if (IsValid<T3>::Result) {
273 ret = reinterpret_cast<const T0 *>(my_cast<T3 *>(d->m_backendObject));
274 if (ret) return ret;
275 if (IsValid<T4>::Result) {
276 ret = reinterpret_cast<const T0 *>(my_cast<T4 *>(d->m_backendObject));
277 if (ret) return ret;
278 }
279 }
280 }
281 ret = reinterpret_cast<const T0 *>(my_cast<T1 *>(d->m_backendObject));
282 if (ret) return ret;
283 }
284 return qobject_cast<T0 *>(d->m_backendObject);
285 }
286
287 inline Iface(MediaNodePrivate *const d) : iface(cast(d)) {}
288 inline operator T0 *() { return iface; }
289 inline operator const T0 *() const { return iface; }
290 inline T0 *operator->() { Q_ASSERT(iface); return iface; }
291 inline const T0 *operator->() const { Q_ASSERT(iface); return iface; }
292 private:
293 T0 *const iface;
294 };
295
296 template<class T0, class T1 = NoIface, class T2 = NoIface, class T3 = NoIface, class T4 = NoIface>
297 class ConstIface
298 {
299 public:
300 inline ConstIface(const MediaNodePrivate *const d) : iface(Iface<T0, T1, T2, T3, T4>::cast(d)) {}
301 inline operator const T0 *() const { return iface; }
302 inline const T0 *operator->() const { Q_ASSERT(iface); return iface; }
303 private:
304 const T0 *const iface;
305 };
306} // namespace Phonon
307
308#define INTERFACE_CALL(function) \
309Iface<PHONON_INTERFACENAME >::cast(d)->function
310
311#define pINTERFACE_CALL(function) \
312Iface<PHONON_INTERFACENAME >::cast(this)->function
313
314#define PHONON_GETTER(rettype, name, retdefault) \
315rettype PHONON_CLASSNAME::name() const \
316{ \
317 const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
318 if (!d->m_backendObject) \
319 return retdefault; \
320 rettype ret; \
321 BACKEND_GET(rettype, ret, #name); \
322 return ret; \
323}
324
325#define PHONON_INTERFACE_GETTER(rettype, name, retdefault) \
326rettype PHONON_CLASSNAME::name() const \
327{ \
328 const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
329 if (!d->m_backendObject) \
330 return retdefault; \
331 return Iface<PHONON_INTERFACENAME >::cast(d)->name(); \
332}
333
334#define PHONON_GETTER1(rettype, name, retdefault, argtype1, argvar1) \
335rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \
336{ \
337 const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
338 if (!d->m_backendObject) \
339 return retdefault; \
340 rettype ret; \
341 BACKEND_GET1(rettype, ret, #name, const QObject *, argvar1->k_ptr->backendObject()); \
342 return ret; \
343}
344
345#define PHONON_INTERFACE_GETTER1(rettype, name, retdefault, argtype1, argvar1) \
346rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \
347{ \
348 const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
349 if (!d->m_backendObject) \
350 return retdefault; \
351 return Iface<PHONON_INTERFACENAME >::cast(d)->name(argvar1->k_ptr->backendObject()); \
352}
353
354#define PHONON_SETTER(functionname, privatevar, argtype1) \
355void PHONON_CLASSNAME::functionname(argtype1 x) \
356{ \
357 PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
358 d->privatevar = x; \
359 if (k_ptr->backendObject()) { \
360 BACKEND_CALL1(#functionname, argtype1, x); \
361 } \
362}
363
364#define PHONON_INTERFACE_SETTER(functionname, privatevar, argtype1) \
365void PHONON_CLASSNAME::functionname(argtype1 x) \
366{ \
367 PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
368 d->privatevar = x; \
369 if (k_ptr->backendObject()) { \
370 Iface<PHONON_INTERFACENAME >::cast(d)->functionname(x); \
371 } \
372}
373
374#ifndef METATYPE_QLIST_INT_DEFINED
375#define METATYPE_QLIST_INT_DEFINED
376// Want this exactly once, see phonondefs_p.h kcm/outputdevicechoice.cpp
377Q_DECLARE_METATYPE(QList<int>)
378#endif
379
380#endif // PHONONDEFS_P_H
381

source code of phonon/phonon/phonondefs_p.h