1 /*
2  * SPDX-FileCopyrightText: 2020-2021 Vifly <viflythink@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "fcitx4frontend.h"
9 #include <fstream>
10 #include "fcitx-utils/dbus/message.h"
11 #include "fcitx-utils/dbus/objectvtable.h"
12 #include "fcitx-utils/dbus/servicewatcher.h"
13 #include "fcitx-utils/log.h"
14 #include "fcitx-utils/metastring.h"
15 #include "fcitx-utils/standardpath.h"
16 #include "fcitx/inputcontext.h"
17 #include "fcitx/inputmethodentry.h"
18 #include "fcitx/inputmethodmanager.h"
19 #include "fcitx/instance.h"
20 #include "fcitx/misc_p.h"
21 #include "dbus_public.h"
22 
23 #define FCITX_INPUTMETHOD_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod"
24 #define FCITX_INPUTCONTEXT_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext"
25 #define FCITX_DBUS_SERVICE "org.fcitx.Fcitx"
26 
27 namespace fcitx {
28 
29 namespace {
30 
31 std::vector<dbus::DBusStruct<std::string, int>>
buildFormattedTextVector(const Text & text)32 buildFormattedTextVector(const Text &text) {
33     std::vector<dbus::DBusStruct<std::string, int>> vector;
34     for (size_t i = 0, e = text.size(); i < e; i++) {
35         // In fcitx 4, underline bit means "no underline", so we need to reverse
36         // it.
37         const auto flag = text.formatAt(i) ^ TextFormatFlag::Underline;
38         vector.emplace_back(
39             std::make_tuple(text.stringAt(i), static_cast<int>(flag)));
40     }
41     return vector;
42 }
43 
getDisplayNumber(const std::string & var)44 int getDisplayNumber(const std::string &var) {
45     auto pos = var.find(':');
46     if (pos == std::string::npos) {
47         return 0;
48     }
49     // skip :
50     pos += 1;
51     // Handle address like :0.0
52     auto period = var.find(pos, '.');
53     if (period != std::string::npos) {
54         period -= pos;
55     }
56 
57     try {
58         std::string num(var.substr(pos, period));
59         int displayNumber = std::stoi(num);
60         return displayNumber;
61     } catch (...) {
62     }
63     return 0;
64 }
65 } // namespace
66 
67 class Fcitx4InputMethod : public dbus::ObjectVTable<Fcitx4InputMethod> {
68 public:
Fcitx4InputMethod(int display,Fcitx4FrontendModule * module,dbus::Bus * bus)69     Fcitx4InputMethod(int display, Fcitx4FrontendModule *module, dbus::Bus *bus)
70         : display_(display), module_(module), instance_(module->instance()),
71           bus_(std::make_unique<dbus::Bus>(bus->address())),
72           watcher_(std::make_unique<dbus::ServiceWatcher>(*bus_)) {
73         bus_->attachEventLoop(&instance_->eventLoop());
74         bus_->addObjectVTable("/inputmethod", FCITX_INPUTMETHOD_DBUS_INTERFACE,
75                               *this);
76         Flags<dbus::RequestNameFlag> requestFlag =
77             dbus::RequestNameFlag::ReplaceExisting;
78         auto dbusServiceName =
79             stringutils::concat(FCITX_DBUS_SERVICE, "-", display);
80         bus_->requestName(dbusServiceName, requestFlag);
81 
82         auto localMachineId = getLocalMachineId(/*fallback=*/"machine-id");
83         auto path = stringutils::joinPath(
84             "fcitx", "dbus", stringutils::concat(localMachineId, "-", display));
85         bool res = StandardPath::global().safeSave(
86             StandardPath::Type::Config, path, [this](int fd) {
87                 auto address = bus_->address();
88                 fs::safeWrite(fd, address.c_str(), address.size() + 1);
89                 // Because fcitx5 don't launch dbus by itself, we write 0
90                 // on purpose to make address resolve fail, except WPS.
91                 pid_t pid = 0;
92                 fs::safeWrite(fd, &pid, sizeof(pid_t));
93                 fs::safeWrite(fd, &pid, sizeof(pid_t));
94                 return true;
95             });
96         if (res) {
97             // Failed to write address file does not matter if we could use
98             // regular dbus.
99             pathWrote_ =
100                 stringutils::joinPath(StandardPath::global().userDirectory(
101                                           StandardPath::Type::Config),
102                                       path);
103         }
104     }
105 
~Fcitx4InputMethod()106     ~Fcitx4InputMethod() {
107         if (!pathWrote_.empty()) {
108             unlink(pathWrote_.data());
109         }
110     }
111 
112     std::tuple<int, bool, uint32_t, uint32_t, uint32_t, uint32_t>
113     createICv3(const std::string &appname, int pid);
114 
serviceWatcher()115     dbus::ServiceWatcher &serviceWatcher() { return *watcher_; }
bus()116     dbus::Bus *bus() { return bus_.get(); }
instance()117     Instance *instance() { return module_->instance(); }
118 
119 private:
120     // V1 and V2 are too old, so we just ignore them.
121     FCITX_OBJECT_VTABLE_METHOD(createICv3, "CreateICv3", "si", "ibuuuu");
122 
123     int display_;
124     Fcitx4FrontendModule *module_;
125     Instance *instance_;
126     std::unique_ptr<dbus::Bus> bus_;
127     std::unique_ptr<dbus::ServiceWatcher> watcher_;
128     std::string pathWrote_;
129 };
130 
131 class Fcitx4InputContext : public InputContext,
132                            public dbus::ObjectVTable<Fcitx4InputContext> {
133 public:
Fcitx4InputContext(int id,InputContextManager & icManager,Fcitx4InputMethod * im,const std::string & sender,const std::string & program)134     Fcitx4InputContext(int id, InputContextManager &icManager,
135                        Fcitx4InputMethod *im, const std::string &sender,
136                        const std::string &program)
137         : InputContext(icManager, program),
138           path_(stringutils::concat("/inputcontext_", id)), im_(im),
139           handler_(im_->serviceWatcher().watchService(
140               sender,
141               [this](const std::string &, const std::string &,
142                      const std::string &newName) {
143                   if (newName.empty()) {
144                       delete this;
145                   }
146               })),
147           name_(sender) {
148         created();
149     }
150 
~Fcitx4InputContext()151     ~Fcitx4InputContext() { InputContext::destroy(); }
152 
frontend() const153     const char *frontend() const override { return "fcitx4"; }
154 
path() const155     const dbus::ObjectPath &path() const { return path_; }
156 
updateIM(const InputMethodEntry * entry)157     void updateIM(const InputMethodEntry *entry) {
158         currentIMTo(name_, entry->name(), entry->uniqueName(),
159                     entry->languageCode());
160     }
161 
commitStringImpl(const std::string & text)162     void commitStringImpl(const std::string &text) override {
163         commitStringDBusTo(name_, text);
164     }
165 
updatePreeditImpl()166     void updatePreeditImpl() override {
167         auto preedit =
168             im_->instance()->outputFilter(this, inputPanel().clientPreedit());
169         std::vector<dbus::DBusStruct<std::string, int>> strs =
170             buildFormattedTextVector(preedit);
171         updateFormattedPreeditTo(name_, strs, preedit.cursor());
172     }
173 
deleteSurroundingTextImpl(int offset,unsigned int size)174     void deleteSurroundingTextImpl(int offset, unsigned int size) override {
175         deleteSurroundingTextDBusTo(name_, offset, size);
176     }
177 
forwardKeyImpl(const ForwardKeyEvent & key)178     void forwardKeyImpl(const ForwardKeyEvent &key) override {
179         forwardKeyDBusTo(name_, static_cast<uint32_t>(key.rawKey().sym()),
180                          static_cast<uint32_t>(key.rawKey().states()),
181                          key.isRelease() ? 1 : 0);
182         bus()->flush();
183     }
184 #define CHECK_SENDER_OR_RETURN                                                 \
185     if (currentMessage()->sender() != name_)                                   \
186     return
187 
enableInputContext()188     void enableInputContext() {}
189 
closeInputContext()190     void closeInputContext() {}
191 
mouseEvent(int)192     void mouseEvent(int) {}
193 
setCursorLocation(int x,int y)194     void setCursorLocation(int x, int y) {
195         CHECK_SENDER_OR_RETURN;
196         setCursorRect(Rect{x, y, 0, 0});
197     }
198 
focusInDBus()199     void focusInDBus() {
200         CHECK_SENDER_OR_RETURN;
201         focusIn();
202     }
203 
focusOutDBus()204     void focusOutDBus() {
205         CHECK_SENDER_OR_RETURN;
206         focusOut();
207     }
208 
resetDBus()209     void resetDBus() {
210         CHECK_SENDER_OR_RETURN;
211         reset();
212     }
213 
setCursorRectDBus(int x,int y,int w,int h)214     void setCursorRectDBus(int x, int y, int w, int h) {
215         CHECK_SENDER_OR_RETURN;
216         setCursorRect(Rect{x, y, x + w, y + h});
217     }
218 
setCapability(uint32_t cap)219     void setCapability(uint32_t cap) {
220         CHECK_SENDER_OR_RETURN;
221         setCapabilityFlags(CapabilityFlags{cap});
222     }
223 
setSurroundingText(const std::string & str,uint32_t cursor,uint32_t anchor)224     void setSurroundingText(const std::string &str, uint32_t cursor,
225                             uint32_t anchor) {
226         CHECK_SENDER_OR_RETURN;
227         surroundingText().setText(str, cursor, anchor);
228         updateSurroundingText();
229     }
230 
setSurroundingTextPosition(uint32_t cursor,uint32_t anchor)231     void setSurroundingTextPosition(uint32_t cursor, uint32_t anchor) {
232         CHECK_SENDER_OR_RETURN;
233         surroundingText().setCursor(cursor, anchor);
234         updateSurroundingText();
235     }
236 
destroyDBus()237     void destroyDBus() {
238         CHECK_SENDER_OR_RETURN;
239         delete this;
240     }
241 
processKeyEvent(uint32_t keyval,uint32_t keycode,uint32_t state,int isRelease,uint32_t time)242     int processKeyEvent(uint32_t keyval, uint32_t keycode, uint32_t state,
243                         int isRelease, uint32_t time) {
244         CHECK_SENDER_OR_RETURN false;
245         KeyEvent event(
246             this, Key(static_cast<KeySym>(keyval), KeyStates(state), keycode),
247             isRelease, time);
248         // Force focus if there's keyevent.
249         if (!hasFocus()) {
250             focusIn();
251         }
252 
253         return keyEvent(event) ? 1 : 0;
254     }
255 
256 private:
257     // Because there is no application to use, don't impl CommitPreedit and
258     // UpdateClientSideUI.
259     FCITX_OBJECT_VTABLE_METHOD(enableInputContext, "EnableIC", "", "");
260     FCITX_OBJECT_VTABLE_METHOD(closeInputContext, "CloseIC", "", "");
261     FCITX_OBJECT_VTABLE_METHOD(focusInDBus, "FocusIn", "", "");
262     FCITX_OBJECT_VTABLE_METHOD(focusOutDBus, "FocusOut", "", "");
263     FCITX_OBJECT_VTABLE_METHOD(resetDBus, "Reset", "", "");
264     FCITX_OBJECT_VTABLE_METHOD(mouseEvent, "MouseEvent", "i", "");
265     FCITX_OBJECT_VTABLE_METHOD(setCursorLocation, "SetCursorLocation", "ii",
266                                "");
267     FCITX_OBJECT_VTABLE_METHOD(setCursorRectDBus, "SetCursorRect", "iiii", "");
268     FCITX_OBJECT_VTABLE_METHOD(setCapability, "SetCapacity", "u", "");
269     FCITX_OBJECT_VTABLE_METHOD(setSurroundingText, "SetSurroundingText", "suu",
270                                "");
271     FCITX_OBJECT_VTABLE_METHOD(setSurroundingTextPosition,
272                                "SetSurroundingTextPosition", "uu", "");
273     FCITX_OBJECT_VTABLE_METHOD(destroyDBus, "DestroyIC", "", "");
274     FCITX_OBJECT_VTABLE_METHOD(processKeyEvent, "ProcessKeyEvent", "uuuiu",
275                                "i");
276 
277     FCITX_OBJECT_VTABLE_SIGNAL(commitStringDBus, "CommitString", "s");
278     FCITX_OBJECT_VTABLE_SIGNAL(currentIM, "CurrentIM", "sss");
279     FCITX_OBJECT_VTABLE_SIGNAL(updateFormattedPreedit, "UpdateFormattedPreedit",
280                                "a(si)i");
281     FCITX_OBJECT_VTABLE_SIGNAL(deleteSurroundingTextDBus,
282                                "DeleteSurroundingText", "iu");
283     FCITX_OBJECT_VTABLE_SIGNAL(forwardKeyDBus, "ForwardKey", "uui");
284 
285     dbus::ObjectPath path_;
286     Fcitx4InputMethod *im_;
287     std::unique_ptr<HandlerTableEntry<dbus::ServiceWatcherCallback>> handler_;
288     std::string name_;
289 };
290 
291 std::tuple<int, bool, uint32_t, uint32_t, uint32_t, uint32_t>
createICv3(const std::string & appname,int)292 Fcitx4InputMethod::createICv3(const std::string &appname, int /*pid*/) {
293     auto sender = currentMessage()->sender();
294     int icid = module_->nextIcIdx();
295     auto *ic = new Fcitx4InputContext(icid, instance_->inputContextManager(),
296                                       this, sender, appname);
297     auto group =
298         instance_->defaultFocusGroup(stringutils::concat("x11::", display_));
299     if (!group) {
300         group = instance_->defaultFocusGroup("x11:");
301     }
302     ic->setFocusGroup(group);
303     bus_->addObjectVTable(ic->path().path(), FCITX_INPUTCONTEXT_DBUS_INTERFACE,
304                           *ic);
305 
306     return std::make_tuple(icid, true, 0, 0, 0, 0);
307 }
308 
Fcitx4FrontendModule(Instance * instance)309 Fcitx4FrontendModule::Fcitx4FrontendModule(Instance *instance)
310     : instance_(instance),
311       table_(
312           [this](int display) {
313               try {
314                   fcitx4InputMethod_.emplace(
315                       display, std::make_unique<Fcitx4InputMethod>(
316                                    display, this, bus()));
317                   return true;
318               } catch (...) {
319               }
320               return false;
321           },
__anone2fb45700502(int display) 322           [this](int display) { fcitx4InputMethod_.erase(display); }) {
323 #ifdef ENABLE_X11
324     if (xcb()) {
325         createdCallback_ =
326             xcb()->call<IXCBModule::addConnectionCreatedCallback>(
327                 [this](const std::string &name, xcb_connection_t *, int,
__anone2fb45700602(const std::string &name, xcb_connection_t *, int, FocusGroup *) 328                        FocusGroup *) { addDisplay(name); });
329         closedCallback_ = xcb()->call<IXCBModule::addConnectionClosedCallback>(
__anone2fb45700702(const std::string &name, xcb_connection_t *) 330             [this](const std::string &name, xcb_connection_t *) {
331                 removeDisplay(name);
332             });
333     }
334 #endif
335     // Always create display number zero.
336     addDisplay("");
337 
338     event_ = instance_->watchEvent(
339         EventType::InputContextInputMethodActivated, EventWatcherPhase::Default,
__anone2fb45700802(Event &event) 340         [this](Event &event) {
341             auto &activated = static_cast<InputMethodActivatedEvent &>(event);
342             auto *ic = activated.inputContext();
343             if (strcmp(ic->frontend(), "fcitx4") == 0) {
344                 if (const auto *entry = instance_->inputMethodManager().entry(
345                         activated.name())) {
346                     static_cast<Fcitx4InputContext *>(ic)->updateIM(entry);
347                 }
348             }
349         });
350 }
351 
bus()352 dbus::Bus *Fcitx4FrontendModule::bus() {
353     return dbus()->call<IDBusModule::bus>();
354 }
355 
addDisplay(const std::string & name)356 void Fcitx4FrontendModule::addDisplay(const std::string &name) {
357     displayToHandle_.emplace(name, table_.add(getDisplayNumber(name), name));
358 }
359 
removeDisplay(const std::string & name)360 void Fcitx4FrontendModule::removeDisplay(const std::string &name) {
361     displayToHandle_.erase(name);
362 }
363 
364 class Fcitx4FrontendModuleFactory : public AddonFactory {
365 public:
create(AddonManager * manager)366     AddonInstance *create(AddonManager *manager) override {
367         return new Fcitx4FrontendModule(manager->instance());
368     }
369 };
370 } // namespace fcitx
371 
372 FCITX_ADDON_FACTORY(fcitx::Fcitx4FrontendModuleFactory);
373