1 /*
2  * SPDX-FileCopyrightText: 2012~2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 #ifndef _DBUSADDONS_FCITXQTINPUTCONTEXTPROXY_P_H_
8 #define _DBUSADDONS_FCITXQTINPUTCONTEXTPROXY_P_H_
9 
10 #include "fcitxqtinputcontextproxy.h"
11 #include "fcitxqtinputcontextproxyimpl.h"
12 #include "fcitxqtinputmethodproxy.h"
13 #include "fcitxqtwatcher.h"
14 #include <QDBusServiceWatcher>
15 
16 namespace fcitx {
17 
18 class FcitxQtInputContextProxyPrivate {
19 public:
FcitxQtInputContextProxyPrivate(FcitxQtWatcher * watcher,FcitxQtInputContextProxy * q)20     FcitxQtInputContextProxyPrivate(FcitxQtWatcher *watcher,
21                                     FcitxQtInputContextProxy *q)
22         : q_ptr(q), fcitxWatcher_(watcher), watcher_(q) {
23         registerFcitxQtDBusTypes();
24         QObject::connect(fcitxWatcher_, &FcitxQtWatcher::availabilityChanged, q,
25                          [this]() { availabilityChanged(); });
26         watcher_.setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
27         QObject::connect(&watcher_, &QDBusServiceWatcher::serviceUnregistered,
28                          q, [this]() {
29                              cleanUp();
30                              availabilityChanged();
31                          });
32         availabilityChanged();
33     }
34 
~FcitxQtInputContextProxyPrivate()35     ~FcitxQtInputContextProxyPrivate() {
36         if (isValid()) {
37             icproxy_->DestroyIC();
38         }
39     }
40 
isValid()41     bool isValid() const { return (icproxy_ && icproxy_->isValid()); }
42 
availabilityChanged()43     void availabilityChanged() {
44         QTimer::singleShot(100, q_ptr, [this]() { recheck(); });
45     }
46 
recheck()47     void recheck() {
48         if (!isValid() && fcitxWatcher_->availability()) {
49             createInputContext();
50         }
51         if (!fcitxWatcher_->availability()) {
52             cleanUp();
53         }
54     }
55 
cleanUp()56     void cleanUp() {
57         auto services = watcher_.watchedServices();
58         for (const auto &service : services) {
59             watcher_.removeWatchedService(service);
60         }
61 
62         delete improxy_;
63         improxy_ = nullptr;
64         delete icproxy_;
65         icproxy_ = nullptr;
66         delete createInputContextWatcher_;
67         createInputContextWatcher_ = nullptr;
68     }
69 
createInputContext()70     void createInputContext() {
71         Q_Q(FcitxQtInputContextProxy);
72         if (!fcitxWatcher_->availability()) {
73             return;
74         }
75 
76         cleanUp();
77 
78         auto service = fcitxWatcher_->serviceName();
79         auto connection = fcitxWatcher_->connection();
80 
81         auto owner = connection.interface()->serviceOwner(service);
82         if (!owner.isValid()) {
83             return;
84         }
85 
86         watcher_.setConnection(connection);
87         watcher_.setWatchedServices(QStringList() << owner);
88         // Avoid race, query again.
89         if (!connection.interface()->isServiceRegistered(owner)) {
90             cleanUp();
91             return;
92         }
93 
94         QFileInfo info(QCoreApplication::applicationFilePath());
95         portal_ = true;
96         improxy_ = new FcitxQtInputMethodProxy(
97             owner, "/org/freedesktop/portal/inputmethod", connection, q);
98         FcitxQtStringKeyValueList list;
99         FcitxQtStringKeyValue arg;
100         arg.setKey("program");
101         arg.setValue(info.fileName());
102         list << arg;
103         if (!display_.isEmpty()) {
104             FcitxQtStringKeyValue arg2;
105             arg2.setKey("display");
106             arg2.setValue(display_);
107             list << arg2;
108         }
109 
110         auto result = improxy_->CreateInputContext(list);
111         createInputContextWatcher_ = new QDBusPendingCallWatcher(result);
112         QObject::connect(createInputContextWatcher_,
113                          &QDBusPendingCallWatcher::finished, q,
114                          [this]() { createInputContextFinished(); });
115     }
116 
createInputContextFinished()117     void createInputContextFinished() {
118         Q_Q(FcitxQtInputContextProxy);
119         if (createInputContextWatcher_->isError()) {
120             cleanUp();
121             return;
122         }
123 
124         QDBusPendingReply<QDBusObjectPath, QByteArray> reply(
125             *createInputContextWatcher_);
126         icproxy_ = new FcitxQtInputContextProxyImpl(improxy_->service(),
127                                                     reply.value().path(),
128                                                     improxy_->connection(), q);
129         QObject::connect(icproxy_, &FcitxQtInputContextProxyImpl::CommitString,
130                          q, &FcitxQtInputContextProxy::commitString);
131         QObject::connect(icproxy_, &FcitxQtInputContextProxyImpl::CurrentIM, q,
132                          &FcitxQtInputContextProxy::currentIM);
133         QObject::connect(icproxy_,
134                          &FcitxQtInputContextProxyImpl::DeleteSurroundingText,
135                          q, &FcitxQtInputContextProxy::deleteSurroundingText);
136         QObject::connect(icproxy_, &FcitxQtInputContextProxyImpl::ForwardKey, q,
137                          &FcitxQtInputContextProxy::forwardKey);
138         QObject::connect(icproxy_,
139                          &FcitxQtInputContextProxyImpl::UpdateFormattedPreedit,
140                          q, &FcitxQtInputContextProxy::updateFormattedPreedit);
141         QObject::connect(icproxy_,
142                          &FcitxQtInputContextProxyImpl::UpdateClientSideUI, q,
143                          &FcitxQtInputContextProxy::updateClientSideUI);
144 
145         delete createInputContextWatcher_;
146         createInputContextWatcher_ = nullptr;
147         Q_EMIT q->inputContextCreated(reply.argumentAt<1>());
148     }
149 
150     FcitxQtInputContextProxy *q_ptr;
151     Q_DECLARE_PUBLIC(FcitxQtInputContextProxy);
152 
153     FcitxQtWatcher *fcitxWatcher_;
154     QDBusServiceWatcher watcher_;
155     FcitxQtInputMethodProxy *improxy_ = nullptr;
156     FcitxQtInputContextProxyImpl *icproxy_ = nullptr;
157     QDBusPendingCallWatcher *createInputContextWatcher_ = nullptr;
158     QString display_;
159     bool portal_ = false;
160 };
161 } // namespace fcitx
162 
163 #endif // _DBUSADDONS_FCITXQTINPUTCONTEXTPROXY_P_H_
164