Fcitx
instance.cpp
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #include "config.h"
8 
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <algorithm>
12 #include <array>
13 #include <cassert>
14 #include <csignal>
15 #include <cstdint>
16 #include <cstdlib>
17 #include <filesystem>
18 #include <functional>
19 #include <iostream>
20 #include <iterator>
21 #include <memory>
22 #include <optional>
23 #include <stdexcept>
24 #include <string>
25 #include <string_view>
26 #include <tuple>
27 #include <unordered_map>
28 #include <unordered_set>
29 #include <utility>
30 #include <vector>
31 #include <getopt.h>
32 #include "fcitx-config/configuration.h"
33 #include "fcitx-config/iniparser.h"
34 #include "fcitx-config/option.h"
36 #include "fcitx-utils/cutf8.h"
37 #include "fcitx-utils/environ.h"
38 #include "fcitx-utils/event.h"
39 #include "fcitx-utils/eventdispatcher.h"
40 #include "fcitx-utils/eventloopinterface.h"
41 #include "fcitx-utils/fs.h"
42 #include "fcitx-utils/handlertable.h"
43 #include "fcitx-utils/i18n.h"
44 #include "fcitx-utils/key.h"
45 #include "fcitx-utils/keysym.h"
46 #include "fcitx-utils/log.h"
47 #include "fcitx-utils/macros.h"
48 #include "fcitx-utils/misc.h"
49 #include "fcitx-utils/misc_p.h"
53 #include "fcitx-utils/utf8.h"
54 #include "../../modules/notifications/notifications_public.h"
55 #include "addonmanager.h"
56 #include "event.h"
57 #include "focusgroup.h"
58 #include "globalconfig.h"
59 #include "inputcontextmanager.h"
60 #include "inputcontextproperty.h"
61 #include "inputmethodengine.h"
62 #include "inputmethodentry.h"
63 #include "inputmethodgroup.h"
64 #include "inputmethodmanager.h"
65 #include "instance.h"
66 #include "instance_p.h"
67 #include "misc_p.h"
68 #include "statusarea.h"
69 #include "text.h"
70 #include "userinterface.h"
71 #include "userinterfacemanager.h"
72 
73 #ifdef HAVE_SYS_WAIT_H
74 #include <sys/wait.h>
75 #endif
76 
77 #ifdef ENABLE_X11
78 #define FCITX_NO_XCB
79 #include <../modules/xcb/xcb_public.h>
80 #endif
81 
82 #ifdef ENABLE_KEYBOARD
83 #include <xkbcommon/xkbcommon-compose.h>
84 #include <xkbcommon/xkbcommon.h>
85 #endif
86 
87 FCITX_DEFINE_LOG_CATEGORY(keyTrace, "key_trace");
88 
89 namespace fcitx {
90 
91 namespace {
92 
93 constexpr uint64_t AutoSaveMinInUsecs = 60ULL * 1000000ULL; // 30 minutes
94 constexpr uint64_t AutoSaveIdleTime = 60ULL * 1000000ULL; // 1 minutes
95 
96 FCITX_CONFIGURATION(DefaultInputMethod,
97  Option<std::vector<std::string>> defaultInputMethods{
98  this, "DefaultInputMethod", "DefaultInputMethod"};
99  Option<std::vector<std::string>> extraLayouts{
100  this, "ExtraLayout", "ExtraLayout"};);
101 
102 void initAsDaemon() {
103 #ifndef _WIN32
104  pid_t pid = fork();
105  if (pid > 0) {
106  waitpid(pid, nullptr, 0);
107  exit(0);
108  }
109  setsid();
110  auto oldint = signal(SIGINT, SIG_IGN);
111  auto oldhup = signal(SIGHUP, SIG_IGN);
112  auto oldquit = signal(SIGQUIT, SIG_IGN);
113  auto oldpipe = signal(SIGPIPE, SIG_IGN);
114  auto oldttou = signal(SIGTTOU, SIG_IGN);
115  auto oldttin = signal(SIGTTIN, SIG_IGN);
116  auto oldchld = signal(SIGCHLD, SIG_IGN);
117  if (fork() > 0) {
118  exit(0);
119  }
120  (void)chdir("/");
121 
122  signal(SIGINT, oldint);
123  signal(SIGHUP, oldhup);
124  signal(SIGQUIT, oldquit);
125  signal(SIGPIPE, oldpipe);
126  signal(SIGTTOU, oldttou);
127  signal(SIGTTIN, oldttin);
128  signal(SIGCHLD, oldchld);
129 #endif
130 }
131 
132 // Switch IM when these capabilities change.
133 bool shouldSwitchIM(const CapabilityFlags &oldFlags,
134  const CapabilityFlags &newFlags) {
135  const bool oldDisable = oldFlags.testAny(
136  CapabilityFlags{CapabilityFlag::Password, CapabilityFlag::Disable});
137  const bool newDisable = newFlags.testAny(
138  CapabilityFlags{CapabilityFlag::Password, CapabilityFlag::Disable});
139  return oldDisable != newDisable;
140 }
141 
142 } // namespace
143 
144 void InstanceArgument::printUsage() const {
145  std::cout
146  << "Usage: " << argv0 << " [Option]\n"
147  << " --disable <addon names>\tA comma separated list of addons to "
148  "be disabled.\n"
149  << "\t\t\t\t\"all\" can be used to disable all addons.\n"
150  << " --enable <addon names>\tA comma separated list of addons to "
151  "be enabled.\n"
152  << "\t\t\t\t\"all\" can be used to enable all addons.\n"
153  << "\t\t\t\tThis value will override the value in the flag "
154  "--disable.\n"
155  << " --verbose <logging rule>\tSet the logging rule for "
156  "displaying message.\n"
157  << "\t\t\t\tSyntax: category1=level1,category2=level2, ...\n"
158  << "\t\t\t\tE.g. default=4,key_trace=5\n"
159  << "\t\t\t\tLevels are numbers ranging from 0 to 5.\n"
160  << "\t\t\t\t\t0 - NoLog\n"
161  << "\t\t\t\t\t1 - Fatal\n"
162  << "\t\t\t\t\t2 - Error\n"
163  << "\t\t\t\t\t3 - Warn\n"
164  << "\t\t\t\t\t4 - Info (default)\n"
165  << "\t\t\t\t\t5 - Debug\n"
166  << "\t\t\t\tSome built-in categories are:\n"
167  << "\t\t\t\t\tdefault - miscellaneous category used by fcitx own "
168  "library.\n"
169  << "\t\t\t\t\tkey_trace - print the key event received by fcitx.\n"
170  << "\t\t\t\t\t\"*\" may be used to represent all logging "
171  "category.\n"
172  << " -u, --ui <addon name>\t\tSet the UI addon to be used.\n"
173  << " -d\t\t\t\tRun as a daemon.\n"
174  << " -D\t\t\t\tDo not run as a daemon (default).\n"
175  << " -s <seconds>\t\t\tNumber of seconds to wait before start.\n"
176  << " -k, --keep\t\t\tKeep running even the main display is "
177  "disconnected.\n"
178  << " -r, --replace\t\t\tReplace the existing instance.\n"
179  << " -o --option <option>\t\tPass the option to addons\n"
180  << "\t\t\t\t<option> is in format like:\n"
181  << "\t\t\t\tname1=opt1a:opt1b,name2=opt2a:opt2b... .\n"
182  << " -v, --version\t\t\tShow version and quit.\n"
183  << " -h, --help\t\t\tShow this help message and quit.\n";
184 }
185 
186 InstancePrivate::InstancePrivate(Instance *q) : QPtrHolder<Instance>(q) {
187 #ifdef ENABLE_KEYBOARD
188  const auto &locale = getCurrentLocale();
189  assert(!locale.empty());
190  xkbContext_.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
191  if (xkbContext_) {
192  xkb_context_set_log_level(xkbContext_.get(), XKB_LOG_LEVEL_CRITICAL);
193  xkbComposeTable_.reset(xkb_compose_table_new_from_locale(
194  xkbContext_.get(), locale.data(), XKB_COMPOSE_COMPILE_NO_FLAGS));
195  if (!xkbComposeTable_) {
196  FCITX_INFO()
197  << "Trying to fallback to compose table for en_US.UTF-8";
198  xkbComposeTable_.reset(xkb_compose_table_new_from_locale(
199  xkbContext_.get(), "en_US.UTF-8",
200  XKB_COMPOSE_COMPILE_NO_FLAGS));
201  }
202  if (!xkbComposeTable_) {
203  FCITX_WARN()
204  << "No compose table is loaded, you may want to check your "
205  "locale settings.";
206  }
207  }
208 #endif
209 }
210 
211 std::unique_ptr<HandlerTableEntry<EventHandler>>
212 InstancePrivate::watchEvent(EventType type, EventWatcherPhase phase,
213  EventHandler callback) {
214  return eventHandlers_[type][phase].add(std::move(callback));
215 }
216 
217 #ifdef ENABLE_KEYBOARD
218 xkb_keymap *InstancePrivate::keymap(const std::string &display,
219  const std::string &layout,
220  const std::string &variant) {
221  auto layoutAndVariant = stringutils::concat(layout, "-", variant);
222  if (auto *keymapPtr = findValue(keymapCache_[display], layoutAndVariant)) {
223  return (*keymapPtr).get();
224  }
225  struct xkb_rule_names names;
226  names.layout = layout.c_str();
227  names.variant = variant.c_str();
228  std::tuple<std::string, std::string, std::string> xkbParam;
229  if (auto *param = findValue(xkbParams_, display)) {
230  xkbParam = *param;
231  } else {
232  if (!xkbParams_.empty()) {
233  xkbParam = xkbParams_.begin()->second;
234  } else {
235  xkbParam = std::make_tuple(DEFAULT_XKB_RULES, "pc101", "");
236  }
237  }
238  if (globalConfig_.overrideXkbOption()) {
239  std::get<2>(xkbParam) = globalConfig_.customXkbOption();
240  }
241  names.rules = std::get<0>(xkbParam).c_str();
242  names.model = std::get<1>(xkbParam).c_str();
243  names.options = std::get<2>(xkbParam).c_str();
244  UniqueCPtr<xkb_keymap, xkb_keymap_unref> keymap(xkb_keymap_new_from_names(
245  xkbContext_.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS));
246  auto result =
247  keymapCache_[display].emplace(layoutAndVariant, std::move(keymap));
248  assert(result.second);
249  return result.first->second.get();
250 }
251 #endif
252 
253 std::pair<std::unordered_set<std::string>, std::unordered_set<std::string>>
254 InstancePrivate::overrideAddons() {
255  std::unordered_set<std::string> enabled;
256  std::unordered_set<std::string> disabled;
257  for (const auto &addon : globalConfig_.enabledAddons()) {
258  enabled.insert(addon);
259  }
260  for (const auto &addon : globalConfig_.disabledAddons()) {
261  enabled.erase(addon);
262  disabled.insert(addon);
263  }
264  for (const auto &addon : arg_.enableList) {
265  disabled.erase(addon);
266  enabled.insert(addon);
267  }
268  for (const auto &addon : arg_.disableList) {
269  enabled.erase(addon);
270  disabled.insert(addon);
271  }
272  return {enabled, disabled};
273 }
274 
275 void InstancePrivate::buildDefaultGroup() {
276  /// Figure out XKB layout information from system.
277  auto *defaultGroup = q_func()->defaultFocusGroup();
278  bool infoFound = false;
279  std::string layouts;
280  std::string variants;
281  auto guessLayout = [this, &layouts, &variants,
282  &infoFound](FocusGroup *focusGroup) {
283  // For now we can only do this on X11.
284  if (!stringutils::startsWith(focusGroup->display(), "x11:")) {
285  return true;
286  }
287 #ifdef ENABLE_X11
288  auto *xcb = addonManager_.addon("xcb");
289  auto x11Name = focusGroup->display().substr(4);
290  if (xcb) {
291  auto rules = xcb->call<IXCBModule::xkbRulesNames>(x11Name);
292  if (!rules[2].empty()) {
293  layouts = rules[2];
294  variants = rules[3];
295  infoFound = true;
296  return false;
297  }
298  }
299 #else
300  FCITX_UNUSED(this);
301  FCITX_UNUSED(layouts);
302  FCITX_UNUSED(variants);
303  FCITX_UNUSED(infoFound);
304 #endif
305  return true;
306  };
307  if (!defaultGroup || guessLayout(defaultGroup)) {
308  icManager_.foreachGroup(
309  [defaultGroup, &guessLayout](FocusGroup *focusGroup) {
310  if (defaultGroup == focusGroup) {
311  return true;
312  }
313  return guessLayout(focusGroup);
314  });
315  }
316  if (!infoFound) {
317  layouts = "us";
318  variants = "";
319  }
320 
321  // layouts and variants are comma separated list for layout information.
322  constexpr char imNamePrefix[] = "keyboard-";
323  auto layoutTokens =
324  stringutils::split(layouts, ",", stringutils::SplitBehavior::KeepEmpty);
325  auto variantTokens = stringutils::split(
326  variants, ",", stringutils::SplitBehavior::KeepEmpty);
327  auto size = std::max(layoutTokens.size(), variantTokens.size());
328  // Make sure we have token to be the same size.
329  layoutTokens.resize(size);
330  variantTokens.resize(size);
331 
332  OrderedSet<std::string> imLayouts;
333  for (decltype(size) i = 0; i < size; i++) {
334  if (layoutTokens[i].empty()) {
335  continue;
336  }
337  std::string layoutName = layoutTokens[i];
338  if (!variantTokens[i].empty()) {
339  layoutName = stringutils::concat(layoutName, "-", variantTokens[i]);
340  }
341 
342  // Skip the layout if we don't have it.
343  if (!imManager_.entry(stringutils::concat(imNamePrefix, layoutName))) {
344  continue;
345  }
346  // Avoid add duplicate entry. layout might have weird duplicate.
347  imLayouts.pushBack(layoutName);
348  }
349 
350  // Load the default profile.
351  auto lang = stripLanguage(getCurrentLanguage());
352  DefaultInputMethod defaultIMConfig;
353  readAsIni(defaultIMConfig, StandardPathsType::PkgData,
354  std::filesystem::path("default") / lang);
355 
356  // Add extra layout from profile.
357  for (const auto &extraLayout : defaultIMConfig.extraLayouts.value()) {
358  if (!imManager_.entry(stringutils::concat(imNamePrefix, extraLayout))) {
359  continue;
360  }
361  imLayouts.pushBack(extraLayout);
362  }
363 
364  // Make sure imLayouts is not empty.
365  if (imLayouts.empty()) {
366  imLayouts.pushBack("us");
367  }
368 
369  // Figure out the first available default input method.
370  std::string defaultIM;
371  for (const auto &im : defaultIMConfig.defaultInputMethods.value()) {
372  if (imManager_.entry(im)) {
373  defaultIM = im;
374  break;
375  }
376  }
377 
378  // Create a group for each layout.
379  std::vector<std::string> groupOrders;
380  for (const auto &imLayout : imLayouts) {
381  std::string groupName;
382  if (imLayouts.size() == 1) {
383  groupName = _("Default");
384  } else {
385  groupName = _("Group {}", imManager_.groupCount() + 1);
386  }
387  imManager_.addEmptyGroup(groupName);
388  groupOrders.push_back(groupName);
389  InputMethodGroup group(groupName);
390  group.inputMethodList().emplace_back(
391  InputMethodGroupItem(stringutils::concat(imNamePrefix, imLayout)));
392  if (!defaultIM.empty()) {
393  group.inputMethodList().emplace_back(
394  InputMethodGroupItem(defaultIM));
395  }
396  FCITX_INFO() << "Items in " << groupName << ": "
397  << group.inputMethodList();
398  group.setDefaultLayout(imLayout);
399  imManager_.setGroup(std::move(group));
400  }
401  FCITX_INFO() << "Generated groups: " << groupOrders;
402  imManager_.setGroupOrder(groupOrders);
403 }
404 
405 void InstancePrivate::showInputMethodInformation(InputContext *ic) {
406  FCITX_Q();
407  auto *inputState = ic->propertyFor(&inputStateFactory_);
408  auto *engine = q->inputMethodEngine(ic);
409  const auto *entry = q->inputMethodEntry(ic);
410  auto &imManager = q->inputMethodManager();
411 
412  if (!inputState->isActive() &&
413  !globalConfig_.showFirstInputMethodInformation()) {
414  return;
415  }
416 
417  std::string display;
418  if (engine) {
419  auto subMode = engine->subMode(*entry, *ic);
420  auto subModeLabel = engine->subModeLabel(*entry, *ic);
421  auto name = globalConfig_.compactInputMethodInformation() &&
422  !entry->label().empty()
423  ? entry->label()
424  : entry->name();
425  if (globalConfig_.compactInputMethodInformation() &&
426  !subModeLabel.empty()) {
427  display = std::move(subModeLabel);
428  } else if (subMode.empty()) {
429  display = std::move(name);
430  } else {
431  display = _("{0} ({1})", name, subMode);
432  }
433  } else if (entry) {
434  display = _("{0} (Not available)", entry->name());
435  } else {
436  display = _("(Not available)");
437  }
438  if (!globalConfig_.compactInputMethodInformation() &&
439  imManager.groupCount() > 1) {
440  display = _("Group {0}: {1}", imManager.currentGroup().name(), display);
441  }
442  inputState->showInputMethodInformation(display);
443 }
444 
445 bool InstancePrivate::canActivate(InputContext *ic) {
446  FCITX_Q();
447  if (!q->canTrigger()) {
448  return false;
449  }
450  auto *inputState = ic->propertyFor(&inputStateFactory_);
451  return !inputState->isActive();
452 }
453 
454 bool InstancePrivate::canDeactivate(InputContext *ic) {
455  FCITX_Q();
456  if (!q->canTrigger()) {
457  return false;
458  }
459  auto *inputState = ic->propertyFor(&inputStateFactory_);
460  return inputState->isActive();
461 }
462 
463 void InstancePrivate::navigateGroup(InputContext *ic, const Key &key,
464  bool forward) {
465  auto *inputState = ic->propertyFor(&inputStateFactory_);
466  inputState->pendingGroupIndex_ =
467  (inputState->pendingGroupIndex_ +
468  (forward ? 1 : imManager_.groupCount() - 1)) %
469  imManager_.groupCount();
470  FCITX_DEBUG() << "Switch to group " << inputState->pendingGroupIndex_;
471 
472  if (notifications_ && !isSingleKey(key)) {
473  notifications_->call<INotifications::showTip>(
474  "enumerate-group", _("Input Method"), "input-keyboard",
475  _("Switch group"),
476  _("Switch group to {0}",
477  imManager_.groups()[inputState->pendingGroupIndex_]),
478  3000);
479  }
480 }
481 
482 void InstancePrivate::acceptGroupChange(const Key &key, InputContext *ic) {
483  FCITX_DEBUG() << "Accept group change, isSingleKey: " << key;
484 
485  auto *inputState = ic->propertyFor(&inputStateFactory_);
486  auto groups = imManager_.groups();
487  if (groups.size() > inputState->pendingGroupIndex_) {
488  if (isSingleKey(key)) {
489  FCITX_DEBUG() << "EnumerateGroupTo: "
490  << inputState->pendingGroupIndex_ << " " << key;
491  imManager_.enumerateGroupTo(groups[inputState->pendingGroupIndex_]);
492  } else {
493  FCITX_DEBUG() << "SetCurrentGroup: "
494  << inputState->pendingGroupIndex_ << " " << key;
495  imManager_.setCurrentGroup(groups[inputState->pendingGroupIndex_]);
496  }
497  }
498  inputState->pendingGroupIndex_ = 0;
499 }
500 
501 InputState::InputState(InstancePrivate *d, InputContext *ic)
502  : d_ptr(d), ic_(ic) {
503  active_ = d->globalConfig_.activeByDefault();
504 #ifdef ENABLE_KEYBOARD
505  if (d->xkbComposeTable_) {
506  xkbComposeState_.reset(xkb_compose_state_new(
507  d->xkbComposeTable_.get(), XKB_COMPOSE_STATE_NO_FLAGS));
508  }
509 #endif
510 }
511 
512 void InputState::showInputMethodInformation(const std::string &name) {
513  ic_->inputPanel().setAuxUp(Text(name));
514  ic_->updateUserInterface(UserInterfaceComponent::InputPanel);
515  lastInfo_ = name;
516  imInfoTimer_ = d_ptr->eventLoop_.addTimeEvent(
517  CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
518  [this](EventSourceTime *, uint64_t) {
519  hideInputMethodInfo();
520  return true;
521  });
522 }
523 
524 #ifdef ENABLE_KEYBOARD
525 xkb_state *InputState::customXkbState(bool refresh) {
526  auto *instance = d_ptr->q_func();
527  const InputMethodGroup &group = d_ptr->imManager_.currentGroup();
528  const auto im = instance->inputMethod(ic_);
529  auto layout = group.layoutFor(im);
530  if (layout.empty() && stringutils::startsWith(im, "keyboard-")) {
531  layout = im.substr(9);
532  }
533  if (layout.empty() || layout == group.defaultLayout()) {
534  // Use system one.
535  xkbState_.reset();
536  modsAllReleased_ = false;
537  lastXkbLayout_.clear();
538  return nullptr;
539  }
540 
541  if (layout == lastXkbLayout_ && !refresh) {
542  return xkbState_.get();
543  }
544 
545  lastXkbLayout_ = layout;
546  const auto layoutAndVariant = parseLayout(layout);
547  if (auto *keymap = d_ptr->keymap(ic_->display(), layoutAndVariant.first,
548  layoutAndVariant.second)) {
549  xkbState_.reset(xkb_state_new(keymap));
550  } else {
551  xkbState_.reset();
552  }
553  modsAllReleased_ = false;
554  return xkbState_.get();
555 }
556 #endif
557 
558 void InputState::setActive(bool active) {
559  if (active_ != active) {
560  active_ = active;
561  ic_->updateProperty(&d_ptr->inputStateFactory_);
562  }
563 }
564 
565 void InputState::setLocalIM(const std::string &localIM) {
566  if (localIM_ != localIM) {
567  localIM_ = localIM;
568  ic_->updateProperty(&d_ptr->inputStateFactory_);
569  }
570 }
571 
572 void InputState::copyTo(InputContextProperty *other) {
573  auto *otherState = static_cast<InputState *>(other);
574  if (otherState->active_ == active_ && otherState->localIM_ == localIM_) {
575  return;
576  }
577 
578  if (otherState->ic_->hasFocus()) {
579  FCITX_DEBUG() << "Sync state to focused ic: "
580  << otherState->ic_->program();
581  CheckInputMethodChanged imChangedRAII(otherState->ic_, d_ptr);
582  otherState->active_ = active_;
583  otherState->localIM_ = localIM_;
584  } else {
585  otherState->active_ = active_;
586  otherState->localIM_ = localIM_;
587  }
588 }
589 
590 void InputState::reset() {
591 #ifdef ENABLE_KEYBOARD
592  if (xkbComposeState_) {
593  xkb_compose_state_reset(xkbComposeState_.get());
594  }
595 #endif
596  pendingGroupIndex_ = 0;
597  keyReleased_ = -1;
598  lastKeyPressed_ = Key();
599  lastKeyPressedTime_ = 0;
600  totallyReleased_ = true;
601 }
602 
603 void InputState::hideInputMethodInfo() {
604  if (!imInfoTimer_) {
605  return;
606  }
607  imInfoTimer_.reset();
608  auto &panel = ic_->inputPanel();
609  if (panel.auxDown().empty() && panel.preedit().empty() &&
610  panel.clientPreedit().empty() &&
611  (!panel.candidateList() || panel.candidateList()->empty()) &&
612  panel.auxUp().size() == 1 && panel.auxUp().stringAt(0) == lastInfo_) {
613  panel.reset();
614  ic_->updateUserInterface(UserInterfaceComponent::InputPanel);
615  }
616 }
617 
618 #ifdef ENABLE_KEYBOARD
619 void InputState::resetXkbState() {
620  lastXkbLayout_.clear();
621  xkbState_.reset();
622 }
623 #endif
624 
625 CheckInputMethodChanged::CheckInputMethodChanged(InputContext *ic,
626  InstancePrivate *instance)
627  : instance_(instance->q_func()), instancePrivate_(instance),
628  ic_(ic->watch()), inputMethod_(instance_->inputMethod(ic)),
629  reason_(InputMethodSwitchedReason::Other) {
630  auto *inputState = ic->propertyFor(&instance->inputStateFactory_);
631  if (!inputState->imChanged_) {
632  inputState->imChanged_ = this;
633  } else {
634  ic_.unwatch();
635  }
636 }
637 
638 CheckInputMethodChanged::~CheckInputMethodChanged() {
639  if (!ic_.isValid()) {
640  return;
641  }
642  auto *ic = ic_.get();
643  auto *inputState = ic->propertyFor(&instancePrivate_->inputStateFactory_);
644  inputState->imChanged_ = nullptr;
645  if (inputMethod_ != instance_->inputMethod(ic) && !ignore_) {
646  instance_->postEvent(
647  InputContextSwitchInputMethodEvent(reason_, inputMethod_, ic));
648  }
649 }
650 
651 Instance::Instance(int argc, char **argv) {
652  InstanceArgument arg;
653  arg.parseOption(argc, argv);
654  if (arg.quietQuit) {
655  throw InstanceQuietQuit();
656  }
657 
658  if (arg.runAsDaemon) {
659  initAsDaemon();
660  }
661 
662  if (arg.overrideDelay > 0) {
663  sleep(arg.overrideDelay);
664  }
665 
666  // we need fork before this
667  d_ptr = std::make_unique<InstancePrivate>(this);
668  FCITX_D();
669  d->arg_ = arg;
670  d->eventDispatcher_.attach(&d->eventLoop_);
671  d->addonManager_.setInstance(this);
672  d->addonManager_.setAddonOptions(arg.addonOptions_);
673  d->icManager_.setInstance(this);
674  d->connections_.emplace_back(
675  d->imManager_.connect<InputMethodManager::CurrentGroupAboutToChange>(
676  [this, d](const std::string &lastGroup) {
677  d->icManager_.foreachFocused([this](InputContext *ic) {
678  assert(ic->hasFocus());
679  InputContextSwitchInputMethodEvent event(
680  InputMethodSwitchedReason::GroupChange, inputMethod(ic),
681  ic);
682  deactivateInputMethod(event);
683  return true;
684  });
685  d->lastGroup_ = lastGroup;
687  }));
688  d->connections_.emplace_back(
689  d->imManager_.connect<InputMethodManager::CurrentGroupChanged>(
690  [this, d](const std::string &newGroup) {
691  d->icManager_.foreachFocused([this](InputContext *ic) {
692  assert(ic->hasFocus());
693  InputContextSwitchInputMethodEvent event(
694  InputMethodSwitchedReason::GroupChange, "", ic);
695  activateInputMethod(event);
696  return true;
697  });
698  postEvent(InputMethodGroupChangedEvent());
699  if (!d->lastGroup_.empty() && !newGroup.empty() &&
700  d->lastGroup_ != newGroup && d->notifications_ &&
701  d->imManager_.groupCount() > 1) {
702  d->notifications_->call<INotifications::showTip>(
703  "enumerate-group", _("Input Method"), "input-keyboard",
704  _("Switch group"),
705  _("Switched group to {0}",
706  d->imManager_.currentGroup().name()),
707  3000);
708  }
709  d->lastGroup_ = newGroup;
710  }));
711 
712  d->eventWatchers_.emplace_back(d->watchEvent(
713  EventType::InputContextCapabilityAboutToChange,
714  EventWatcherPhase::ReservedFirst, [this](Event &event) {
715  auto &capChanged =
716  static_cast<CapabilityAboutToChangeEvent &>(event);
717  if (!capChanged.inputContext()->hasFocus()) {
718  return;
719  }
720 
721  if (!shouldSwitchIM(capChanged.oldFlags(), capChanged.newFlags())) {
722  return;
723  }
724 
727  inputMethod(capChanged.inputContext()),
728  capChanged.inputContext());
729  deactivateInputMethod(switchIM);
730  }));
731  d->eventWatchers_.emplace_back(d->watchEvent(
732  EventType::InputContextCapabilityChanged,
733  EventWatcherPhase::ReservedFirst, [this](Event &event) {
734  auto &capChanged = static_cast<CapabilityChangedEvent &>(event);
735  if (!capChanged.inputContext()->hasFocus()) {
736  return;
737  }
738 
739  if (!shouldSwitchIM(capChanged.oldFlags(), capChanged.newFlags())) {
740  return;
741  }
742 
745  capChanged.inputContext());
746  activateInputMethod(switchIM);
747  }));
748 
749  d->eventWatchers_.emplace_back(watchEvent(
750  EventType::InputContextKeyEvent, EventWatcherPhase::InputMethod,
751  [this, d](Event &event) {
752  auto &keyEvent = static_cast<KeyEvent &>(event);
753  auto *ic = keyEvent.inputContext();
754  CheckInputMethodChanged imChangedRAII(ic, d);
755  auto origKey = keyEvent.origKey().normalize();
756 
757  struct {
758  const KeyList &list;
759  std::function<bool()> check;
760  std::function<void(bool)> trigger;
761  } keyHandlers[] = {
762  {.list = d->globalConfig_.triggerKeys(),
763  .check = [this]() { return canTrigger(); },
764  .trigger =
765  [this, ic](bool totallyReleased) {
766  return trigger(ic, totallyReleased);
767  }},
768  {.list = d->globalConfig_.altTriggerKeys(),
769  .check = [this, ic]() { return canAltTrigger(ic); },
770  .trigger = [this, ic](bool) { return altTrigger(ic); }},
771  {.list = d->globalConfig_.activateKeys(),
772  .check = [ic, d]() { return d->canActivate(ic); },
773  .trigger = [this, ic](bool) { return activate(ic); }},
774  {.list = d->globalConfig_.deactivateKeys(),
775  .check = [ic, d]() { return d->canDeactivate(ic); },
776  .trigger = [this, ic](bool) { return deactivate(ic); }},
777  {.list = d->globalConfig_.enumerateForwardKeys(),
778  .check = [this, ic]() { return canEnumerate(ic); },
779  .trigger = [this, ic](bool) { return enumerate(ic, true); }},
780  {.list = d->globalConfig_.enumerateBackwardKeys(),
781  .check = [this, ic]() { return canEnumerate(ic); },
782  .trigger = [this, ic](bool) { return enumerate(ic, false); }},
783  {.list = d->globalConfig_.enumerateGroupForwardKeys(),
784  .check = [this]() { return canChangeGroup(); },
785  .trigger = [ic, d, origKey](
786  bool) { d->navigateGroup(ic, origKey, true); }},
787  {.list = d->globalConfig_.enumerateGroupBackwardKeys(),
788  .check = [this]() { return canChangeGroup(); },
789  .trigger =
790  [ic, d, origKey](bool) {
791  d->navigateGroup(ic, origKey, false);
792  }},
793  };
794 
795  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
796  int keyReleased = inputState->keyReleased_;
797  Key lastKeyPressed = inputState->lastKeyPressed_;
798  // Keep this value, and reset them in the state
799  inputState->keyReleased_ = -1;
800  const bool isModifier = origKey.isModifier();
801  if (keyEvent.isRelease()) {
802  int idx = 0;
803  for (auto &keyHandler : keyHandlers) {
804  if (keyReleased == idx &&
805  origKey.isReleaseOfModifier(lastKeyPressed) &&
806  keyHandler.check()) {
807  if (isModifier) {
808  if (d->globalConfig_.checkModifierOnlyKeyTimeout(
809  inputState->lastKeyPressedTime_)) {
810  keyHandler.trigger(
811  inputState->totallyReleased_);
812  }
813  inputState->lastKeyPressedTime_ = 0;
814  if (origKey.hasModifier()) {
815  inputState->totallyReleased_ = false;
816  }
817  }
818  keyEvent.filter();
819  break;
820  }
821  idx++;
822  }
823  if (isSingleModifier(origKey)) {
824  inputState->totallyReleased_ = true;
825  }
826  }
827 
828  if (inputState->pendingGroupIndex_ &&
829  inputState->totallyReleased_) {
830  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
831  if (inputState->imChanged_) {
832  inputState->imChanged_->ignore();
833  }
834  d->acceptGroupChange(lastKeyPressed, ic);
835  inputState->lastKeyPressed_ = Key();
836  }
837 
838  if (!keyEvent.filtered() && !keyEvent.isRelease()) {
839  int idx = 0;
840  for (auto &keyHandler : keyHandlers) {
841  auto keyIdx = origKey.keyListIndex(keyHandler.list);
842  if (keyIdx >= 0 && keyHandler.check()) {
843  inputState->keyReleased_ = idx;
844  inputState->lastKeyPressed_ = origKey;
845  if (isModifier) {
846  inputState->lastKeyPressedTime_ =
847  now(CLOCK_MONOTONIC);
848  // don't forward to input method, but make it pass
849  // through to client.
850  keyEvent.filter();
851  return;
852  }
853  keyHandler.trigger(inputState->totallyReleased_);
854  if (origKey.hasModifier()) {
855  inputState->totallyReleased_ = false;
856  }
857  keyEvent.filterAndAccept();
858  return;
859  }
860  idx++;
861  }
862  }
863  }));
864  d->eventWatchers_.emplace_back(watchEvent(
865  EventType::InputContextKeyEvent, EventWatcherPhase::PreInputMethod,
866  [d](Event &event) {
867  auto &keyEvent = static_cast<KeyEvent &>(event);
868  auto *ic = keyEvent.inputContext();
869  if (!keyEvent.isRelease() &&
870  keyEvent.key().checkKeyList(
871  d->globalConfig_.togglePreeditKeys())) {
872  // Clear client preedit on disable.
873  ic->reset();
874  ic->setEnablePreedit(!ic->isPreeditEnabled());
875  if (d->notifications_) {
876  d->notifications_->call<INotifications::showTip>(
877  "toggle-preedit", _("Input Method"), "", _("Preedit"),
878  ic->isPreeditEnabled() ? _("Preedit enabled")
879  : _("Preedit disabled"),
880  3000);
881  }
882  keyEvent.filterAndAccept();
883  }
884  }));
885  d->eventWatchers_.emplace_back(d->watchEvent(
886  EventType::InputContextKeyEvent, EventWatcherPhase::ReservedFirst,
887  [d](Event &event) {
888  // Update auto save.
889  d->idleStartTimestamp_ = now(CLOCK_MONOTONIC);
890  auto &keyEvent = static_cast<KeyEvent &>(event);
891  auto *ic = keyEvent.inputContext();
892  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
893 #ifdef ENABLE_KEYBOARD
894  auto *xkbState = inputState->customXkbState();
895  if (xkbState) {
896  if (auto *mods = findValue(d->stateMask_, ic->display())) {
897  FCITX_KEYTRACE() << "Update mask to customXkbState";
898  // Keep latched, but propagate depressed optionally and
899  // locked.
900  uint32_t depressed;
901  if (inputState->isModsAllReleased()) {
902  depressed = xkb_state_serialize_mods(
903  xkbState, XKB_STATE_MODS_DEPRESSED);
904  } else {
905  depressed = std::get<0>(*mods);
906  }
907  if (std::get<0>(*mods) == 0) {
908  inputState->setModsAllReleased();
909  }
910  auto latched = xkb_state_serialize_mods(
911  xkbState, XKB_STATE_MODS_LATCHED);
912  auto locked = std::get<2>(*mods);
913 
914  // set modifiers in depressed if they don't appear in any of
915  // the final masks
916  // depressed |= ~(depressed | latched | locked);
917  FCITX_DEBUG()
918  << depressed << " " << latched << " " << locked;
919  xkb_state_update_mask(xkbState, depressed, latched, locked,
920  0, 0, 0);
921  }
922  const uint32_t effective = xkb_state_serialize_mods(
923  xkbState, XKB_STATE_MODS_EFFECTIVE);
924  auto newSym = xkb_state_key_get_one_sym(
925  xkbState, keyEvent.rawKey().code());
926  auto newModifier = KeyStates(effective);
927  auto *keymap = xkb_state_get_keymap(xkbState);
928  if (keyEvent.rawKey().states().test(KeyState::Repeat) &&
929  xkb_keymap_key_repeats(keymap, keyEvent.rawKey().code())) {
930  newModifier |= KeyState::Repeat;
931  }
932 
933  const uint32_t modsDepressed = xkb_state_serialize_mods(
934  xkbState, XKB_STATE_MODS_DEPRESSED);
935  const uint32_t modsLatched =
936  xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED);
937  const uint32_t modsLocked =
938  xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED);
939  FCITX_KEYTRACE() << "Current mods: " << modsDepressed << " "
940  << modsLatched << " " << modsLocked;
941  auto newCode = keyEvent.rawKey().code();
942  Key key(static_cast<KeySym>(newSym), newModifier, newCode);
943  FCITX_KEYTRACE()
944  << "Custom Xkb translated Key: " << key.toString();
945  keyEvent.setRawKey(key);
946  }
947 #endif
948  FCITX_KEYTRACE() << "KeyEvent: " << keyEvent.key()
949  << " rawKey: " << keyEvent.rawKey()
950  << " origKey: " << keyEvent.origKey()
951  << " Release:" << keyEvent.isRelease()
952  << " keycode: " << keyEvent.origKey().code()
953  << " program: " << ic->program();
954 
955  if (keyEvent.isRelease()) {
956  return;
957  }
958  inputState->hideInputMethodInfo();
959  }));
960  d->eventWatchers_.emplace_back(
962  EventWatcherPhase::InputMethod, [this](Event &event) {
963  auto &keyEvent = static_cast<KeyEvent &>(event);
964  auto *ic = keyEvent.inputContext();
965  auto *engine = inputMethodEngine(ic);
966  const auto *entry = inputMethodEntry(ic);
967  if (!engine || !entry) {
968  return;
969  }
970  engine->keyEvent(*entry, keyEvent);
971  }));
972  d->eventWatchers_.emplace_back(watchEvent(
973  EventType::InputContextVirtualKeyboardEvent,
974  EventWatcherPhase::InputMethod, [this](Event &event) {
975  auto &keyEvent = static_cast<VirtualKeyboardEvent &>(event);
976  auto *ic = keyEvent.inputContext();
977  auto *engine = inputMethodEngine(ic);
978  const auto *entry = inputMethodEntry(ic);
979  if (!engine || !entry) {
980  return;
981  }
982  engine->virtualKeyboardEvent(*entry, keyEvent);
983  }));
984  d->eventWatchers_.emplace_back(watchEvent(
985  EventType::InputContextInvokeAction, EventWatcherPhase::InputMethod,
986  [this](Event &event) {
987  auto &invokeActionEvent = static_cast<InvokeActionEvent &>(event);
988  auto *ic = invokeActionEvent.inputContext();
989  auto *engine = inputMethodEngine(ic);
990  const auto *entry = inputMethodEntry(ic);
991  if (!engine || !entry) {
992  return;
993  }
994  engine->invokeAction(*entry, invokeActionEvent);
995  }));
996  d->eventWatchers_.emplace_back(d->watchEvent(
997  EventType::InputContextKeyEvent, EventWatcherPhase::ReservedLast,
998  [this](Event &event) {
999  auto &keyEvent = static_cast<KeyEvent &>(event);
1000  auto *ic = keyEvent.inputContext();
1001  auto *engine = inputMethodEngine(ic);
1002  const auto *entry = inputMethodEntry(ic);
1003  if (!engine || !entry) {
1004  return;
1005  }
1006  engine->filterKey(*entry, keyEvent);
1007  emit<Instance::KeyEventResult>(keyEvent);
1008 #ifdef ENABLE_KEYBOARD
1009  if (keyEvent.forward()) {
1010  FCITX_D();
1011  // Always let the release key go through, since it shouldn't
1012  // produce character. Otherwise it may wrongly trigger wayland
1013  // client side repetition.
1014  if (keyEvent.isRelease()) {
1015  keyEvent.filter();
1016  return;
1017  }
1018  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1019  if (auto *xkbState = inputState->customXkbState()) {
1020  if (auto utf32 = xkb_state_key_get_utf32(
1021  xkbState, keyEvent.key().code())) {
1022  // Ignore newline, return, backspace, tab, and delete.
1023  if (utf32 == '\n' || utf32 == '\b' || utf32 == '\r' ||
1024  utf32 == '\t' || utf32 == '\033' ||
1025  utf32 == '\x7f') {
1026  return;
1027  }
1028  if (keyEvent.key().states().test(KeyState::Ctrl) ||
1029  keyEvent.rawKey().sym() ==
1030  keyEvent.origKey().sym()) {
1031  return;
1032  }
1033  FCITX_KEYTRACE() << "Will commit char: " << utf32;
1034  ic->commitString(utf8::UCS4ToUTF8(utf32));
1035  keyEvent.filterAndAccept();
1036  } else if (!keyEvent.key().states().test(KeyState::Ctrl) &&
1037  keyEvent.rawKey().sym() !=
1038  keyEvent.origKey().sym() &&
1039  Key::keySymToUnicode(keyEvent.origKey().sym()) !=
1040  0) {
1041  // filter key for the case that: origKey will produce
1042  // character, while the translated will not.
1043  keyEvent.filterAndAccept();
1044  }
1045  }
1046  }
1047 #endif
1048  }));
1049  d->eventWatchers_.emplace_back(d->watchEvent(
1050  EventType::InputContextFocusIn, EventWatcherPhase::ReservedFirst,
1051  [this, d](Event &event) {
1052  auto &icEvent = static_cast<InputContextEvent &>(event);
1053  auto isSameProgram = [&icEvent, d]() {
1054  // Check if they are same IC, or they are same program.
1055  return (icEvent.inputContext() == d->lastUnFocusedIc_.get()) ||
1056  (!icEvent.inputContext()->program().empty() &&
1057  (icEvent.inputContext()->program() ==
1058  d->lastUnFocusedProgram_));
1059  };
1060 
1061  if (d->globalConfig_.resetStateWhenFocusIn() ==
1062  PropertyPropagatePolicy::All ||
1063  (d->globalConfig_.resetStateWhenFocusIn() ==
1064  PropertyPropagatePolicy::Program &&
1065  !isSameProgram())) {
1066  if (d->globalConfig_.activeByDefault()) {
1067  activate(icEvent.inputContext());
1068  } else {
1069  deactivate(icEvent.inputContext());
1070  }
1071  }
1072 
1073  activateInputMethod(icEvent);
1074 
1075  auto *inputContext = icEvent.inputContext();
1076  if (!inputContext->clientControlVirtualkeyboardShow()) {
1077  inputContext->showVirtualKeyboard();
1078  }
1079 
1080  if (!d->globalConfig_.showInputMethodInformationWhenFocusIn() ||
1081  icEvent.inputContext()->capabilityFlags().test(
1083  return;
1084  }
1085  // Give some time because the cursor location may need some time
1086  // to be updated.
1087  d->focusInImInfoTimer_ = d->eventLoop_.addTimeEvent(
1088  CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 30000, 0,
1089  [d, icRef = icEvent.inputContext()->watch()](EventSourceTime *,
1090  uint64_t) {
1091  // Check if ic is still valid and has focus.
1092  if (auto *ic = icRef.get(); ic && ic->hasFocus()) {
1093  d->showInputMethodInformation(ic);
1094  }
1095  return true;
1096  });
1097  }));
1098  d->eventWatchers_.emplace_back(d->watchEvent(
1099  EventType::InputContextFocusOut, EventWatcherPhase::ReservedFirst,
1100  [d](Event &event) {
1101  auto &icEvent = static_cast<InputContextEvent &>(event);
1102  auto *ic = icEvent.inputContext();
1103  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1104  inputState->reset();
1105  if (!ic->capabilityFlags().test(
1106  CapabilityFlag::ClientUnfocusCommit)) {
1107  // do server side commit
1108  auto commit =
1109  ic->inputPanel().clientPreedit().toStringForCommit();
1110  if (!commit.empty()) {
1111  ic->commitString(commit);
1112  }
1113  }
1114  }));
1115  d->eventWatchers_.emplace_back(d->watchEvent(
1116  EventType::InputContextFocusOut, EventWatcherPhase::InputMethod,
1117  [this, d](Event &event) {
1118  auto &icEvent = static_cast<InputContextEvent &>(event);
1119  d->lastUnFocusedProgram_ = icEvent.inputContext()->program();
1120  d->lastUnFocusedIc_ = icEvent.inputContext()->watch();
1121  deactivateInputMethod(icEvent);
1122 
1123  auto *inputContext = icEvent.inputContext();
1124  if (!inputContext->clientControlVirtualkeyboardHide()) {
1125  inputContext->hideVirtualKeyboard();
1126  }
1127  }));
1128  d->eventWatchers_.emplace_back(d->watchEvent(
1129  EventType::InputContextReset, EventWatcherPhase::ReservedFirst,
1130  [d](Event &event) {
1131  auto &icEvent = static_cast<InputContextEvent &>(event);
1132  auto *ic = icEvent.inputContext();
1133  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1134  inputState->reset();
1135  }));
1136  d->eventWatchers_.emplace_back(
1137  watchEvent(EventType::InputContextReset, EventWatcherPhase::InputMethod,
1138  [this](Event &event) {
1139  auto &icEvent = static_cast<InputContextEvent &>(event);
1140  auto *ic = icEvent.inputContext();
1141  if (!ic->hasFocus()) {
1142  return;
1143  }
1144  auto *engine = inputMethodEngine(ic);
1145  const auto *entry = inputMethodEntry(ic);
1146  if (!engine || !entry) {
1147  return;
1148  }
1149  engine->reset(*entry, icEvent);
1150  }));
1151  d->eventWatchers_.emplace_back(d->watchEvent(
1153  EventWatcherPhase::ReservedFirst, [this](Event &event) {
1154  auto &icEvent =
1155  static_cast<InputContextSwitchInputMethodEvent &>(event);
1156  auto *ic = icEvent.inputContext();
1157  if (!ic->hasFocus()) {
1158  return;
1159  }
1160  deactivateInputMethod(icEvent);
1161  activateInputMethod(icEvent);
1162  }));
1163  d->eventWatchers_.emplace_back(d->watchEvent(
1165  EventWatcherPhase::ReservedLast, [this, d](Event &event) {
1166  auto &icEvent =
1167  static_cast<InputContextSwitchInputMethodEvent &>(event);
1168  auto *ic = icEvent.inputContext();
1169  if (!ic->hasFocus()) {
1170  return;
1171  }
1172 
1173  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1174  inputState->lastIMChangeIsAltTrigger_ =
1175  icEvent.reason() == InputMethodSwitchedReason::AltTrigger;
1176 
1177  if ((icEvent.reason() != InputMethodSwitchedReason::Trigger &&
1178  icEvent.reason() != InputMethodSwitchedReason::AltTrigger &&
1179  icEvent.reason() != InputMethodSwitchedReason::Enumerate &&
1180  icEvent.reason() != InputMethodSwitchedReason::Activate &&
1181  icEvent.reason() != InputMethodSwitchedReason::Other &&
1182  icEvent.reason() != InputMethodSwitchedReason::GroupChange &&
1183  icEvent.reason() != InputMethodSwitchedReason::Deactivate)) {
1184  return;
1185  }
1186  showInputMethodInformation(ic);
1187  }));
1188  d->eventWatchers_.emplace_back(
1189  d->watchEvent(EventType::InputMethodGroupChanged,
1190  EventWatcherPhase::ReservedLast, [this, d](Event &) {
1191  // Use a timer here. so we can get focus back to real
1192  // window.
1193  d->imGroupInfoTimer_ = d->eventLoop_.addTimeEvent(
1194  CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 30000, 0,
1195  [this](EventSourceTime *, uint64_t) {
1196  inputContextManager().foreachFocused(
1197  [this](InputContext *ic) {
1198  showInputMethodInformation(ic);
1199  return true;
1200  });
1201  return true;
1202  });
1203  }));
1204 
1205  d->eventWatchers_.emplace_back(d->watchEvent(
1206  EventType::InputContextUpdateUI, EventWatcherPhase::ReservedFirst,
1207  [d](Event &event) {
1208  auto &icEvent = static_cast<InputContextUpdateUIEvent &>(event);
1209  if (icEvent.immediate()) {
1210  d->uiManager_.update(icEvent.component(),
1211  icEvent.inputContext());
1212  d->uiManager_.flush();
1213  } else {
1214  d->uiManager_.update(icEvent.component(),
1215  icEvent.inputContext());
1216  d->uiUpdateEvent_->setOneShot();
1217  }
1218  }));
1219  d->eventWatchers_.emplace_back(d->watchEvent(
1220  EventType::InputContextDestroyed, EventWatcherPhase::ReservedFirst,
1221  [d](Event &event) {
1222  auto &icEvent = static_cast<InputContextEvent &>(event);
1223  d->uiManager_.expire(icEvent.inputContext());
1224  }));
1225  d->eventWatchers_.emplace_back(d->watchEvent(
1226  EventType::InputMethodModeChanged, EventWatcherPhase::ReservedFirst,
1227  [d](Event &) { d->uiManager_.updateAvailability(); }));
1228  d->uiUpdateEvent_ = d->eventLoop_.addDeferEvent([d](EventSource *) {
1229  d->uiManager_.flush();
1230  return true;
1231  });
1232  d->uiUpdateEvent_->setEnabled(false);
1233  d->periodicalSave_ = d->eventLoop_.addTimeEvent(
1234  CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, AutoSaveIdleTime,
1235  [this, d](EventSourceTime *time, uint64_t) {
1236  if (exiting()) {
1237  return true;
1238  }
1239 
1240  // Check if the idle time is long enough.
1241  auto currentTime = now(CLOCK_MONOTONIC);
1242  if (currentTime <= d->idleStartTimestamp_ ||
1243  currentTime - d->idleStartTimestamp_ < AutoSaveIdleTime) {
1244  // IF not idle, shorten the next checking period.
1245  time->setNextInterval(2 * AutoSaveIdleTime);
1246  time->setOneShot();
1247  return true;
1248  }
1249 
1250  FCITX_INFO() << "Running autosave...";
1251  save();
1252  FCITX_INFO() << "End autosave";
1253  if (d->globalConfig_.autoSavePeriod() > 0) {
1254  time->setNextInterval(d->globalConfig_.autoSavePeriod() *
1255  AutoSaveMinInUsecs);
1256  time->setOneShot();
1257  }
1258  return true;
1259  });
1260  d->periodicalSave_->setEnabled(false);
1261 }
1262 
1263 Instance::~Instance() {
1264  FCITX_D();
1265  d->icManager_.finalize();
1266  d->addonManager_.unload();
1267  d->notifications_ = nullptr;
1268  d->icManager_.setInstance(nullptr);
1269 }
1270 
1271 void InstanceArgument::parseOption(int argc, char **argv) {
1272  if (argc >= 1) {
1273  argv0 = argv[0];
1274  } else {
1275  argv0 = "fcitx5";
1276  }
1277  struct option longOptions[] = {{"enable", required_argument, nullptr, 0},
1278  {"disable", required_argument, nullptr, 0},
1279  {"verbose", required_argument, nullptr, 0},
1280  {"keep", no_argument, nullptr, 'k'},
1281  {"ui", required_argument, nullptr, 'u'},
1282  {"replace", no_argument, nullptr, 'r'},
1283  {"version", no_argument, nullptr, 'v'},
1284  {"help", no_argument, nullptr, 'h'},
1285  {"option", required_argument, nullptr, 'o'},
1286  {nullptr, 0, 0, 0}};
1287 
1288  int optionIndex = 0;
1289  int c;
1290  std::string addonOptionString;
1291  while ((c = getopt_long(argc, argv, "ru:dDs:hvo:k", longOptions,
1292  &optionIndex)) != EOF) {
1293  switch (c) {
1294  case 0: {
1295  switch (optionIndex) {
1296  case 0:
1297  enableList = stringutils::split(optarg, ",");
1298  break;
1299  case 1:
1300  disableList = stringutils::split(optarg, ",");
1301  break;
1302  case 2:
1303  Log::setLogRule(optarg);
1304  break;
1305  default:
1306  quietQuit = true;
1307  printUsage();
1308  break;
1309  }
1310  } break;
1311  case 'r':
1312  tryReplace = true;
1313  break;
1314  case 'u':
1315  uiName = optarg;
1316  break;
1317  case 'd':
1318  runAsDaemon = true;
1319  break;
1320  case 'D':
1321  runAsDaemon = false;
1322  break;
1323  case 'k':
1324  exitWhenMainDisplayDisconnected = false;
1325  break;
1326  case 's':
1327  overrideDelay = std::atoi(optarg);
1328  break;
1329  case 'h':
1330  quietQuit = true;
1331  printUsage();
1332  break;
1333  case 'v':
1334  quietQuit = true;
1335  printVersion();
1336  break;
1337  case 'o':
1338  addonOptionString = optarg;
1339  break;
1340  default:
1341  quietQuit = true;
1342  printUsage();
1343  }
1344  if (quietQuit) {
1345  break;
1346  }
1347  }
1348 
1349  std::unordered_map<std::string, std::vector<std::string>> addonOptions;
1350  for (const std::string_view item :
1351  stringutils::split(addonOptionString, ",")) {
1352  auto tokens = stringutils::split(item, "=");
1353  if (tokens.size() != 2) {
1354  continue;
1355  }
1356  addonOptions[tokens[0]] = stringutils::split(tokens[1], ":");
1357  }
1358  addonOptions_ = std::move(addonOptions);
1359 }
1360 
1362 #ifdef _WIN32
1363  FCITX_UNUSED(fd);
1364 #else
1365  FCITX_D();
1366  d->signalPipe_ = fd;
1367  d->signalPipeEvent_ = d->eventLoop_.addIOEvent(
1368  fd, IOEventFlag::In, [this](EventSource *, int, IOEventFlags) {
1369  handleSignal();
1370  return true;
1371  });
1372 #endif
1373 }
1374 
1376  FCITX_D();
1377  return d->arg_.tryReplace;
1378 }
1379 
1381  FCITX_D();
1382  return d->arg_.exitWhenMainDisplayDisconnected;
1383 }
1384 
1385 bool Instance::exiting() const {
1386  FCITX_D();
1387  return d->exit_;
1388 }
1389 
1390 void Instance::handleSignal() {
1391 #ifndef _WIN32
1392  FCITX_D();
1393  uint8_t signo = 0;
1394  while (fs::safeRead(d->signalPipe_, &signo, sizeof(signo)) > 0) {
1395  if (signo == SIGINT || signo == SIGTERM || signo == SIGQUIT ||
1396  signo == SIGXCPU) {
1397  exit();
1398  } else if (signo == SIGUSR1) {
1399  reloadConfig();
1400  } else if (signo == SIGCHLD) {
1401  d->zombieReaper_->setNextInterval(2000000);
1402  d->zombieReaper_->setOneShot();
1403  }
1404  }
1405 #endif
1406 }
1407 
1409  FCITX_D();
1410  if (!d->arg_.uiName.empty()) {
1411  d->arg_.enableList.push_back(d->arg_.uiName);
1412  }
1413  reloadConfig();
1414  d->icManager_.registerProperty("inputState", &d->inputStateFactory_);
1415  std::unordered_set<std::string> enabled;
1416  std::unordered_set<std::string> disabled;
1417  std::tie(enabled, disabled) = d->overrideAddons();
1418  FCITX_INFO() << "Override Enabled Addons: " << enabled;
1419  FCITX_INFO() << "Override Disabled Addons: " << disabled;
1420  d->addonManager_.load(enabled, disabled);
1421  if (d->exit_) {
1422  return;
1423  }
1424  d->imManager_.load([d](InputMethodManager &) { d->buildDefaultGroup(); });
1425  d->uiManager_.load(d->arg_.uiName);
1426 
1427  const auto *entry = d->imManager_.entry("keyboard-us");
1428  FCITX_LOG_IF(Error, !entry) << "Couldn't find keyboard-us";
1429  d->preloadInputMethodEvent_ = d->eventLoop_.addTimeEvent(
1430  CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
1431  [this](EventSourceTime *, uint64_t) {
1432  FCITX_D();
1433  if (d->exit_ || !d->globalConfig_.preloadInputMethod()) {
1434  return false;
1435  }
1436  // Preload first input method.
1437  if (!d->imManager_.currentGroup().inputMethodList().empty()) {
1438  if (const auto *entry =
1439  d->imManager_.entry(d->imManager_.currentGroup()
1440  .inputMethodList()[0]
1441  .name())) {
1442  d->addonManager_.addon(entry->addon(), true);
1443  }
1444  }
1445  // Preload default input method.
1446  if (!d->imManager_.currentGroup().defaultInputMethod().empty()) {
1447  if (const auto *entry = d->imManager_.entry(
1448  d->imManager_.currentGroup().defaultInputMethod())) {
1449  d->addonManager_.addon(entry->addon(), true);
1450  }
1451  }
1452  return false;
1453  });
1454 #ifndef _WIN32
1455  d->zombieReaper_ = d->eventLoop_.addTimeEvent(
1456  CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), 0,
1457  [](EventSourceTime *, uint64_t) {
1458  pid_t res;
1459  while ((res = waitpid(-1, nullptr, WNOHANG)) > 0) {
1460  }
1461  return false;
1462  });
1463  d->zombieReaper_->setEnabled(false);
1464 #endif
1465 
1466  d->exitEvent_ = d->eventLoop_.addExitEvent([this](EventSource *) {
1467  FCITX_DEBUG() << "Running save...";
1468  save();
1469  return false;
1470  });
1471  d->notifications_ = d->addonManager_.addon("notifications", true);
1472 }
1473 
1475  FCITX_D();
1476  if (d->arg_.quietQuit) {
1477  return 0;
1478  }
1479  d->exit_ = false;
1480  d->exitCode_ = 0;
1481  initialize();
1482  if (d->exit_) {
1483  return d->exitCode_;
1484  }
1485  d->running_ = true;
1486  auto r = eventLoop().exec();
1487  d->running_ = false;
1488 
1489  return r ? d->exitCode_ : 1;
1490 }
1491 
1492 void Instance::setRunning(bool running) {
1493  FCITX_D();
1494  d->running_ = running;
1495 }
1496 
1497 bool Instance::isRunning() const {
1498  FCITX_D();
1499  return d->running_;
1500 }
1501 
1502 InputMethodMode Instance::inputMethodMode() const {
1503  FCITX_D();
1504  return d->inputMethodMode_;
1505 }
1506 
1507 void Instance::setInputMethodMode(InputMethodMode mode) {
1508  FCITX_D();
1509  if (d->inputMethodMode_ == mode) {
1510  return;
1511  }
1512  d->inputMethodMode_ = mode;
1513  postEvent(InputMethodModeChangedEvent());
1514 }
1515 
1517  FCITX_D();
1518  return d->restart_;
1519 }
1520 
1521 bool Instance::virtualKeyboardAutoShow() const {
1522  FCITX_D();
1523  return d->virtualKeyboardAutoShow_;
1524 }
1525 
1526 void Instance::setVirtualKeyboardAutoShow(bool autoShow) {
1527  FCITX_D();
1528  d->virtualKeyboardAutoShow_ = autoShow;
1529 }
1530 
1531 bool Instance::virtualKeyboardAutoHide() const {
1532  FCITX_D();
1533  return d->virtualKeyboardAutoHide_;
1534 }
1535 
1536 void Instance::setVirtualKeyboardAutoHide(bool autoHide) {
1537  FCITX_D();
1538  d->virtualKeyboardAutoHide_ = autoHide;
1539 }
1540 
1541 VirtualKeyboardFunctionMode Instance::virtualKeyboardFunctionMode() const {
1542  FCITX_D();
1543  return d->virtualKeyboardFunctionMode_;
1544 }
1545 
1546 void Instance::setVirtualKeyboardFunctionMode(
1547  VirtualKeyboardFunctionMode mode) {
1548  FCITX_D();
1549  d->virtualKeyboardFunctionMode_ = mode;
1550 }
1551 
1553  FCITX_D();
1554  d->binaryMode_ = true;
1555 }
1556 
1557 bool Instance::canRestart() const {
1558  FCITX_D();
1559  const auto &addonNames = d->addonManager_.loadedAddonNames();
1560  return d->binaryMode_ &&
1561  std::all_of(addonNames.begin(), addonNames.end(),
1562  [d](const std::string &name) {
1563  auto *addon = d->addonManager_.lookupAddon(name);
1564  if (!addon) {
1565  return true;
1566  }
1567  return addon->canRestart();
1568  });
1569 }
1570 
1571 InstancePrivate *Instance::privateData() {
1572  FCITX_D();
1573  return d;
1574 }
1575 
1577  FCITX_D();
1578  return d->eventLoop_;
1579 }
1580 
1582  FCITX_D();
1583  return d->eventDispatcher_;
1584 }
1585 
1587  FCITX_D();
1588  return d->icManager_;
1589 }
1590 
1592  FCITX_D();
1593  return d->addonManager_;
1594 }
1595 
1597  FCITX_D();
1598  return d->imManager_;
1599 }
1600 
1602  FCITX_D();
1603  return d->imManager_;
1604 }
1605 
1607  FCITX_D();
1608  return d->uiManager_;
1609 }
1610 
1612  FCITX_D();
1613  return d->globalConfig_;
1614 }
1615 
1616 bool Instance::postEvent(Event &event) {
1617  return std::as_const(*this).postEvent(event);
1618 }
1619 
1620 bool Instance::postEvent(Event &event) const {
1621  FCITX_D();
1622  if (d->exit_) {
1623  return false;
1624  }
1625  auto iter = d->eventHandlers_.find(event.type());
1626  if (iter != d->eventHandlers_.end()) {
1627  const auto &handlers = iter->second;
1628  EventWatcherPhase phaseOrder[] = {
1629  EventWatcherPhase::ReservedFirst, EventWatcherPhase::PreInputMethod,
1630  EventWatcherPhase::InputMethod, EventWatcherPhase::PostInputMethod,
1631  EventWatcherPhase::ReservedLast};
1632 
1633  for (auto phase : phaseOrder) {
1634  if (auto iter2 = handlers.find(phase); iter2 != handlers.end()) {
1635  for (auto &handler : iter2->second.view()) {
1636  handler(event);
1637  if (event.filtered()) {
1638  break;
1639  }
1640  }
1641  }
1642  if (event.filtered()) {
1643  break;
1644  }
1645  }
1646 
1647  // Make sure this part of fix is always executed regardless of the
1648  // filter.
1649  if (event.type() == EventType::InputContextKeyEvent) {
1650  auto &keyEvent = static_cast<KeyEvent &>(event);
1651  auto *ic = keyEvent.inputContext();
1652 #ifdef ENABLE_KEYBOARD
1653  do {
1654  if (!keyEvent.forward() && !keyEvent.origKey().code()) {
1655  break;
1656  }
1657  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1658  auto *xkbState = inputState->customXkbState();
1659  if (!xkbState) {
1660  break;
1661  }
1662  // This need to be called after xkb_state_key_get_*, and should
1663  // be called against all Key regardless whether they are
1664  // filtered or not.
1665  xkb_state_update_key(xkbState, keyEvent.origKey().code(),
1666  keyEvent.isRelease() ? XKB_KEY_UP
1667  : XKB_KEY_DOWN);
1668  } while (0);
1669 #endif
1670  if (ic->capabilityFlags().test(CapabilityFlag::KeyEventOrderFix) &&
1671  !keyEvent.accepted() && ic->hasPendingEventsStrictOrder()) {
1672  // Re-forward the event to ensure we got delivered later than
1673  // commit.
1674  keyEvent.filterAndAccept();
1675  ic->forwardKey(keyEvent.origKey(), keyEvent.isRelease(),
1676  keyEvent.time());
1677  }
1678  d_ptr->uiManager_.flush();
1679  }
1680  }
1681  return event.accepted();
1682 }
1683 
1684 std::unique_ptr<HandlerTableEntry<EventHandler>>
1685 Instance::watchEvent(EventType type, EventWatcherPhase phase,
1686  EventHandler callback) {
1687  FCITX_D();
1688  if (phase == EventWatcherPhase::ReservedFirst ||
1689  phase == EventWatcherPhase::ReservedLast) {
1690  throw std::invalid_argument("Reserved Phase is only for internal use");
1691  }
1692  return d->watchEvent(type, phase, std::move(callback));
1693 }
1694 
1695 bool groupContains(const InputMethodGroup &group, const std::string &name) {
1696  const auto &list = group.inputMethodList();
1697  auto iter = std::find_if(list.begin(), list.end(),
1698  [&name](const InputMethodGroupItem &item) {
1699  return item.name() == name;
1700  });
1701  return iter != list.end();
1702 }
1703 
1705  FCITX_D();
1706  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1707  // Small hack to make sure when InputMethodEngine::deactivate is called,
1708  // current im is the right one.
1709  if (!inputState->overrideDeactivateIM_.empty()) {
1710  return inputState->overrideDeactivateIM_;
1711  }
1712 
1713  const auto &group = d->imManager_.currentGroup();
1714  if (ic->capabilityFlags().test(CapabilityFlag::Disable) ||
1715  (ic->capabilityFlags().test(CapabilityFlag::Password) &&
1716  !d->globalConfig_.allowInputMethodForPassword())) {
1717  auto defaultLayoutIM =
1718  stringutils::concat("keyboard-", group.defaultLayout());
1719  const auto *entry = d->imManager_.entry(defaultLayoutIM);
1720  if (!entry) {
1721  entry = d->imManager_.entry("keyboard-us");
1722  }
1723  return entry ? entry->uniqueName() : "";
1724  }
1725 
1726  if (group.inputMethodList().empty()) {
1727  return "";
1728  }
1729  if (inputState->isActive()) {
1730  if (!inputState->localIM_.empty() &&
1731  groupContains(group, inputState->localIM_)) {
1732  return inputState->localIM_;
1733  }
1734  return group.defaultInputMethod();
1735  }
1736 
1737  return group.inputMethodList()[0].name();
1738 }
1739 
1741  FCITX_D();
1742  auto imName = inputMethod(ic);
1743  if (imName.empty()) {
1744  return nullptr;
1745  }
1746  return d->imManager_.entry(imName);
1747 }
1748 
1750  FCITX_D();
1751  const auto *entry = inputMethodEntry(ic);
1752  if (!entry) {
1753  return nullptr;
1754  }
1755  return static_cast<InputMethodEngine *>(
1756  d->addonManager_.addon(entry->addon(), true));
1757 }
1758 
1760  FCITX_D();
1761  const auto *entry = d->imManager_.entry(name);
1762  if (!entry) {
1763  return nullptr;
1764  }
1765  return static_cast<InputMethodEngine *>(
1766  d->addonManager_.addon(entry->addon(), true));
1767 }
1768 
1770  std::string icon;
1771  const auto *entry = inputMethodEntry(ic);
1772  if (entry) {
1773  auto *engine = inputMethodEngine(ic);
1774  if (engine) {
1775  icon = engine->subModeIcon(*entry, *ic);
1776  }
1777  if (icon.empty()) {
1778  icon = entry->icon();
1779  }
1780  } else {
1781  icon = "input-keyboard";
1782  }
1783  return icon;
1784 }
1785 
1787  std::string label;
1788 
1789  const auto *entry = inputMethodEntry(ic);
1790  auto *engine = inputMethodEngine(ic);
1791 
1792  if (engine && entry) {
1793  label = engine->subModeLabel(*entry, *ic);
1794  }
1795  if (label.empty() && entry) {
1796  label = entry->label();
1797  }
1798  return label;
1799 }
1800 
1801 uint32_t Instance::processCompose(InputContext *ic, KeySym keysym) {
1802 #ifdef ENABLE_KEYBOARD
1803  FCITX_D();
1804  auto *state = ic->propertyFor(&d->inputStateFactory_);
1805 
1806  auto *xkbComposeState = state->xkbComposeState();
1807  if (!xkbComposeState) {
1808  return 0;
1809  }
1810 
1811  auto keyval = static_cast<xkb_keysym_t>(keysym);
1812 
1813  enum xkb_compose_feed_result result =
1814  xkb_compose_state_feed(xkbComposeState, keyval);
1815  if (result == XKB_COMPOSE_FEED_IGNORED) {
1816  return 0;
1817  }
1818 
1819  enum xkb_compose_status status =
1820  xkb_compose_state_get_status(xkbComposeState);
1821  if (status == XKB_COMPOSE_NOTHING) {
1822  return 0;
1823  }
1824  if (status == XKB_COMPOSE_COMPOSED) {
1825  char buffer[FCITX_UTF8_MAX_LENGTH + 1] = {'\0', '\0', '\0', '\0',
1826  '\0', '\0', '\0'};
1827  int length =
1828  xkb_compose_state_get_utf8(xkbComposeState, buffer, sizeof(buffer));
1829  xkb_compose_state_reset(xkbComposeState);
1830  if (length == 0) {
1831  return FCITX_INVALID_COMPOSE_RESULT;
1832  }
1833 
1834  uint32_t c = utf8::getChar(buffer);
1835  return utf8::isValidChar(c) ? c : 0;
1836  }
1837  if (status == XKB_COMPOSE_CANCELLED) {
1838  xkb_compose_state_reset(xkbComposeState);
1839  }
1840 
1841  return FCITX_INVALID_COMPOSE_RESULT;
1842 #else
1843  FCITX_UNUSED(ic);
1844  FCITX_UNUSED(keysym);
1845  return 0;
1846 #endif
1847 }
1848 
1849 std::optional<std::string> Instance::processComposeString(InputContext *ic,
1850  KeySym keysym) {
1851 #ifdef ENABLE_KEYBOARD
1852  FCITX_D();
1853  auto *state = ic->propertyFor(&d->inputStateFactory_);
1854 
1855  auto *xkbComposeState = state->xkbComposeState();
1856  if (!xkbComposeState) {
1857  return std::string();
1858  }
1859 
1860  auto keyval = static_cast<xkb_keysym_t>(keysym);
1861  enum xkb_compose_feed_result result =
1862  xkb_compose_state_feed(xkbComposeState, keyval);
1863 
1864  if (result == XKB_COMPOSE_FEED_IGNORED) {
1865  return std::string();
1866  }
1867 
1868  enum xkb_compose_status status =
1869  xkb_compose_state_get_status(xkbComposeState);
1870  if (status == XKB_COMPOSE_NOTHING) {
1871  return std::string();
1872  }
1873  if (status == XKB_COMPOSE_COMPOSED) {
1874  // This may not be NUL-terminiated.
1875  std::array<char, 256> buffer;
1876  auto length = xkb_compose_state_get_utf8(xkbComposeState, buffer.data(),
1877  buffer.size());
1878  xkb_compose_state_reset(xkbComposeState);
1879  if (length == 0) {
1880  return std::nullopt;
1881  }
1882 
1883  auto bufferBegin = buffer.begin();
1884  auto bufferEnd = std::next(bufferBegin, length);
1885  if (utf8::validate(bufferBegin, bufferEnd)) {
1886  return std::string(bufferBegin, bufferEnd);
1887  }
1888  return std::nullopt;
1889  }
1890  if (status == XKB_COMPOSE_CANCELLED) {
1891  xkb_compose_state_reset(xkbComposeState);
1892  }
1893  return std::nullopt;
1894 #else
1895  FCITX_UNUSED(ic);
1896  FCITX_UNUSED(keysym);
1897  return std::string();
1898 #endif
1899 }
1900 
1902 #ifdef ENABLE_KEYBOARD
1903  FCITX_D();
1904  auto *state = inputContext->propertyFor(&d->inputStateFactory_);
1905 
1906  auto *xkbComposeState = state->xkbComposeState();
1907  if (!xkbComposeState) {
1908  return false;
1909  }
1910 
1911  return xkb_compose_state_get_status(xkbComposeState) ==
1912  XKB_COMPOSE_COMPOSING;
1913 #else
1914  FCITX_UNUSED(inputContext);
1915  return false;
1916 #endif
1917 }
1918 
1920 #ifdef ENABLE_KEYBOARD
1921  FCITX_D();
1922  auto *state = inputContext->propertyFor(&d->inputStateFactory_);
1923  auto *xkbComposeState = state->xkbComposeState();
1924  if (!xkbComposeState) {
1925  return;
1926  }
1927  xkb_compose_state_reset(xkbComposeState);
1928 #else
1929  FCITX_UNUSED(inputContext);
1930 #endif
1931 }
1932 
1934  FCITX_D();
1935  // Refresh timestamp for next auto save.
1936  d->idleStartTimestamp_ = now(CLOCK_MONOTONIC);
1937  d->imManager_.save();
1938  d->addonManager_.saveAll();
1939 }
1940 
1942  FCITX_D();
1943  if (auto *ic = mostRecentInputContext()) {
1944  CheckInputMethodChanged imChangedRAII(ic, d);
1945  activate(ic);
1946  }
1947 }
1948 
1949 std::string Instance::addonForInputMethod(const std::string &imName) {
1950 
1951  if (const auto *entry = inputMethodManager().entry(imName)) {
1952  return entry->uniqueName();
1953  }
1954  return {};
1955 }
1956 
1958  startProcess(
1959  {StandardPaths::fcitxPath("bindir", "fcitx5-configtool").string()});
1960 }
1961 
1962 void Instance::configureAddon(const std::string & /*unused*/) {}
1963 
1964 void Instance::configureInputMethod(const std::string & /*unused*/) {}
1965 
1967  if (auto *ic = mostRecentInputContext()) {
1968  if (const auto *entry = inputMethodEntry(ic)) {
1969  return entry->uniqueName();
1970  }
1971  }
1972  return {};
1973 }
1974 
1975 std::string Instance::currentUI() {
1976  FCITX_D();
1977  return d->uiManager_.currentUI();
1978 }
1979 
1981  FCITX_D();
1982  if (auto *ic = mostRecentInputContext()) {
1983  CheckInputMethodChanged imChangedRAII(ic, d);
1984  deactivate(ic);
1985  }
1986 }
1987 
1988 void Instance::exit() { exit(0); }
1989 
1990 void Instance::exit(int exitCode) {
1991  FCITX_D();
1992  d->exit_ = true;
1993  d->exitCode_ = exitCode;
1994  if (d->running_) {
1995  d->eventLoop_.exit();
1996  }
1997 }
1998 
1999 void Instance::reloadAddonConfig(const std::string &addonName) {
2000  auto *addon = addonManager().addon(addonName);
2001  if (addon) {
2002  addon->reloadConfig();
2003  }
2004 }
2005 
2007  FCITX_D();
2008  auto [enabled, disabled] = d->overrideAddons();
2009  d->addonManager_.load(enabled, disabled);
2010  d->imManager_.refresh();
2011 }
2012 
2014  FCITX_D();
2015  readAsIni(d->globalConfig_.config(), StandardPathsType::PkgConfig,
2016  "config");
2017  FCITX_DEBUG() << "Trigger Key: "
2018  << Key::keyListToString(d->globalConfig_.triggerKeys());
2019  d->icManager_.setPropertyPropagatePolicy(
2020  d->globalConfig_.shareInputState());
2021  if (d->globalConfig_.preeditEnabledByDefault() !=
2022  d->icManager_.isPreeditEnabledByDefault()) {
2023  d->icManager_.setPreeditEnabledByDefault(
2024  d->globalConfig_.preeditEnabledByDefault());
2025  d->icManager_.foreach([d](InputContext *ic) {
2026  ic->setEnablePreedit(d->globalConfig_.preeditEnabledByDefault());
2027  return true;
2028  });
2029  }
2030 #ifdef ENABLE_KEYBOARD
2031  d->keymapCache_.clear();
2032  if (d->inputStateFactory_.registered()) {
2033  d->icManager_.foreach([d](InputContext *ic) {
2034  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2035  inputState->resetXkbState();
2036  return true;
2037  });
2038  }
2039 #endif
2040  if (d->running_) {
2041  postEvent(GlobalConfigReloadedEvent());
2042  }
2043 
2044  if (d->globalConfig_.autoSavePeriod() <= 0) {
2045  d->periodicalSave_->setEnabled(false);
2046  } else {
2047  d->periodicalSave_->setNextInterval(AutoSaveMinInUsecs *
2048  d->globalConfig_.autoSavePeriod());
2049  d->periodicalSave_->setOneShot();
2050  }
2051 }
2052 
2054  FCITX_D();
2055  d->imManager_.reset([d](InputMethodManager &) { d->buildDefaultGroup(); });
2056 }
2057 
2059  FCITX_D();
2060  if (!canRestart()) {
2061  return;
2062  }
2063  d->restart_ = true;
2064  exit();
2065 }
2066 
2067 void Instance::setCurrentInputMethod(const std::string &name) {
2068  setCurrentInputMethod(mostRecentInputContext(), name, false);
2069 }
2070 
2071 void Instance::setCurrentInputMethod(InputContext *ic, const std::string &name,
2072  bool local) {
2073  FCITX_D();
2074  if (!canTrigger()) {
2075  return;
2076  }
2077 
2078  auto &imManager = inputMethodManager();
2079  const auto &imList = imManager.currentGroup().inputMethodList();
2080  auto iter = std::find_if(imList.begin(), imList.end(),
2081  [&name](const InputMethodGroupItem &item) {
2082  return item.name() == name;
2083  });
2084  if (iter == imList.end()) {
2085  return;
2086  }
2087 
2088  auto setGlobalDefaultInputMethod = [d](const std::string &name) {
2089  std::vector<std::unique_ptr<CheckInputMethodChanged>> groupRAIICheck;
2090  d->icManager_.foreachFocused([d, &groupRAIICheck](InputContext *ic) {
2091  assert(ic->hasFocus());
2092  groupRAIICheck.push_back(
2093  std::make_unique<CheckInputMethodChanged>(ic, d));
2094  return true;
2095  });
2096  d->imManager_.setDefaultInputMethod(name);
2097  };
2098 
2099  auto idx = std::distance(imList.begin(), iter);
2100  if (ic) {
2101  CheckInputMethodChanged imChangedRAII(ic, d);
2102  auto currentIM = inputMethod(ic);
2103  if (currentIM == name) {
2104  return;
2105  }
2106  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2107 
2108  if (idx != 0) {
2109  if (local) {
2110  inputState->setLocalIM(name);
2111  } else {
2112  inputState->setLocalIM({});
2113 
2114  setGlobalDefaultInputMethod(name);
2115  }
2116  inputState->setActive(true);
2117  } else {
2118  inputState->setActive(false);
2119  }
2120  if (inputState->imChanged_) {
2121  inputState->imChanged_->setReason(InputMethodSwitchedReason::Other);
2122  }
2123  } else {
2124  // We can't set local input method if we don't have a IC, but we should
2125  // still to change the global default.
2126  if (local) {
2127  return;
2128  }
2129  if (idx != 0) {
2130  setGlobalDefaultInputMethod(name);
2131  }
2132  return;
2133  }
2134 }
2135 
2137  FCITX_D();
2138  if (auto *ic = mostRecentInputContext()) {
2139  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2140  return inputState->isActive() ? 2 : 1;
2141  }
2142  return 0;
2143 }
2144 
2146  FCITX_D();
2147  if (auto *ic = mostRecentInputContext()) {
2148  CheckInputMethodChanged imChangedRAII(ic, d);
2149  trigger(ic, true);
2150  }
2151 }
2152 
2153 void Instance::enumerate(bool forward) {
2154  FCITX_D();
2155  if (auto *ic = mostRecentInputContext()) {
2156  CheckInputMethodChanged imChangedRAII(ic, d);
2157  enumerate(ic, forward);
2158  }
2159 }
2160 
2161 bool Instance::canTrigger() const {
2162  const auto &imManager = inputMethodManager();
2163  return (imManager.currentGroup().inputMethodList().size() > 1);
2164 }
2165 
2166 bool Instance::canAltTrigger(InputContext *ic) const {
2167  if (!canTrigger()) {
2168  return false;
2169  }
2170  FCITX_D();
2171  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2172  if (inputState->isActive()) {
2173  return true;
2174  }
2175  return inputState->lastIMChangeIsAltTrigger_;
2176 }
2177 
2178 bool Instance::canEnumerate(InputContext *ic) const {
2179  FCITX_D();
2180  if (!canTrigger()) {
2181  return false;
2182  }
2183 
2184  if (d->globalConfig_.enumerateSkipFirst()) {
2185  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2186  if (!inputState->isActive()) {
2187  return false;
2188  }
2189  return d->imManager_.currentGroup().inputMethodList().size() > 2;
2190  }
2191 
2192  return true;
2193 }
2194 
2195 bool Instance::canChangeGroup() const {
2196  const auto &imManager = inputMethodManager();
2197  return (imManager.groupCount() > 1);
2198 }
2199 
2201  FCITX_D();
2202  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2203  if (!canTrigger()) {
2204  return false;
2205  }
2206  inputState->setActive(!inputState->isActive());
2207  if (inputState->imChanged_) {
2208  inputState->imChanged_->setReason(reason);
2209  }
2210  return true;
2211 }
2212 
2213 bool Instance::trigger(InputContext *ic, bool totallyReleased) {
2214  FCITX_D();
2215  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2216  if (!canTrigger()) {
2217  return false;
2218  }
2219  // Active -> inactive -> enumerate.
2220  // Inactive -> active -> inactive -> enumerate.
2221  if (totallyReleased) {
2222  toggle(ic);
2223  inputState->firstTrigger_ = true;
2224  } else {
2225  if (!d->globalConfig_.enumerateWithTriggerKeys() ||
2226  (inputState->firstTrigger_ && inputState->isActive()) ||
2227  (d->globalConfig_.enumerateSkipFirst() &&
2228  d->imManager_.currentGroup().inputMethodList().size() <= 2)) {
2229  toggle(ic);
2230  } else {
2231  enumerate(ic, true);
2232  }
2233  inputState->firstTrigger_ = false;
2234  }
2235  return true;
2236 }
2237 
2238 bool Instance::altTrigger(InputContext *ic) {
2239  if (!canAltTrigger(ic)) {
2240  return false;
2241  }
2242 
2244  return true;
2245 }
2246 
2247 bool Instance::activate(InputContext *ic) {
2248  FCITX_D();
2249  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2250  if (!canTrigger()) {
2251  return false;
2252  }
2253  if (inputState->isActive()) {
2254  return true;
2255  }
2256  inputState->setActive(true);
2257  if (inputState->imChanged_) {
2258  inputState->imChanged_->setReason(InputMethodSwitchedReason::Activate);
2259  }
2260  return true;
2261 }
2262 
2264  FCITX_D();
2265  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2266  if (!canTrigger()) {
2267  return false;
2268  }
2269  if (!inputState->isActive()) {
2270  return true;
2271  }
2272  inputState->setActive(false);
2273  if (inputState->imChanged_) {
2274  inputState->imChanged_->setReason(
2276  }
2277  return true;
2278 }
2279 
2280 bool Instance::enumerate(InputContext *ic, bool forward) {
2281  FCITX_D();
2282  auto &imManager = inputMethodManager();
2283  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2284  const auto &imList = imManager.currentGroup().inputMethodList();
2285  if (!canTrigger()) {
2286  return false;
2287  }
2288 
2289  if (d->globalConfig_.enumerateSkipFirst() && imList.size() <= 2) {
2290  return false;
2291  }
2292 
2293  auto currentIM = inputMethod(ic);
2294 
2295  auto iter = std::ranges::find_if(
2296  imList, [&currentIM](const InputMethodGroupItem &item) {
2297  return item.name() == currentIM;
2298  });
2299  if (iter == imList.end()) {
2300  return false;
2301  }
2302  int idx = std::distance(imList.begin(), iter);
2303  auto nextIdx = [forward, &imList](int idx) {
2304  // be careful not to use negative to avoid overflow.
2305  return (idx + (forward ? 1 : (imList.size() - 1))) % imList.size();
2306  };
2307 
2308  idx = nextIdx(idx);
2309  if (d->globalConfig_.enumerateSkipFirst() && idx == 0) {
2310  idx = nextIdx(idx);
2311  }
2312  if (idx != 0) {
2313  std::vector<std::unique_ptr<CheckInputMethodChanged>> groupRAIICheck;
2314  d->icManager_.foreachFocused([d, &groupRAIICheck](InputContext *ic) {
2315  assert(ic->hasFocus());
2316  groupRAIICheck.push_back(
2317  std::make_unique<CheckInputMethodChanged>(ic, d));
2318  return true;
2319  });
2320  imManager.setDefaultInputMethod(imList[idx].name());
2321  inputState->setActive(true);
2322  inputState->setLocalIM({});
2323  } else {
2324  inputState->setActive(false);
2325  }
2326  if (inputState->imChanged_) {
2327  inputState->imChanged_->setReason(InputMethodSwitchedReason::Enumerate);
2328  }
2329 
2330  return true;
2331 }
2332 
2333 std::string Instance::commitFilter(InputContext *inputContext,
2334  const std::string &orig) {
2335  std::string result = orig;
2336  emit<Instance::CommitFilter>(inputContext, result);
2337  return result;
2338 }
2339 
2340 Text Instance::outputFilter(InputContext *inputContext, const Text &orig) {
2341  Text result = orig;
2342  emit<Instance::OutputFilter>(inputContext, result);
2343  if ((&orig == &inputContext->inputPanel().clientPreedit() ||
2344  &orig == &inputContext->inputPanel().preedit()) &&
2345  !globalConfig().showPreeditForPassword() &&
2346  inputContext->capabilityFlags().test(CapabilityFlag::Password)) {
2347  Text newText;
2348  for (int i = 0, e = result.size(); i < e; i++) {
2349  auto length = utf8::length(result.stringAt(i));
2350  std::string dot;
2351  dot.reserve(length * 3);
2352  while (length != 0) {
2353  dot += "\xe2\x80\xa2";
2354  length -= 1;
2355  }
2356  newText.append(std::move(dot),
2357  result.formatAt(i) | TextFormatFlag::DontCommit);
2358  }
2359  result = std::move(newText);
2360  }
2361  return result;
2362 }
2363 
2365  FCITX_D();
2366  return d->icManager_.lastFocusedInputContext();
2367 }
2368 
2370  FCITX_D();
2371  return d->icManager_.mostRecentInputContext();
2372 }
2373 
2375  FCITX_D();
2376  d->uiManager_.flush();
2377 }
2378 
2379 int scoreForGroup(FocusGroup *group, const std::string &displayHint) {
2380  // Hardcode wayland over X11.
2381  if (displayHint.empty()) {
2382  if (group->display() == "x11:") {
2383  return 2;
2384  }
2385  if (stringutils::startsWith(group->display(), "x11:")) {
2386  return 1;
2387  }
2388  if (group->display() == "wayland:") {
2389  return 4;
2390  }
2391  if (stringutils::startsWith(group->display(), "wayland:")) {
2392  return 3;
2393  }
2394  } else {
2395  if (group->display() == displayHint) {
2396  return 2;
2397  }
2398  if (stringutils::startsWith(group->display(), displayHint)) {
2399  return 1;
2400  }
2401  }
2402  return -1;
2403 }
2404 
2405 FocusGroup *Instance::defaultFocusGroup(const std::string &displayHint) {
2406  FCITX_D();
2407  FocusGroup *defaultFocusGroup = nullptr;
2408 
2409  int score = 0;
2410  d->icManager_.foreachGroup(
2411  [&score, &displayHint, &defaultFocusGroup](FocusGroup *group) {
2412  auto newScore = scoreForGroup(group, displayHint);
2413  if (newScore > score) {
2414  defaultFocusGroup = group;
2415  score = newScore;
2416  }
2417 
2418  return true;
2419  });
2420  return defaultFocusGroup;
2421 }
2422 
2423 void Instance::activateInputMethod(InputContextEvent &event) {
2424  FCITX_D();
2425  FCITX_DEBUG() << "Instance::activateInputMethod";
2426  InputContext *ic = event.inputContext();
2427  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2428  const auto *entry = inputMethodEntry(ic);
2429  if (entry) {
2430  FCITX_DEBUG() << "Activate: "
2431  << "[Last]:" << inputState->lastIM_
2432  << " [Activating]:" << entry->uniqueName();
2433  assert(inputState->lastIM_.empty());
2434  inputState->lastIM_ = entry->uniqueName();
2435  }
2436  auto *engine = inputMethodEngine(ic);
2437  if (!engine || !entry) {
2438  return;
2439  }
2440 #ifdef ENABLE_KEYBOARD
2441  if (auto *xkbState = inputState->customXkbState(true)) {
2442  if (auto *mods = findValue(d->stateMask_, ic->display())) {
2443  FCITX_KEYTRACE() << "Update mask to customXkbState";
2444  auto depressed = std::get<0>(*mods);
2445  auto latched = std::get<1>(*mods);
2446  auto locked = std::get<2>(*mods);
2447 
2448  // set modifiers in depressed if they don't appear in any of the
2449  // final masks
2450  // depressed |= ~(depressed | latched | locked);
2451  FCITX_KEYTRACE() << depressed << " " << latched << " " << locked;
2452  if (depressed == 0) {
2453  inputState->setModsAllReleased();
2454  }
2455  xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0,
2456  0);
2457  }
2458  }
2459 #endif
2460  ic->statusArea().clearGroup(StatusGroup::InputMethod);
2461  engine->activate(*entry, event);
2462  postEvent(InputMethodActivatedEvent(entry->uniqueName(), ic));
2463 }
2464 
2465 void Instance::deactivateInputMethod(InputContextEvent &event) {
2466  FCITX_D();
2467  FCITX_DEBUG() << "Instance::deactivateInputMethod event_type="
2468  << static_cast<uint32_t>(event.type());
2469  InputContext *ic = event.inputContext();
2470  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2471  const InputMethodEntry *entry = nullptr;
2472  InputMethodEngine *engine = nullptr;
2473 
2475  auto &icEvent =
2476  static_cast<InputContextSwitchInputMethodEvent &>(event);
2477  FCITX_DEBUG() << "Switch reason: "
2478  << static_cast<int>(icEvent.reason());
2479  FCITX_DEBUG() << "Old Input method: " << icEvent.oldInputMethod();
2480  entry = d->imManager_.entry(icEvent.oldInputMethod());
2481  } else {
2482  entry = inputMethodEntry(ic);
2483  }
2484  if (entry) {
2485  FCITX_DEBUG() << "Deactivate: "
2486  << "[Last]:" << inputState->lastIM_
2487  << " [Deactivating]:" << entry->uniqueName();
2488  assert(entry->uniqueName() == inputState->lastIM_);
2489  engine = static_cast<InputMethodEngine *>(
2490  d->addonManager_.addon(entry->addon()));
2491  }
2492  inputState->lastIM_.clear();
2493  if (!engine || !entry) {
2494  return;
2495  }
2496  inputState->overrideDeactivateIM_ = entry->uniqueName();
2497  engine->deactivate(*entry, event);
2498  inputState->overrideDeactivateIM_.clear();
2499  postEvent(InputMethodDeactivatedEvent(entry->uniqueName(), ic));
2500 }
2501 
2502 bool Instance::enumerateGroup(bool forward) {
2503  auto &imManager = inputMethodManager();
2504  auto groups = imManager.groups();
2505  if (groups.size() <= 1) {
2506  return false;
2507  }
2508  if (forward) {
2509  imManager.setCurrentGroup(groups[1]);
2510  } else {
2511  imManager.setCurrentGroup(groups.back());
2512  }
2513  return true;
2514 }
2515 
2517  FCITX_DEBUG() << "Input method switched";
2518  FCITX_D();
2519  if (!d->globalConfig_.showInputMethodInformation()) {
2520  return;
2521  }
2522  d->showInputMethodInformation(ic);
2523 }
2524 
2526  const std::string &message) {
2527  FCITX_DEBUG() << "Input method switched";
2528  FCITX_D();
2529  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2530  inputState->showInputMethodInformation(message);
2531 }
2532 
2534  FCITX_D();
2535  return (isInFlatpak() &&
2536  std::filesystem::is_regular_file("/app/.updated")) ||
2537  d->addonManager_.checkUpdate() || d->imManager_.checkUpdate() ||
2538  postEvent(CheckUpdateEvent());
2539 }
2540 
2541 void Instance::setXkbParameters(const std::string &display,
2542  const std::string &rule,
2543  const std::string &model,
2544  const std::string &options) {
2545 #ifdef ENABLE_KEYBOARD
2546  FCITX_D();
2547  bool resetState = false;
2548  if (auto *param = findValue(d->xkbParams_, display)) {
2549  if (std::get<0>(*param) != rule || std::get<1>(*param) != model ||
2550  std::get<2>(*param) != options) {
2551  std::get<0>(*param) = rule;
2552  std::get<1>(*param) = model;
2553  std::get<2>(*param) = options;
2554  resetState = true;
2555  }
2556  } else {
2557  d->xkbParams_.emplace(display, std::make_tuple(rule, model, options));
2558  }
2559 
2560  if (resetState) {
2561  d->keymapCache_[display].clear();
2562  d->icManager_.foreach([d, &display](InputContext *ic) {
2563  if (ic->display() == display ||
2564  !d->xkbParams_.contains(ic->display())) {
2565  auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2566  inputState->resetXkbState();
2567  }
2568  return true;
2569  });
2570  }
2571 #else
2572  FCITX_UNUSED(display);
2573  FCITX_UNUSED(rule);
2574  FCITX_UNUSED(model);
2575  FCITX_UNUSED(options);
2576 #endif
2577 }
2578 
2579 void Instance::updateXkbStateMask(const std::string &display,
2580  uint32_t depressed_mods,
2581  uint32_t latched_mods, uint32_t locked_mods) {
2582  FCITX_D();
2583  d->stateMask_[display] =
2584  std::make_tuple(depressed_mods, latched_mods, locked_mods);
2585 }
2586 
2587 void Instance::clearXkbStateMask(const std::string &display) {
2588  FCITX_D();
2589  d->stateMask_.erase(display);
2590 }
2591 
2592 const char *Instance::version() { return FCITX_VERSION_STRING; }
2593 
2594 } // namespace fcitx
void updateXkbStateMask(const std::string &display, uint32_t depressed_mods, uint32_t latched_mods, uint32_t locked_mods)
Update xkb state mask for given display.
Definition: instance.cpp:2579
Describe a Key in fcitx.
Definition: key.h:41
void restart()
Restart fcitx instance, this should only be used within a regular Fcitx server, not within embedded m...
Definition: instance.cpp:2058
void reloadAddonConfig(const std::string &addonName)
Reload certain addon config.
Definition: instance.cpp:1999
CapabilityFlags capabilityFlags() const
Returns the current capability flags.
FCITXCORE_DEPRECATED uint32_t processCompose(InputContext *ic, KeySym keysym)
Handle current XCompose state.
Definition: instance.cpp:1801
std::string inputMethodIcon(InputContext *ic)
Return the input method icon for input context.
Definition: instance.cpp:1769
void setXkbParameters(const std::string &display, const std::string &rule, const std::string &model, const std::string &options)
Set xkb RLVMO tuple for given display.
Definition: instance.cpp:2541
void activate()
Activate last focused input context. (Switch to the active input method)
Definition: instance.cpp:1941
EventType
Type of input method events.
Definition: event.h:65
Switched by alternative trigger key.
ResetEvent is generated.
void resetCompose(InputContext *inputContext)
Reset the compose state.
Definition: instance.cpp:1919
T::PropertyType * propertyFor(const T *factory)
Helper function to return the input context property in specific type by given factory.
Definition: inputcontext.h:285
std::string UCS4ToUTF8(uint32_t code)
Convert UCS4 to UTF8 string.
Definition: utf8.cpp:19
FocusInEvent is generated when client gets focused.
Formatted string commonly used in user interface.
Whether client request input method to be disabled.
FocusGroup * defaultFocusGroup(const std::string &displayHint={})
Get the default focus group with given display hint.
Definition: instance.cpp:2405
static uint32_t keySymToUnicode(KeySym sym)
Convert keysym to a unicode.
Definition: key.cpp:740
void flushUI()
All user interface update is batched internally.
Definition: instance.cpp:2374
This is generated when input method group changed.
std::string commitFilter(InputContext *inputContext, const std::string &orig)
Update the commit string to frontend.
Definition: instance.cpp:2333
AddonManager & addonManager()
Get the addon manager.
Definition: instance.cpp:1591
std::string currentInputMethod()
Return the current input method of last focused input context.
Definition: instance.cpp:1966
Simple file system related API for checking file status.
size_t length(Iter start, Iter end)
Return the number UTF-8 characters in the string iterator range.
Definition: utf8.h:33
bool validate(Iter start, Iter end)
Check if the string iterator range is valid utf8 string.
Definition: utf8.h:74
when user switch to a different input method by hand such as ctrl+shift by default, or by ui, default behavior is reset IM.
InputMethodSwitchedReason
The reason why input method is switched to another.
Definition: event.h:41
void deactivate()
Deactivate last focused input context.
Definition: instance.cpp:1980
int state()
Return a fcitx5-remote compatible value for the state.
Definition: instance.cpp:2136
Definition: action.cpp:17
InputMethodEngine * inputMethodEngine(InputContext *ic)
Return the input method engine object for given input context.
Definition: instance.cpp:1749
void setSignalPipe(int fd)
Set the pipe forwarding unix signal information.
Definition: instance.cpp:1361
InputMethodMode inputMethodMode() const
The current global input method mode.
Definition: instance.cpp:1502
void initialize()
Initialize fcitx.
Definition: instance.cpp:1408
EventLoop & eventLoop()
Get the fcitx event loop.
Definition: instance.cpp:1576
std::string currentUI()
Return the name of current user interface addon.
Definition: instance.cpp:1975
bool exitWhenMainDisplayDisconnected() const
Check whether command line specify whether to keep fcitx running.
Definition: instance.cpp:1380
C++ Utility functions for handling utf8 strings.
InputContext * mostRecentInputContext()
Return the most recent focused input context.
Definition: instance.cpp:2369
FCITX_NODISCARD std::unique_ptr< HandlerTableEntry< EventHandler > > watchEvent(EventType type, EventWatcherPhase phase, EventHandler callback)
Add a callback to for certain event type.
Definition: instance.cpp:1685
InputContextManager & inputContextManager()
Get the input context manager.
Definition: instance.cpp:1586
void setEnablePreedit(bool enable)
Override the preedit hint from client.
std::vector< std::string > split(std::string_view str, std::string_view delim, SplitBehavior behavior)
Split the string by delim.
virtual bool filtered() const
Whether a event is filtered by handler.
Definition: event.h:241
void clearGroup(StatusGroup group)
Clear only given status group.
Definition: statusarea.cpp:80
bool hasFocus() const
Returns whether the input context holds the input focus.
bool isRunning() const
Whether event loop is started and still running.
Definition: instance.cpp:1497
A class represents a formatted string.
Definition: text.h:27
bool willTryReplace() const
Check whether command line specify if it will replace an existing fcitx server.
Definition: instance.cpp:1375
Class to manage all the input method relation information.
bool isRestartRequested() const
Whether restart is requested.
Definition: instance.cpp:1516
Manager class for user interface.
Instance(int argc, char *argv[])
A main function like construct to be used to create Fcitx Instance.
Definition: instance.cpp:651
Base class for fcitx event.
Definition: event.h:212
virtual void deactivate(const InputMethodEntry &entry, InputContextEvent &event)
Called when input context switch its input method.
bool isValidChar(uint32_t c)
Check the chr value is not two invalid value above.
Definition: utf8.h:97
std::string inputMethodLabel(InputContext *ic)
Return the input method label for input context.
Definition: instance.cpp:1786
void save()
Save everything including input method profile and addon data.
Definition: instance.cpp:1933
Enum type for input context capability.
Class for status area in UI.
void resetInputMethodList()
Reset the input method configuration and recreate based on system language.
Definition: instance.cpp:2053
Switched by capability change (e.g. password field)
Base class for User Interface addon.
A thread safe class to post event to a certain EventLoop.
void clearXkbStateMask(const std::string &display)
Clear xkb state mask for given display.
Definition: instance.cpp:2587
Enum flag for text formatting.
void configure()
Launch configtool.
Definition: instance.cpp:1957
bool checkUpdate() const
Check if need to invoke Instance::refresh.
Definition: instance.cpp:2533
New Utility classes to handle application specific path.
uint32_t getChar(Iter iter, Iter end)
Get next UCS4 char from iter, do not cross end.
Definition: utf8.h:104
InvokeAction event is generated when client click on the preedit.
InputMethodManager & inputMethodManager()
Get the input method manager.
Definition: instance.cpp:1596
void reloadConfig()
Reload global config.
Definition: instance.cpp:2013
int exec()
Start the event loop of Fcitx.
Definition: instance.cpp:1474
C-style utf8 utility functions.
void setBinaryMode()
Set if this instance is running as fcitx5 binary.
Definition: instance.cpp:1552
bool isModifier() const
Check if the key is a modifier press.
Definition: key.cpp:462
InputContext * lastFocusedInputContext()
Return a focused input context.
Definition: instance.cpp:2364
std::string display() const
Returns the display server of the client.
void toggle()
Toggle between the first input method and active input method.
Definition: instance.cpp:2145
Notify the input method mode is changed.
Definition: event.h:646
void setCurrentInputMethod(const std::string &imName)
Set the input method of last focused input context.
Definition: instance.cpp:2067
void setRunning(bool running)
Let other know that event loop is already running.
Definition: instance.cpp:1492
bool canRestart() const
Check if fcitx 5 can safely restart by itself.
Definition: instance.cpp:1557
void showCustomInputMethodInformation(InputContext *ic, const std::string &message)
Show a small popup with input popup window with current input method information. ...
Definition: instance.cpp:2525
std::optional< std::string > processComposeString(InputContext *ic, KeySym keysym)
Handle current XCompose state.
Definition: instance.cpp:1849
void refresh()
Load newly installed input methods and addons.
Definition: instance.cpp:2006
Input method mode changed.
std::string inputMethod(InputContext *ic)
Return the unique name of input method for given input context.
Definition: instance.cpp:1704
UserInterfaceManager & userInterfaceManager()
Get the user interface manager.
Definition: instance.cpp:1606
void enumerate(bool forward)
Enumerate input method with in current group.
Definition: instance.cpp:2153
bool exiting() const
Check whether fcitx is in exiting process.
Definition: instance.cpp:1385
bool startsWith(std::string_view str, std::string_view prefix)
Check if a string starts with a prefix.
Definition: stringutils.cpp:86
Key sym related types.
void showInputMethodInformation(InputContext *ic)
Show a small popup with input popup window with current input method information. ...
Definition: instance.cpp:2516
StatusArea & statusArea()
Returns the associated StatusArea.
String handle utilities.
Input Method Manager For fcitx.
EventType type() const
Type of event, can be used to decide event class.
Definition: event.h:222
bool empty() const
Whether input panel is totally empty.
Definition: inputpanel.cpp:117
void setInputMethodMode(InputMethodMode mode)
Set the current global input method mode.
Definition: instance.cpp:1507
FCITXCORE_DEPRECATED void reset(ResetReason reason)
Called when input context state need to be reset.
ssize_t safeRead(int fd, void *data, size_t maxlen)
a simple wrapper around read(), ignore EINTR.
Definition: fs.cpp:231
std::string addonForInputMethod(const std::string &imName)
Return the addon name of given input method.
Definition: instance.cpp:1949
Input Context Property for Fcitx.
static std::filesystem::path fcitxPath(const char *path, const std::filesystem::path &subPath={})
Return fcitx specific path defined at compile time.
An input context represents a client of Fcitx.
Definition: inputcontext.h:47
GlobalConfig & globalConfig()
Get the global config.
Definition: instance.cpp:1611
void exit()
Exit the fcitx event loop.
Definition: instance.cpp:1988
EventDispatcher & eventDispatcher()
Return a shared event dispatcher that is already attached to instance&#39;s event loop.
Definition: instance.cpp:1581
Key event is generated when client press or release a key.
const InputMethodEntry * inputMethodEntry(InputContext *ic)
Return the input method entry for given input context.
Definition: instance.cpp:1740
Class to represent a key.
Log utilities.
bool isComposing(InputContext *inputContext)
Check whether input context is composing or not.
Definition: instance.cpp:1901
static std::string keyListToString(const Container &container, KeyStringFormat format=KeyStringFormat::Portable)
Convert a key list to string.
Definition: key.h:204
Notify the global config is reloaded.
Definition: event.h:657
when using lost focus this might be variance case to case.
Addon Manager class.
static const char * version()
Return the version string of Fcitx.
Definition: instance.cpp:2592
Text outputFilter(InputContext *inputContext, const Text &orig)
Update the string that will be displayed in user interface.
Definition: instance.cpp:2340