1 /*
2  * Copyright (C) 2011~2017 by CSSlayer
3  * wengxt@gmail.com
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above Copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above Copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of the authors nor the names of its contributors
17  *    may be used to endorse or promote products derived from this
18  *    software without specific prior written permission.
19  */
20 
21 #ifndef QFCITXPLATFORMINPUTCONTEXT_H
22 #define QFCITXPLATFORMINPUTCONTEXT_H
23 
24 #include "fcitxinputcontextproxy.h"
25 #include "fcitxqtdbustypes.h"
26 #include "fcitxwatcher.h"
27 #include <QDBusConnection>
28 #include <QDBusServiceWatcher>
29 #include <QGuiApplication>
30 #include <QKeyEvent>
31 #include <QPointer>
32 #include <QRect>
33 #include <QWindow>
34 #include <memory>
35 #include <qpa/qplatforminputcontext.h>
36 #include <unordered_map>
37 #include <xkbcommon/xkbcommon-compose.h>
38 
39 class QFileSystemWatcher;
40 enum FcitxKeyEventType { FCITX_PRESS_KEY, FCITX_RELEASE_KEY };
41 
42 enum FcitxCapabilityFlags {
43     CAPACITY_NONE = 0,
44     CAPACITY_CLIENT_SIDE_UI = (1 << 0),
45     CAPACITY_PREEDIT = (1 << 1),
46     CAPACITY_CLIENT_SIDE_CONTROL_STATE = (1 << 2),
47     CAPACITY_PASSWORD = (1 << 3),
48     CAPACITY_FORMATTED_PREEDIT = (1 << 4),
49     CAPACITY_CLIENT_UNFOCUS_COMMIT = (1 << 5),
50     CAPACITY_SURROUNDING_TEXT = (1 << 6),
51     CAPACITY_EMAIL = (1 << 7),
52     CAPACITY_DIGIT = (1 << 8),
53     CAPACITY_UPPERCASE = (1 << 9),
54     CAPACITY_LOWERCASE = (1 << 10),
55     CAPACITY_NOAUTOUPPERCASE = (1 << 11),
56     CAPACITY_URL = (1 << 12),
57     CAPACITY_DIALABLE = (1 << 13),
58     CAPACITY_NUMBER = (1 << 14),
59     CAPACITY_NO_ON_SCREEN_KEYBOARD = (1 << 15),
60     CAPACITY_SPELLCHECK = (1 << 16),
61     CAPACITY_NO_SPELLCHECK = (1 << 17),
62     CAPACITY_WORD_COMPLETION = (1 << 18),
63     CAPACITY_UPPERCASE_WORDS = (1 << 19),
64     CAPACITY_UPPERCASE_SENTENCES = (1 << 20),
65     CAPACITY_ALPHA = (1 << 21),
66     CAPACITY_NAME = (1 << 22),
67     CAPACITY_GET_IM_INFO_ON_FOCUS = (1 << 23),
68     CAPACITY_RELATIVE_CURSOR_RECT = (1 << 24),
69 };
70 
71 enum FcitxKeyState {
72     FcitxKeyState_None = 0,
73     FcitxKeyState_Shift = 1 << 0,
74     FcitxKeyState_CapsLock = 1 << 1,
75     FcitxKeyState_Ctrl = 1 << 2,
76     FcitxKeyState_Alt = 1 << 3,
77     FcitxKeyState_Alt_Shift = FcitxKeyState_Alt | FcitxKeyState_Shift,
78     FcitxKeyState_Ctrl_Shift = FcitxKeyState_Ctrl | FcitxKeyState_Shift,
79     FcitxKeyState_Ctrl_Alt = FcitxKeyState_Ctrl | FcitxKeyState_Alt,
80     FcitxKeyState_Ctrl_Alt_Shift =
81         FcitxKeyState_Ctrl | FcitxKeyState_Alt | FcitxKeyState_Shift,
82     FcitxKeyState_NumLock = 1 << 4,
83     FcitxKeyState_Super = 1 << 6,
84     FcitxKeyState_ScrollLock = 1 << 7,
85     FcitxKeyState_MousePressed = 1 << 8,
86     FcitxKeyState_HandledMask = 1 << 24,
87     FcitxKeyState_IgnoredMask = 1 << 25,
88     FcitxKeyState_Super2 = 1 << 26,
89     FcitxKeyState_Hyper = 1 << 27,
90     FcitxKeyState_Meta = 1 << 28,
91     FcitxKeyState_UsedMask = 0x5c001fff
92 };
93 
94 struct FcitxQtICData {
FcitxQtICDataFcitxQtICData95     FcitxQtICData(FcitxWatcher *watcher)
96         : proxy(new FcitxInputContextProxy(watcher, watcher)),
97           surroundingAnchor(-1), surroundingCursor(-1) {}
98     FcitxQtICData(const FcitxQtICData &that) = delete;
~FcitxQtICDataFcitxQtICData99     ~FcitxQtICData() {
100         if (proxy) {
101             delete proxy;
102         }
103     }
104     QFlags<FcitxCapabilityFlags> capability;
105     FcitxInputContextProxy *proxy;
106     QRect rect;
107     // Last key event forwarded.
108     std::unique_ptr<QKeyEvent> event;
109     QString surroundingText;
110     int surroundingAnchor;
111     int surroundingCursor;
112 };
113 
114 class ProcessKeyWatcher : public QDBusPendingCallWatcher {
115     Q_OBJECT
116 public:
117     ProcessKeyWatcher(const QKeyEvent &event, QWindow *window,
118                       const QDBusPendingCall &call, QObject *parent = 0)
QDBusPendingCallWatcher(call,parent)119         : QDBusPendingCallWatcher(call, parent),
120           m_event(event.type(), event.key(), event.modifiers(),
121                   event.nativeScanCode(), event.nativeVirtualKey(),
122                   event.nativeModifiers(), event.text(), event.isAutoRepeat(),
123                   event.count()),
124           m_window(window) {}
125 
~ProcessKeyWatcher()126     virtual ~ProcessKeyWatcher() {}
127 
keyEvent()128     const QKeyEvent &keyEvent() { return m_event; }
129 
window()130     QWindow *window() { return m_window.data(); }
131 
132 private:
133     QKeyEvent m_event;
134     QPointer<QWindow> m_window;
135 };
136 
137 struct XkbContextDeleter {
cleanupXkbContextDeleter138     static inline void cleanup(struct xkb_context *pointer) {
139         if (pointer)
140             xkb_context_unref(pointer);
141     }
142 };
143 
144 struct XkbComposeTableDeleter {
cleanupXkbComposeTableDeleter145     static inline void cleanup(struct xkb_compose_table *pointer) {
146         if (pointer)
147             xkb_compose_table_unref(pointer);
148     }
149 };
150 
151 struct XkbComposeStateDeleter {
cleanupXkbComposeStateDeleter152     static inline void cleanup(struct xkb_compose_state *pointer) {
153         if (pointer)
154             xkb_compose_state_unref(pointer);
155     }
156 };
157 
158 class FcitxQtInputMethodProxy;
159 
160 class QFcitxPlatformInputContext : public QPlatformInputContext {
161     Q_OBJECT
162 public:
163     QFcitxPlatformInputContext();
164     virtual ~QFcitxPlatformInputContext();
165 
166     virtual bool filterEvent(const QEvent *event) Q_DECL_OVERRIDE;
167     virtual bool isValid() const Q_DECL_OVERRIDE;
168     virtual void invokeAction(QInputMethod::Action,
169                               int cursorPosition) Q_DECL_OVERRIDE;
170     virtual void reset() Q_DECL_OVERRIDE;
171     virtual void commit() Q_DECL_OVERRIDE;
172     virtual void update(Qt::InputMethodQueries quries) Q_DECL_OVERRIDE;
173     virtual void setFocusObject(QObject *object) Q_DECL_OVERRIDE;
174     virtual QLocale locale() const Q_DECL_OVERRIDE;
175 
176 public Q_SLOTS:
177     void cursorRectChanged();
178     void commitString(const QString &str);
179     void updateFormattedPreedit(const FcitxFormattedPreeditList &preeditList,
180                                 int cursorPos);
181     void deleteSurroundingText(int offset, uint nchar);
182     void forwardKey(uint keyval, uint state, bool type);
183     void createInputContextFinished();
184     void cleanUp();
185     void windowDestroyed(QObject *object);
186     void updateCurrentIM(const QString &name, const QString &uniqueName,
187                          const QString &langCode);
188 
189 private:
190     bool processCompose(uint keyval, uint state, bool isRelaese);
191     QKeyEvent *createKeyEvent(uint keyval, uint state, bool isRelaese,
192                               const QKeyEvent *event);
193     void forwardEvent(QWindow *window, const QKeyEvent &event);
194 
195     void addCapability(FcitxQtICData &data,
196                        QFlags<FcitxCapabilityFlags> capability,
197                        bool forceUpdate = false) {
198         QFlags<FcitxCapabilityFlags> newcaps = data.capability | capability;
199         if (data.capability != newcaps || forceUpdate) {
200             data.capability = newcaps;
201             updateCapability(data);
202         }
203     }
204 
205     void removeCapability(FcitxQtICData &data,
206                           QFlags<FcitxCapabilityFlags> capability,
207                           bool forceUpdate = false) {
208         QFlags<FcitxCapabilityFlags> newcaps = data.capability & (~capability);
209         if (data.capability != newcaps || forceUpdate) {
210             data.capability = newcaps;
211             updateCapability(data);
212         }
213     }
214 
215     void updateCapability(const FcitxQtICData &data);
216     void commitPreedit(QPointer<QObject> input = qApp->focusObject());
217     void createICData(QWindow *w);
218     FcitxInputContextProxy *validIC();
219     FcitxInputContextProxy *validICByWindow(QWindow *window);
220     bool filterEventFallback(uint keyval, uint keycode, uint state,
221                              bool isRelaese);
222 
223     FcitxWatcher *m_watcher;
224     QString m_preedit;
225     QString m_commitPreedit;
226     FcitxFormattedPreeditList m_preeditList;
227     int m_cursorPos;
228     bool m_useSurroundingText;
229     bool m_syncMode;
230     QString m_lastSurroundingText;
231     int m_lastSurroundingAnchor = 0;
232     int m_lastSurroundingCursor = 0;
233     std::unordered_map<QWindow *, FcitxQtICData> m_icMap;
234     QPointer<QWindow> m_lastWindow;
235     QPointer<QObject> m_lastObject;
236     bool m_destroy;
237     QScopedPointer<struct xkb_context, XkbContextDeleter> m_xkbContext;
238     QScopedPointer<struct xkb_compose_table, XkbComposeTableDeleter>
239         m_xkbComposeTable;
240     QScopedPointer<struct xkb_compose_state, XkbComposeStateDeleter>
241         m_xkbComposeState;
242     QLocale m_locale;
243 private Q_SLOTS:
244     void processKeyEventFinished(QDBusPendingCallWatcher *);
245 };
246 
247 #endif // QFCITXPLATFORMINPUTCONTEXT_H
248