File indexing completed on 2024-05-05 03:56:27

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org>
0003  *  SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "tabletmodewatcher.h"
0009 #include <QCoreApplication>
0010 
0011 #if defined(KIRIGAMI_ENABLE_DBUS)
0012 #include "settings_interface.h"
0013 #include <QDBusConnection>
0014 #endif
0015 
0016 using namespace Qt::Literals::StringLiterals;
0017 
0018 // TODO: All the dbus stuff should be conditional, optional win32 support
0019 
0020 namespace Kirigami
0021 {
0022 namespace Platform
0023 {
0024 
0025 class TabletModeWatcherSingleton
0026 {
0027 public:
0028     TabletModeWatcher self;
0029 };
0030 
0031 Q_GLOBAL_STATIC(TabletModeWatcherSingleton, privateTabletModeWatcherSelf)
0032 
0033 class TabletModeWatcherPrivate
0034 {
0035     static constexpr auto PORTAL_GROUP = "org.kde.TabletMode"_L1;
0036     static constexpr auto KEY_AVAILABLE = "available"_L1;
0037     static constexpr auto KEY_ENABLED = "enabled"_L1;
0038 
0039 public:
0040     TabletModeWatcherPrivate(TabletModeWatcher *watcher)
0041         : q(watcher)
0042     {
0043         // Called here to avoid collisions with application event types so we should use
0044         // registerEventType for generating the event types.
0045         TabletModeChangedEvent::type = QEvent::Type(QEvent::registerEventType());
0046 #if !defined(KIRIGAMI_ENABLE_DBUS) && (defined(Q_OS_ANDROID) || defined(Q_OS_IOS))
0047         isTabletModeAvailable = true;
0048         isTabletMode = true;
0049 #elif defined(KIRIGAMI_ENABLE_DBUS)
0050         // Mostly for debug purposes and for platforms which are always mobile,
0051         // such as Plasma Mobile
0052         if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE") || qEnvironmentVariableIsSet("KDE_KIRIGAMI_TABLET_MODE")) {
0053             /* clang-format off */
0054             isTabletMode =
0055                 (QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QStringLiteral("1")
0056                     || QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QStringLiteral("true"))
0057                     || (QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QStringLiteral("1")
0058                     || QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QStringLiteral("true"));
0059             /* clang-format on */
0060             isTabletModeAvailable = isTabletMode;
0061         } else {
0062             qDBusRegisterMetaType<VariantMapMap>();
0063             auto portal = new OrgFreedesktopPortalSettingsInterface(u"org.freedesktop.portal.Desktop"_s,
0064                                                                     u"/org/freedesktop/portal/desktop"_s,
0065                                                                     QDBusConnection::sessionBus(),
0066                                                                     q);
0067 
0068             QObject::connect(portal,
0069                              &OrgFreedesktopPortalSettingsInterface::SettingChanged,
0070                              q,
0071                              [this](const QString &group, const QString &key, const QDBusVariant &value) {
0072                                  if (group != PORTAL_GROUP) {
0073                                      return;
0074                                  }
0075                                  if (key == KEY_AVAILABLE) {
0076                                      Q_EMIT q->tabletModeAvailableChanged(value.variant().toBool());
0077                                  } else if (key == KEY_ENABLED) {
0078                                      setIsTablet(value.variant().toBool());
0079                                  }
0080                              });
0081 
0082             const auto reply = portal->ReadAll({PORTAL_GROUP});
0083             auto watcher = new QDBusPendingCallWatcher(reply, q);
0084             QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this, watcher]() {
0085                 watcher->deleteLater();
0086                 QDBusPendingReply<VariantMapMap> reply = *watcher;
0087                 const auto properties = reply.value().value(PORTAL_GROUP);
0088                 Q_EMIT q->tabletModeAvailableChanged(properties[KEY_AVAILABLE].toBool());
0089                 setIsTablet(properties[KEY_ENABLED].toBool());
0090             });
0091         }
0092 // TODO: case for Windows
0093 #endif
0094     }
0095     ~TabletModeWatcherPrivate(){};
0096     void setIsTablet(bool tablet);
0097 
0098     TabletModeWatcher *q;
0099     QList<QObject *> watchers;
0100     bool isTabletModeAvailable = false;
0101     bool isTabletMode = false;
0102 };
0103 
0104 void TabletModeWatcherPrivate::setIsTablet(bool tablet)
0105 {
0106     if (isTabletMode == tablet) {
0107         return;
0108     }
0109 
0110     isTabletMode = tablet;
0111     TabletModeChangedEvent event{tablet};
0112     Q_EMIT q->tabletModeChanged(tablet);
0113     for (auto *w : watchers) {
0114         QCoreApplication::sendEvent(w, &event);
0115     }
0116 }
0117 
0118 TabletModeWatcher::TabletModeWatcher(QObject *parent)
0119     : QObject(parent)
0120     , d(new TabletModeWatcherPrivate(this))
0121 {
0122 }
0123 
0124 TabletModeWatcher::~TabletModeWatcher()
0125 {
0126     delete d;
0127 }
0128 
0129 TabletModeWatcher *TabletModeWatcher::self()
0130 {
0131     return &privateTabletModeWatcherSelf()->self;
0132 }
0133 
0134 bool TabletModeWatcher::isTabletModeAvailable() const
0135 {
0136     return d->isTabletModeAvailable;
0137 }
0138 
0139 bool TabletModeWatcher::isTabletMode() const
0140 {
0141     return d->isTabletMode;
0142 }
0143 
0144 void TabletModeWatcher::addWatcher(QObject *watcher)
0145 {
0146     d->watchers.append(watcher);
0147 }
0148 
0149 void TabletModeWatcher::removeWatcher(QObject *watcher)
0150 {
0151     d->watchers.removeAll(watcher);
0152 }
0153 
0154 }
0155 }
0156 
0157 #include "moc_tabletmodewatcher.cpp"