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 \ |
34 | protected: \ |
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 \ |
53 | protected: \ |
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 \ |
71 | PHONON_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 \ |
78 | PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \ |
79 | : QObject(parent), \ |
80 | MediaNode(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)()) \ |
81 | { \ |
82 | } \ |
83 | void 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) \ |
95 | PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \ |
96 | : parentclass(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private), parent) \ |
97 | { \ |
98 | } \ |
99 | void 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) \ |
111 | QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar)) |
112 | #define BACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \ |
113 | QMetaObject::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) \ |
115 | QMetaObject::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) \ |
117 | QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection) |
118 | #define BACKEND_CALL1(methodName, varType1, var1) \ |
119 | QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1)) |
120 | #define BACKEND_CALL2(methodName, varType1, var1, varType2, var2) \ |
121 | QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2)) |
122 | |
123 | #define pBACKEND_GET(returnType, returnVar, methodName) \ |
124 | QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar)) |
125 | #define pBACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \ |
126 | QMetaObject::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) \ |
128 | QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2)) |
129 | #define pBACKEND_CALL(methodName) \ |
130 | QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection) |
131 | #define pBACKEND_CALL1(methodName, varType1, var1) \ |
132 | QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1)) |
133 | #define pBACKEND_CALL2(methodName, varType1, var1, varType2, var2) \ |
134 | QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2)) |
135 | |
136 | namespace 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) \ |
309 | Iface<PHONON_INTERFACENAME >::cast(d)->function |
310 | |
311 | #define pINTERFACE_CALL(function) \ |
312 | Iface<PHONON_INTERFACENAME >::cast(this)->function |
313 | |
314 | #define PHONON_GETTER(rettype, name, retdefault) \ |
315 | rettype 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) \ |
326 | rettype 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) \ |
335 | rettype 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) \ |
346 | rettype 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) \ |
355 | void 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) \ |
365 | void 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 |
377 | Q_DECLARE_METATYPE(QList<int>) |
378 | #endif |
379 | |
380 | #endif // PHONONDEFS_P_H |
381 | |