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