1 /*
2  * SPDX-FileCopyrightText: 2012~2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #ifndef QFCITXPLATFORMINPUTCONTEXT_H
9 #define QFCITXPLATFORMINPUTCONTEXT_H
10 
11 #include "fcitxcandidatewindow.h"
12 #include "fcitxqtinputcontextproxy.h"
13 #include "fcitxqtwatcher.h"
14 #include <QDBusConnection>
15 #include <QDBusServiceWatcher>
16 #include <QGuiApplication>
17 #include <QKeyEvent>
18 #include <QPointer>
19 #include <QRect>
20 #include <QWindow>
21 #include <memory>
22 #include <qpa/qplatforminputcontext.h>
23 #include <unordered_map>
24 #include <xkbcommon/xkbcommon-compose.h>
25 
26 namespace fcitx {
27 
28 class FcitxQtConnection;
29 class FcitxTheme;
30 
31 struct FcitxQtICData {
FcitxQtICDataFcitxQtICData32     FcitxQtICData(FcitxQtWatcher *watcher, QWindow *window)
33         : proxy(new FcitxQtInputContextProxy(watcher, watcher)),
34           watcher_(watcher), window_(window) {
35         proxy->setProperty("icData",
36                            QVariant::fromValue(static_cast<void *>(this)));
37         QObject::connect(window, &QWindow::visibilityChanged, proxy,
38                          [this](bool visible) {
39                              if (!visible) {
40                                  resetCandidateWindow();
41                              }
42                          });
43         QObject::connect(watcher, &FcitxQtWatcher::availabilityChanged, proxy,
44                          [this](bool avail) {
45                              if (!avail) {
46                                  resetCandidateWindow();
47                              }
48                          });
49     }
50     FcitxQtICData(const FcitxQtICData &that) = delete;
~FcitxQtICDataFcitxQtICData51     ~FcitxQtICData() {
52         delete proxy;
53         resetCandidateWindow();
54     }
55 
candidateWindowFcitxQtICData56     FcitxCandidateWindow *candidateWindow(FcitxTheme *theme) {
57         if (!candidateWindow_) {
58             candidateWindow_ = new FcitxCandidateWindow(window(), theme);
59             QObject::connect(
60                 candidateWindow_, &FcitxCandidateWindow::candidateSelected,
61                 proxy,
62                 [proxy = proxy](int index) { proxy->selectCandidate(index); });
63             QObject::connect(candidateWindow_,
64                              &FcitxCandidateWindow::prevClicked, proxy,
65                              [proxy = proxy]() { proxy->prevPage(); });
66             QObject::connect(candidateWindow_,
67                              &FcitxCandidateWindow::nextClicked, proxy,
68                              [proxy = proxy]() { proxy->nextPage(); });
69         }
70         return candidateWindow_;
71     }
72 
windowFcitxQtICData73     QWindow *window() { return window_.data(); }
watcherFcitxQtICData74     auto *watcher() { return watcher_; }
75 
resetCandidateWindowFcitxQtICData76     void resetCandidateWindow() {
77         if (auto *w = candidateWindow_.data()) {
78             candidateWindow_ = nullptr;
79             w->deleteLater();
80             return;
81         }
82     }
83 
84     quint64 capability = 0;
85     FcitxQtInputContextProxy *proxy;
86     QRect rect;
87     // Last key event forwarded.
88     std::unique_ptr<QKeyEvent> event;
89     QString surroundingText;
90     int surroundingAnchor = -1;
91     int surroundingCursor = -1;
92 
93 private:
94     FcitxQtWatcher *watcher_;
95     QPointer<QWindow> window_;
96     QPointer<FcitxCandidateWindow> candidateWindow_;
97 };
98 
99 class ProcessKeyWatcher : public QDBusPendingCallWatcher {
100     Q_OBJECT
101 public:
102     ProcessKeyWatcher(const QKeyEvent &event, QWindow *window,
103                       const QDBusPendingCall &call, QObject *parent = 0)
QDBusPendingCallWatcher(call,parent)104         : QDBusPendingCallWatcher(call, parent),
105           event_(event.type(), event.key(), event.modifiers(),
106                  event.nativeScanCode(), event.nativeVirtualKey(),
107                  event.nativeModifiers(), event.text(), event.isAutoRepeat(),
108                  event.count()),
109           window_(window) {}
110 
~ProcessKeyWatcher()111     virtual ~ProcessKeyWatcher() {}
112 
keyEvent()113     const QKeyEvent &keyEvent() { return event_; }
114 
window()115     QWindow *window() { return window_.data(); }
116 
117 private:
118     QKeyEvent event_;
119     QPointer<QWindow> window_;
120 };
121 
122 struct XkbContextDeleter {
cleanupXkbContextDeleter123     static inline void cleanup(struct xkb_context *pointer) {
124         if (pointer)
125             xkb_context_unref(pointer);
126     }
127 };
128 
129 struct XkbComposeTableDeleter {
cleanupXkbComposeTableDeleter130     static inline void cleanup(struct xkb_compose_table *pointer) {
131         if (pointer)
132             xkb_compose_table_unref(pointer);
133     }
134 };
135 
136 struct XkbComposeStateDeleter {
cleanupXkbComposeStateDeleter137     static inline void cleanup(struct xkb_compose_state *pointer) {
138         if (pointer)
139             xkb_compose_state_unref(pointer);
140     }
141 };
142 
143 class QFcitxPlatformInputContext : public QPlatformInputContext {
144     Q_OBJECT
145 public:
146     QFcitxPlatformInputContext();
147     virtual ~QFcitxPlatformInputContext();
148 
149     bool isValid() const override;
150     void setFocusObject(QObject *object) override;
151     void invokeAction(QInputMethod::Action, int cursorPosition) override;
152     void reset() override;
153     void commit() override;
154     void update(Qt::InputMethodQueries quries) override;
155     bool filterEvent(const QEvent *event) override;
156     QLocale locale() const override;
157     bool hasCapability(Capability capability) const override;
158 
159 public Q_SLOTS:
160     void cursorRectChanged();
161     void commitString(const QString &str);
162     void updateFormattedPreedit(const FcitxQtFormattedPreeditList &preeditList,
163                                 int cursorPos);
164     void deleteSurroundingText(int offset, unsigned int nchar);
165     void forwardKey(unsigned int keyval, unsigned int state, bool type);
166     void createInputContextFinished(const QByteArray &uuid);
167     void cleanUp();
168     void windowDestroyed(QObject *object);
169     void updateCurrentIM(const QString &name, const QString &uniqueName,
170                          const QString &langCode);
171     void updateClientSideUI(const FcitxQtFormattedPreeditList &preedit,
172                             int cursorpos,
173                             const FcitxQtFormattedPreeditList &auxUp,
174                             const FcitxQtFormattedPreeditList &auxDown,
175                             const FcitxQtStringKeyValueList &candidates,
176                             int candidateIndex, int layoutHint, bool hasPrev,
177                             bool hasNext);
178 private Q_SLOTS:
179     void processKeyEventFinished(QDBusPendingCallWatcher *);
180 
181 private:
182     bool processCompose(unsigned int keyval, unsigned int state,
183                         bool isRelaese);
184     QKeyEvent *createKeyEvent(unsigned int keyval, unsigned int state,
185                               bool isRelaese, const QKeyEvent *event);
186     void forwardEvent(QWindow *window, const QKeyEvent &event);
187 
188     void addCapability(FcitxQtICData &data, quint64 capability,
189                        bool forceUpdate = false) {
190         auto newcaps = data.capability | capability;
191         if (data.capability != newcaps || forceUpdate) {
192             data.capability = newcaps;
193             updateCapability(data);
194         }
195     }
196 
197     void removeCapability(FcitxQtICData &data, quint64 capability,
198                           bool forceUpdate = false) {
199         auto newcaps = data.capability & (~capability);
200         if (data.capability != newcaps || forceUpdate) {
201             data.capability = newcaps;
202             updateCapability(data);
203         }
204     }
205 
206     void updateCapability(const FcitxQtICData &data);
207     void commitPreedit(QPointer<QObject> input = qApp->focusObject());
208     void createICData(QWindow *w);
209     FcitxQtInputContextProxy *validIC();
210     FcitxQtInputContextProxy *validICByWindow(QWindow *window);
211     bool filterEventFallback(unsigned int keyval, unsigned int keycode,
212                              unsigned int state, bool isRelaese);
213 
214     Q_INVOKABLE void updateCursorRect(QPointer<QWindow> window);
215 
216     FcitxQtWatcher *watcher_;
217     QString preedit_;
218     QString commitPreedit_;
219     FcitxQtFormattedPreeditList preeditList_;
220     int cursorPos_;
221     bool useSurroundingText_;
222     bool syncMode_;
223     std::unordered_map<QWindow *, FcitxQtICData> icMap_;
224     QPointer<QWindow> lastWindow_;
225     QPointer<QObject> lastObject_;
226     bool destroy_;
227     QScopedPointer<struct xkb_context, XkbContextDeleter> xkbContext_;
228     QScopedPointer<struct xkb_compose_table, XkbComposeTableDeleter>
229         xkbComposeTable_;
230     QScopedPointer<struct xkb_compose_state, XkbComposeStateDeleter>
231         xkbComposeState_;
232     QLocale locale_;
233     FcitxTheme *theme_ = nullptr;
234 };
235 } // namespace fcitx
236 
237 #endif // QFCITXPLATFORMINPUTCONTEXT_H
238