File indexing completed on 2024-05-12 15:42:38

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "tabletmodewatcher.h"
0008 #include <QCoreApplication>
0009 
0010 #if defined(KIRIGAMI_ENABLE_DBUS)
0011 #include "tabletmodemanager_interface.h"
0012 #include <QDBusConnection>
0013 #endif
0014 
0015 // TODO: All the dbus stuff should be conditional, optional win32 support
0016 
0017 namespace Kirigami
0018 {
0019 KIRIGAMI2_EXPORT QEvent::Type TabletModeChangedEvent::type = QEvent::None;
0020 
0021 class TabletModeWatcherSingleton
0022 {
0023 public:
0024     TabletModeWatcher self;
0025 };
0026 
0027 Q_GLOBAL_STATIC(TabletModeWatcherSingleton, privateTabletModeWatcherSelf)
0028 
0029 class TabletModeWatcherPrivate
0030 {
0031 public:
0032     TabletModeWatcherPrivate(TabletModeWatcher *watcher)
0033         : q(watcher)
0034     {
0035         // Called here to avoid collisions with application event types so we should use
0036         // registerEventType for generating the event types.
0037         TabletModeChangedEvent::type = QEvent::Type(QEvent::registerEventType());
0038 #if !defined(KIRIGAMI_ENABLE_DBUS) && (defined(Q_OS_ANDROID) || defined(Q_OS_IOS))
0039         isTabletModeAvailable = true;
0040         isTabletMode = true;
0041 #elif defined(KIRIGAMI_ENABLE_DBUS)
0042         // Mostly for debug purposes and for platforms which are always mobile,
0043         // such as Plasma Mobile
0044         if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE") || qEnvironmentVariableIsSet("KDE_KIRIGAMI_TABLET_MODE")) {
0045             /* clang-format off */
0046             isTabletMode =
0047                 (QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QStringLiteral("1")
0048                     || QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QStringLiteral("true"))
0049                 || (QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QStringLiteral("1")
0050                     || QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QStringLiteral("true"));
0051             /* clang-format on */
0052             isTabletModeAvailable = isTabletMode;
0053         } else {
0054             m_interface =
0055                 new OrgKdeKWinTabletModeManagerInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/org/kde/KWin"), QDBusConnection::sessionBus(), q);
0056 
0057             if (m_interface->isValid()) {
0058                 // NOTE: the initial call is actually sync, because is better a tiny freeze than having the ui always recalculated and changed at the start
0059                 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
0060                                                                       QStringLiteral("/org/kde/KWin"),
0061                                                                       QStringLiteral("org.freedesktop.DBus.Properties"),
0062                                                                       QStringLiteral("GetAll"));
0063                 message.setArguments({m_interface->interface()});
0064                 QDBusReply<QVariantMap> propsReply = QDBusConnection::sessionBus().call(message);
0065                 isTabletModeAvailable = propsReply.value()[QLatin1String("tabletModeAvailable")].toBool();
0066                 isTabletMode = propsReply.value()[QLatin1String("tabletMode")].toBool();
0067                 QObject::connect(m_interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeChanged, q, [this](bool tabletMode) {
0068                     setIsTablet(tabletMode);
0069                 });
0070                 QObject::connect(m_interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeAvailableChanged, q, [this](bool avail) {
0071                     isTabletModeAvailable = avail;
0072                     Q_EMIT q->tabletModeAvailableChanged(avail);
0073                 });
0074             } else {
0075                 isTabletModeAvailable = false;
0076                 isTabletMode = false;
0077             }
0078         }
0079 // TODO: case for Windows
0080 #else
0081         isTabletModeAvailable = false;
0082         isTabletMode = false;
0083 #endif
0084     }
0085     ~TabletModeWatcherPrivate(){};
0086     void setIsTablet(bool tablet);
0087 
0088     TabletModeWatcher *q;
0089 #if defined(KIRIGAMI_ENABLE_DBUS)
0090     OrgKdeKWinTabletModeManagerInterface *m_interface = nullptr;
0091 #endif
0092     QVector<QObject *> watchers;
0093     bool isTabletModeAvailable = false;
0094     bool isTabletMode = false;
0095 };
0096 
0097 void TabletModeWatcherPrivate::setIsTablet(bool tablet)
0098 {
0099     if (isTabletMode == tablet) {
0100         return;
0101     }
0102 
0103     isTabletMode = tablet;
0104     TabletModeChangedEvent event{tablet};
0105     Q_EMIT q->tabletModeChanged(tablet);
0106     for (auto *w : watchers) {
0107         QCoreApplication::sendEvent(w, &event);
0108     }
0109 }
0110 
0111 TabletModeWatcher::TabletModeWatcher(QObject *parent)
0112     : QObject(parent)
0113     , d(new TabletModeWatcherPrivate(this))
0114 {
0115 }
0116 
0117 TabletModeWatcher::~TabletModeWatcher()
0118 {
0119     delete d;
0120 }
0121 
0122 TabletModeWatcher *TabletModeWatcher::self()
0123 {
0124     return &privateTabletModeWatcherSelf()->self;
0125 }
0126 
0127 bool TabletModeWatcher::isTabletModeAvailable() const
0128 {
0129     return d->isTabletModeAvailable;
0130 }
0131 
0132 bool TabletModeWatcher::isTabletMode() const
0133 {
0134     return d->isTabletMode;
0135 }
0136 
0137 void TabletModeWatcher::addWatcher(QObject *watcher)
0138 {
0139     d->watchers.append(watcher);
0140 }
0141 
0142 void TabletModeWatcher::removeWatcher(QObject *watcher)
0143 {
0144     d->watchers.removeAll(watcher);
0145 }
0146 }
0147 
0148 #include "moc_tabletmodewatcher.cpp"