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