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