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