Kirigami2

tabletmodewatcher.cpp
1/*
2 * SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org>
3 * SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "tabletmodewatcher.h"
9#include <QCoreApplication>
10
11#if defined(KIRIGAMI_ENABLE_DBUS)
12#include "settings_interface.h"
13#include <QDBusConnection>
14#endif
15
16using namespace Qt::Literals::StringLiterals;
17
18// TODO: All the dbus stuff should be conditional, optional win32 support
19
20namespace Kirigami
21{
22namespace Platform
23{
24
25class TabletModeWatcherSingleton
26{
27public:
28 TabletModeWatcher self;
29};
30
31Q_GLOBAL_STATIC(TabletModeWatcherSingleton, privateTabletModeWatcherSelf)
32
33class TabletModeWatcherPrivate
34{
35 static constexpr auto PORTAL_GROUP = "org.kde.TabletMode"_L1;
36 static constexpr auto KEY_AVAILABLE = "available"_L1;
37 static constexpr auto KEY_ENABLED = "enabled"_L1;
38
39public:
40 TabletModeWatcherPrivate(TabletModeWatcher *watcher)
41 : q(watcher)
42 {
43 // Called here to avoid collisions with application event types so we should use
44 // registerEventType for generating the event types.
45 TabletModeChangedEvent::type = QEvent::Type(QEvent::registerEventType());
46#if !defined(KIRIGAMI_ENABLE_DBUS) && (defined(Q_OS_ANDROID) || defined(Q_OS_IOS))
47 isTabletModeAvailable = true;
48 isTabletMode = true;
49#elif defined(KIRIGAMI_ENABLE_DBUS)
50 // Mostly for debug purposes and for platforms which are always mobile,
51 // such as Plasma Mobile
52 if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE") || qEnvironmentVariableIsSet("KDE_KIRIGAMI_TABLET_MODE")) {
53 isTabletMode = (QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QLatin1String("1")
54 || QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QLatin1String("true"))
55 || (QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QLatin1String("1")
56 || QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QLatin1String("true"));
57 isTabletModeAvailable = isTabletMode;
58 } else if (qEnvironmentVariableIsSet("QT_NO_XDG_DESKTOP_PORTAL")) {
59 isTabletMode = false;
60 } else {
61 qDBusRegisterMetaType<VariantMapMap>();
62 auto portal = new OrgFreedesktopPortalSettingsInterface(u"org.freedesktop.portal.Desktop"_s,
63 u"/org/freedesktop/portal/desktop"_s,
65 q);
66
67 QObject::connect(portal,
68 &OrgFreedesktopPortalSettingsInterface::SettingChanged,
69 q,
70 [this](const QString &group, const QString &key, const QDBusVariant &value) {
71 if (group != PORTAL_GROUP) {
72 return;
73 }
74 if (key == KEY_AVAILABLE) {
75 Q_EMIT q->tabletModeAvailableChanged(value.variant().toBool());
76 } else if (key == KEY_ENABLED) {
77 setIsTablet(value.variant().toBool());
78 }
79 });
80
81 auto readAllProps = [portal, this] {
82 const auto reply = portal->ReadAll({PORTAL_GROUP});
83 auto watcher = new QDBusPendingCallWatcher(reply, q);
84 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this, watcher]() {
85 watcher->deleteLater();
86 QDBusPendingReply<VariantMapMap> reply = *watcher;
87 const auto properties = reply.value().value(PORTAL_GROUP);
88 Q_EMIT q->tabletModeAvailableChanged(properties[KEY_AVAILABLE].toBool());
89 setIsTablet(properties[KEY_ENABLED].toBool());
90 });
91 };
92 // If app.exec() has not been called yet, give Qt the chance to register us with the host portal
93 if (QThread::currentThread()->loopLevel() == 0) {
95 } else {
96 readAllProps();
97 }
98 }
99// TODO: case for Windows
100#endif
101 }
102 ~TabletModeWatcherPrivate() = default;
103 void setIsTablet(bool tablet);
104
105 TabletModeWatcher *q;
106 QList<QObject *> watchers;
107 bool isTabletModeAvailable = false;
108 bool isTabletMode = false;
109};
110
111void TabletModeWatcherPrivate::setIsTablet(bool tablet)
112{
113 if (isTabletMode == tablet) {
114 return;
115 }
116
117 isTabletMode = tablet;
118 TabletModeChangedEvent event{tablet};
119 Q_EMIT q->tabletModeChanged(tablet);
120 for (auto *w : watchers) {
122 }
123}
124
125TabletModeWatcher::TabletModeWatcher(QObject *parent)
126 : QObject(parent)
127 , d(new TabletModeWatcherPrivate(this))
128{
129}
130
131TabletModeWatcher::~TabletModeWatcher()
132{
133 delete d;
134}
135
136TabletModeWatcher *TabletModeWatcher::self()
137{
138 return &privateTabletModeWatcherSelf()->self;
139}
140
142{
143 return d->isTabletModeAvailable;
144}
145
147{
148 return d->isTabletMode;
149}
150
152{
153 d->watchers.append(watcher);
154}
155
156void TabletModeWatcher::removeWatcher(QObject *watcher)
157{
158 d->watchers.removeAll(watcher);
159}
160
161}
162}
163
164#include "moc_tabletmodewatcher.cpp"
void addWatcher(QObject *watcher)
Register an arbitrary QObject to send events from this.
KGuiItem properties()
bool sendEvent(QObject *receiver, QEvent *event)
QDBusConnection sessionBus()
void finished(QDBusPendingCallWatcher *self)
typename Select< 0 >::Type value() const const
QVariant variant() const const
int registerEventType(int hint)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QObject(QObject *parent)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString fromLatin1(QByteArrayView str)
QueuedConnection
QThread * currentThread()
bool toBool() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:47:53 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.