File indexing completed on 2024-04-21 04:43:18

0001 /*
0002     Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>
0003     Copyright (C) 2011 Harald Sitter <sitter@kde.org>
0004 
0005     This library is free software; you can redistribute it and/or
0006     modify it under the terms of the GNU Lesser General Public
0007     License as published by the Free Software Foundation; either
0008     version 2.1 of the License, or (at your option) version 3, or any
0009     later version accepted by the membership of KDE e.V. (or its
0010     successor approved by the membership of KDE e.V.), Nokia Corporation
0011     (or its successors, if any) and the KDE Free Qt Foundation, which shall
0012     act as a proxy defined in Section 6 of version 3 of the license.
0013 
0014     This library is distributed in the hope that it will be useful,
0015     but WITHOUT ANY WARRANTY; without even the implied warranty of
0016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017     Lesser General Public License for more details.
0018 
0019     You should have received a copy of the GNU Lesser General Public
0020     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 
0023 #ifndef PHONONDEFS_P_H
0024 #define PHONONDEFS_P_H
0025 
0026 #include <QMetaType>
0027 #include "medianode_p.h"
0028 #include "phononpimpl_p.h"
0029 
0030 #define PHONON_CONCAT_HELPER_INTERNAL(x, y) x ## y
0031 #define PHONON_CONCAT_HELPER(x, y) PHONON_CONCAT_HELPER_INTERNAL(x, y)
0032 
0033 #define PHONON_PRIVATECLASS \
0034 protected: \
0035     virtual bool aboutToDeleteBackendObject() override; \
0036     virtual void createBackendObject() override; \
0037     /**
0038      * \internal
0039      * After construction of the Iface object this method is called
0040      * throughout the complete class hierarchy in order to set up the
0041      * properties that were already set on the public interface.
0042      *
0043      * An example implementation could look like this:
0044      * \code
0045      * ParentClassPrivate::setupBackendObject();
0046      * m_iface->setPropertyA(d->propertyA);
0047      * m_iface->setPropertyB(d->propertyB);
0048      * \endcode
0049      */ \
0050     void setupBackendObject();
0051 
0052 #define PHONON_PRIVATEABSTRACTCLASS \
0053 protected: \
0054     virtual bool aboutToDeleteBackendObject() override; \
0055     /**
0056      * \internal
0057      * After construction of the Iface object this method is called
0058      * throughout the complete class hierarchy in order to set up the
0059      * properties that were already set on the public interface.
0060      *
0061      * An example implementation could look like this:
0062      * \code
0063      * ParentClassPrivate::setupBackendObject();
0064      * m_iface->setPropertyA(d->propertyA);
0065      * m_iface->setPropertyB(d->propertyB);
0066      * \endcode
0067      */ \
0068     void setupBackendObject();
0069 
0070 #define PHONON_ABSTRACTBASE_IMPL \
0071 PHONON_CLASSNAME::PHONON_CLASSNAME(PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) &dd, QObject *parent) \
0072     : QObject(parent), \
0073     MediaNode(dd) \
0074 { \
0075 }
0076 
0077 #define PHONON_OBJECT_IMPL \
0078 PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
0079     : QObject(parent), \
0080     MediaNode(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)()) \
0081 { \
0082 } \
0083 void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \
0084 { \
0085     if (m_backendObject) \
0086         return; \
0087     P_Q(PHONON_CLASSNAME); \
0088     m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \
0089     if (m_backendObject) { \
0090         setupBackendObject(); \
0091     } \
0092 }
0093 
0094 #define PHONON_HEIR_IMPL(parentclass) \
0095 PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \
0096     : parentclass(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private), parent) \
0097 { \
0098 } \
0099 void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \
0100 { \
0101     if (m_backendObject) \
0102         return; \
0103     P_Q(PHONON_CLASSNAME); \
0104     m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \
0105     if (m_backendObject) { \
0106         setupBackendObject(); \
0107     } \
0108 }
0109 
0110 #define BACKEND_GET(returnType, returnVar, methodName) \
0111 QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
0112 #define BACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
0113 QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1))
0114 #define BACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \
0115 QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2))
0116 #define BACKEND_CALL(methodName) \
0117 QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection)
0118 #define BACKEND_CALL1(methodName, varType1, var1) \
0119 QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
0120 #define BACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
0121 QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
0122 
0123 #define pBACKEND_GET(returnType, returnVar, methodName) \
0124 QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar))
0125 #define pBACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \
0126 QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1))
0127 #define pBACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \
0128 QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2))
0129 #define pBACKEND_CALL(methodName) \
0130 QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection)
0131 #define pBACKEND_CALL1(methodName, varType1, var1) \
0132 QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1))
0133 #define pBACKEND_CALL2(methodName, varType1, var1, varType2, var2) \
0134 QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2))
0135 
0136 namespace Phonon
0137 {
0138     namespace
0139     {
0140         class NoIface;
0141 
0142         /// All template arguments are valid
0143         template<typename T> struct IsValid { enum { Result = true }; };
0144         /// except NoIface
0145         template<> struct IsValid<NoIface> { enum { Result = false }; };
0146 
0147         template<class T> inline T my_cast(QObject *o) { return qobject_cast<T>(o); }
0148         template<class T> inline T my_cast(const QObject *o) { return qobject_cast<T>(o); }
0149 
0150         template<> inline NoIface *my_cast<NoIface *>(QObject *) { return nullptr; }
0151         template<> inline NoIface *my_cast<NoIface *>(const QObject *) { return nullptr; }
0152     } // anonymous namespace
0153 
0154     /**
0155      * \internal
0156      *
0157      * \brief Helper class to cast the backend object to the correct version of the interface.
0158      *
0159      * Additions to the backend interfaces cannot be done by adding virtual methods as that would
0160      * break the binary interface. So the old class is renamed and a new class with the old name
0161      * inheriting the old class is added, containing all the new virtual methods.
0162      * Example:
0163      * \code
0164        class FooInterface
0165        {
0166        public:
0167            virtual ~FooInterface() {}
0168            virtual oldMethod() = 0;
0169        };
0170        Q_DECLARE_INTERFACE(FooInterface, "FooInterface0.phonon.kde.org")
0171      * \endcode
0172      * becomes
0173      * \code
0174        class FooInterface0
0175        {
0176        public:
0177            virtual ~FooInterface0() {}
0178            virtual oldMethod() = 0;
0179        };
0180        class FooInterface : public FooInterface0
0181        {
0182        public:
0183            virtual newMethod() = 0;
0184        };
0185        Q_DECLARE_INTERFACE(FooInterface0, "FooInterface0.phonon.kde.org")
0186        Q_DECLARE_INTERFACE(FooInterface,  "FooInterface1.phonon.kde.org")
0187      * \endcode
0188      *
0189      * With this, backends compiled against the old header can be qobject_casted to FooInterface0,
0190      * but not to FooInterface. On the other hand backends compiled against the new header (they first
0191      * need to implement newMethod) can only be qobject_casted to FooInterface but not to
0192      * FooInterface0. (The qobject_cast relies on the string in Q_DECLARE_INTERFACE and not the
0193      * class name which is why it behaves that way.)
0194      *
0195      * Now, in order to call oldMethod, the code needs to try to cast to both FooInterface and
0196      * FooInterface0 (new backends will work with the former, old backends with the latter) and then
0197      * if one of them in non-zero call oldMethod on it.
0198      *
0199      * To call newMethod only a cast to FooInterface needs to be done.
0200      *
0201      * The Iface class does all this for you for up to three (for now) interface revisions. Just
0202      * create an object like this:
0203      * \code
0204        Iface<FooInterface0, FooInterface> iface0(d);
0205        if (iface0) {
0206            iface0->oldMethod();
0207        }
0208        Iface<FooInterface> iface(d);
0209        if (iface) {
0210            iface->newMethod();
0211        }
0212      * \endcode
0213      *
0214      * This becomes a bit more convenient if you add macros like this:
0215      * \code
0216        #define IFACES1 FooInterface
0217        #define IFACES0 FooInterface0, IFACES1
0218      * \endcode
0219      * which you can use like this:
0220      * \code
0221        Iface<IFACES0> iface0(d);
0222        if (iface0) {
0223            iface0->oldMethod();
0224        }
0225        Iface<IFACES1> iface(d);
0226        if (iface) {
0227            iface->newMethod();
0228        }
0229      * \endcode
0230      * With the next revision you can then change the macros to
0231      * \code
0232        #define IFACES2 FooInterface
0233        #define IFACES1 FooInterface1, IFACES2
0234        #define IFACES0 FooInterface0, IFACES1
0235      * \endcode
0236      *
0237      * \author Matthias Kretz <kretz@kde.org>
0238      */
0239     template<class T0, class T1 = NoIface, class T2 = NoIface, class T3 = NoIface, class T4 = NoIface>
0240     class Iface
0241     {
0242     public:
0243         static inline T0 *cast(MediaNodePrivate *const d)
0244         {
0245             if (IsValid<T1>::Result) {
0246                 T0 *ret;
0247                 if (IsValid<T2>::Result) {
0248                     ret = reinterpret_cast<T0 *>(my_cast<T2 *>(d->m_backendObject));
0249                     if (ret) return ret;
0250                     if (IsValid<T3>::Result) {
0251                         ret = reinterpret_cast<T0 *>(my_cast<T3 *>(d->m_backendObject));
0252                         if (ret) return ret;
0253                         if (IsValid<T4>::Result) {
0254                             ret = reinterpret_cast<T0 *>(my_cast<T4 *>(d->m_backendObject));
0255                             if (ret) return ret;
0256                         }
0257                     }
0258                 }
0259                 ret = reinterpret_cast<T0 *>(my_cast<T1 *>(d->m_backendObject));
0260                 if (ret) return ret;
0261             }
0262             return qobject_cast<T0 *>(d->m_backendObject);
0263         }
0264 
0265         static inline const T0 *cast(const MediaNodePrivate *const d)
0266         {
0267             if (IsValid<T1>::Result) {
0268                 const T0 *ret;
0269                 if (IsValid<T2>::Result) {
0270                     ret = reinterpret_cast<const T0 *>(my_cast<T2 *>(d->m_backendObject));
0271                     if (ret) return ret;
0272                     if (IsValid<T3>::Result) {
0273                         ret = reinterpret_cast<const T0 *>(my_cast<T3 *>(d->m_backendObject));
0274                         if (ret) return ret;
0275                         if (IsValid<T4>::Result) {
0276                             ret = reinterpret_cast<const T0 *>(my_cast<T4 *>(d->m_backendObject));
0277                             if (ret) return ret;
0278                         }
0279                     }
0280                 }
0281                 ret = reinterpret_cast<const T0 *>(my_cast<T1 *>(d->m_backendObject));
0282                 if (ret) return ret;
0283             }
0284             return qobject_cast<T0 *>(d->m_backendObject);
0285         }
0286 
0287         inline Iface(MediaNodePrivate *const d) : iface(cast(d)) {}
0288         inline operator       T0 *()       { return iface; }
0289         inline operator const T0 *() const { return iface; }
0290         inline       T0 *operator->()       { Q_ASSERT(iface); return iface; }
0291         inline const T0 *operator->() const { Q_ASSERT(iface); return iface; }
0292     private:
0293         T0 *const iface;
0294     };
0295 
0296     template<class T0, class T1 = NoIface, class T2 = NoIface, class T3 = NoIface, class T4 = NoIface>
0297     class ConstIface
0298     {
0299     public:
0300         inline ConstIface(const MediaNodePrivate *const d) : iface(Iface<T0, T1, T2, T3, T4>::cast(d)) {}
0301         inline operator const T0 *() const { return iface; }
0302         inline const T0 *operator->() const { Q_ASSERT(iface); return iface; }
0303     private:
0304         const T0 *const iface;
0305     };
0306 } // namespace Phonon
0307 
0308 #define INTERFACE_CALL(function) \
0309 Iface<PHONON_INTERFACENAME >::cast(d)->function
0310 
0311 #define pINTERFACE_CALL(function) \
0312 Iface<PHONON_INTERFACENAME >::cast(this)->function
0313 
0314 #define PHONON_GETTER(rettype, name, retdefault) \
0315 rettype PHONON_CLASSNAME::name() const \
0316 { \
0317     const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
0318     if (!d->m_backendObject) \
0319         return retdefault; \
0320     rettype ret; \
0321     BACKEND_GET(rettype, ret, #name); \
0322     return ret; \
0323 }
0324 
0325 #define PHONON_INTERFACE_GETTER(rettype, name, retdefault) \
0326 rettype PHONON_CLASSNAME::name() const \
0327 { \
0328     const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
0329     if (!d->m_backendObject) \
0330         return retdefault; \
0331     return Iface<PHONON_INTERFACENAME >::cast(d)->name(); \
0332 }
0333 
0334 #define PHONON_GETTER1(rettype, name, retdefault, argtype1, argvar1) \
0335 rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \
0336 { \
0337     const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
0338     if (!d->m_backendObject) \
0339         return retdefault; \
0340     rettype ret; \
0341     BACKEND_GET1(rettype, ret, #name, const QObject *, argvar1->k_ptr->backendObject()); \
0342     return ret; \
0343 }
0344 
0345 #define PHONON_INTERFACE_GETTER1(rettype, name, retdefault, argtype1, argvar1) \
0346 rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \
0347 { \
0348     const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
0349     if (!d->m_backendObject) \
0350         return retdefault; \
0351     return Iface<PHONON_INTERFACENAME >::cast(d)->name(argvar1->k_ptr->backendObject()); \
0352 }
0353 
0354 #define PHONON_SETTER(functionname, privatevar, argtype1) \
0355 void PHONON_CLASSNAME::functionname(argtype1 x) \
0356 { \
0357     PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
0358     d->privatevar = x; \
0359     if (k_ptr->backendObject()) { \
0360         BACKEND_CALL1(#functionname, argtype1, x); \
0361     } \
0362 }
0363 
0364 #define PHONON_INTERFACE_SETTER(functionname, privatevar, argtype1) \
0365 void PHONON_CLASSNAME::functionname(argtype1 x) \
0366 { \
0367     PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \
0368     d->privatevar = x; \
0369     if (k_ptr->backendObject()) { \
0370         Iface<PHONON_INTERFACENAME >::cast(d)->functionname(x); \
0371     } \
0372 }
0373 
0374 #ifndef METATYPE_QLIST_INT_DEFINED
0375 #define METATYPE_QLIST_INT_DEFINED
0376 // Want this exactly once, see phonondefs_p.h kcm/outputdevicechoice.cpp
0377 Q_DECLARE_METATYPE(QList<int>)
0378 #endif
0379 
0380 #endif // PHONONDEFS_P_H