25 #include <string_view> 27 #include <unordered_map> 28 #include <unordered_set> 32 #include "fcitx-config/configuration.h" 33 #include "fcitx-config/iniparser.h" 34 #include "fcitx-config/option.h" 37 #include "fcitx-utils/environ.h" 38 #include "fcitx-utils/event.h" 39 #include "fcitx-utils/eventdispatcher.h" 40 #include "fcitx-utils/eventloopinterface.h" 42 #include "fcitx-utils/handlertable.h" 43 #include "fcitx-utils/i18n.h" 47 #include "fcitx-utils/macros.h" 48 #include "fcitx-utils/misc.h" 49 #include "fcitx-utils/misc_p.h" 54 #include "../../modules/notifications/notifications_public.h" 57 #include "focusgroup.h" 58 #include "globalconfig.h" 59 #include "inputcontextmanager.h" 61 #include "inputmethodengine.h" 62 #include "inputmethodentry.h" 63 #include "inputmethodgroup.h" 66 #include "instance_p.h" 73 #ifdef HAVE_SYS_WAIT_H 79 #include <../modules/xcb/xcb_public.h> 82 #ifdef ENABLE_KEYBOARD 83 #include <xkbcommon/xkbcommon-compose.h> 84 #include <xkbcommon/xkbcommon.h> 87 FCITX_DEFINE_LOG_CATEGORY(keyTrace,
"key_trace");
93 constexpr uint64_t AutoSaveMinInUsecs = 60ULL * 1000000ULL;
94 constexpr uint64_t AutoSaveIdleTime = 60ULL * 1000000ULL;
96 FCITX_CONFIGURATION(DefaultInputMethod,
97 Option<std::vector<std::string>> defaultInputMethods{
98 this,
"DefaultInputMethod",
"DefaultInputMethod"};
99 Option<std::vector<std::string>> extraLayouts{
100 this,
"ExtraLayout",
"ExtraLayout"};);
102 void initAsDaemon() {
106 waitpid(pid,
nullptr, 0);
110 auto oldint = signal(SIGINT, SIG_IGN);
111 auto oldhup = signal(SIGHUP, SIG_IGN);
112 auto oldquit = signal(SIGQUIT, SIG_IGN);
113 auto oldpipe = signal(SIGPIPE, SIG_IGN);
114 auto oldttou = signal(SIGTTOU, SIG_IGN);
115 auto oldttin = signal(SIGTTIN, SIG_IGN);
116 auto oldchld = signal(SIGCHLD, SIG_IGN);
122 signal(SIGINT, oldint);
123 signal(SIGHUP, oldhup);
124 signal(SIGQUIT, oldquit);
125 signal(SIGPIPE, oldpipe);
126 signal(SIGTTOU, oldttou);
127 signal(SIGTTIN, oldttin);
128 signal(SIGCHLD, oldchld);
133 bool shouldSwitchIM(
const CapabilityFlags &oldFlags,
134 const CapabilityFlags &newFlags) {
135 const bool oldDisable = oldFlags.testAny(
137 const bool newDisable = newFlags.testAny(
139 return oldDisable != newDisable;
144 void InstanceArgument::printUsage()
const {
146 <<
"Usage: " << argv0 <<
" [Option]\n" 147 <<
" --disable <addon names>\tA comma separated list of addons to " 149 <<
"\t\t\t\t\"all\" can be used to disable all addons.\n" 150 <<
" --enable <addon names>\tA comma separated list of addons to " 152 <<
"\t\t\t\t\"all\" can be used to enable all addons.\n" 153 <<
"\t\t\t\tThis value will override the value in the flag " 155 <<
" --verbose <logging rule>\tSet the logging rule for " 156 "displaying message.\n" 157 <<
"\t\t\t\tSyntax: category1=level1,category2=level2, ...\n" 158 <<
"\t\t\t\tE.g. default=4,key_trace=5\n" 159 <<
"\t\t\t\tLevels are numbers ranging from 0 to 5.\n" 160 <<
"\t\t\t\t\t0 - NoLog\n" 161 <<
"\t\t\t\t\t1 - Fatal\n" 162 <<
"\t\t\t\t\t2 - Error\n" 163 <<
"\t\t\t\t\t3 - Warn\n" 164 <<
"\t\t\t\t\t4 - Info (default)\n" 165 <<
"\t\t\t\t\t5 - Debug\n" 166 <<
"\t\t\t\tSome built-in categories are:\n" 167 <<
"\t\t\t\t\tdefault - miscellaneous category used by fcitx own " 169 <<
"\t\t\t\t\tkey_trace - print the key event received by fcitx.\n" 170 <<
"\t\t\t\t\t\"*\" may be used to represent all logging " 172 <<
" -u, --ui <addon name>\t\tSet the UI addon to be used.\n" 173 <<
" -d\t\t\t\tRun as a daemon.\n" 174 <<
" -D\t\t\t\tDo not run as a daemon (default).\n" 175 <<
" -s <seconds>\t\t\tNumber of seconds to wait before start.\n" 176 <<
" -k, --keep\t\t\tKeep running even the main display is " 178 <<
" -r, --replace\t\t\tReplace the existing instance.\n" 179 <<
" -o --option <option>\t\tPass the option to addons\n" 180 <<
"\t\t\t\t<option> is in format like:\n" 181 <<
"\t\t\t\tname1=opt1a:opt1b,name2=opt2a:opt2b... .\n" 182 <<
" -v, --version\t\t\tShow version and quit.\n" 183 <<
" -h, --help\t\t\tShow this help message and quit.\n";
186 InstancePrivate::InstancePrivate(Instance *q) : QPtrHolder<Instance>(q) {
187 #ifdef ENABLE_KEYBOARD 188 auto locale = getEnvironment(
"LC_ALL");
190 locale = getEnvironment(
"LC_CTYPE");
193 locale = getEnvironment(
"LANG");
198 assert(locale.has_value());
199 xkbContext_.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
201 xkb_context_set_log_level(xkbContext_.get(), XKB_LOG_LEVEL_CRITICAL);
202 xkbComposeTable_.reset(xkb_compose_table_new_from_locale(
203 xkbContext_.get(), locale->data(), XKB_COMPOSE_COMPILE_NO_FLAGS));
204 if (!xkbComposeTable_) {
206 <<
"Trying to fallback to compose table for en_US.UTF-8";
207 xkbComposeTable_.reset(xkb_compose_table_new_from_locale(
208 xkbContext_.get(),
"en_US.UTF-8",
209 XKB_COMPOSE_COMPILE_NO_FLAGS));
211 if (!xkbComposeTable_) {
213 <<
"No compose table is loaded, you may want to check your " 220 std::unique_ptr<HandlerTableEntry<EventHandler>>
221 InstancePrivate::watchEvent(
EventType type, EventWatcherPhase phase,
222 EventHandler callback) {
223 return eventHandlers_[type][phase].add(std::move(callback));
226 #ifdef ENABLE_KEYBOARD 227 xkb_keymap *InstancePrivate::keymap(
const std::string &display,
228 const std::string &layout,
229 const std::string &variant) {
230 auto layoutAndVariant = stringutils::concat(layout,
"-", variant);
231 if (
auto *keymapPtr = findValue(keymapCache_[display], layoutAndVariant)) {
232 return (*keymapPtr).get();
234 struct xkb_rule_names names;
235 names.layout = layout.c_str();
236 names.variant = variant.c_str();
237 std::tuple<std::string, std::string, std::string> xkbParam;
238 if (
auto *param = findValue(xkbParams_, display)) {
241 if (!xkbParams_.empty()) {
242 xkbParam = xkbParams_.begin()->second;
244 xkbParam = std::make_tuple(DEFAULT_XKB_RULES,
"pc101",
"");
247 if (globalConfig_.overrideXkbOption()) {
248 std::get<2>(xkbParam) = globalConfig_.customXkbOption();
250 names.rules = std::get<0>(xkbParam).c_str();
251 names.model = std::get<1>(xkbParam).c_str();
252 names.options = std::get<2>(xkbParam).c_str();
253 UniqueCPtr<xkb_keymap, xkb_keymap_unref> keymap(xkb_keymap_new_from_names(
254 xkbContext_.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS));
256 keymapCache_[display].emplace(layoutAndVariant, std::move(keymap));
257 assert(result.second);
258 return result.first->second.get();
262 std::pair<std::unordered_set<std::string>, std::unordered_set<std::string>>
263 InstancePrivate::overrideAddons() {
264 std::unordered_set<std::string> enabled;
265 std::unordered_set<std::string> disabled;
266 for (
const auto &addon : globalConfig_.enabledAddons()) {
267 enabled.insert(addon);
269 for (
const auto &addon : globalConfig_.disabledAddons()) {
270 enabled.erase(addon);
271 disabled.insert(addon);
273 for (
auto &addon : arg_.enableList) {
274 disabled.erase(addon);
275 enabled.insert(addon);
277 for (
auto &addon : arg_.disableList) {
278 enabled.erase(addon);
279 disabled.insert(addon);
281 return {enabled, disabled};
284 void InstancePrivate::buildDefaultGroup() {
286 auto *defaultGroup = q_func()->defaultFocusGroup();
287 bool infoFound =
false;
289 std::string variants;
290 auto guessLayout = [
this, &layouts, &variants,
291 &infoFound](FocusGroup *focusGroup) {
297 auto *xcb = addonManager_.addon(
"xcb");
298 auto x11Name = focusGroup->display().substr(4);
300 auto rules = xcb->call<IXCBModule::xkbRulesNames>(x11Name);
301 if (!rules[2].
empty()) {
310 FCITX_UNUSED(layouts);
311 FCITX_UNUSED(variants);
312 FCITX_UNUSED(infoFound);
316 if (!defaultGroup || guessLayout(defaultGroup)) {
317 icManager_.foreachGroup(
318 [defaultGroup, &guessLayout](FocusGroup *focusGroup) {
319 if (defaultGroup == focusGroup) {
322 return guessLayout(focusGroup);
331 constexpr
char imNamePrefix[] =
"keyboard-";
335 variants,
",", stringutils::SplitBehavior::KeepEmpty);
336 auto size = std::max(layoutTokens.size(), variantTokens.size());
338 layoutTokens.resize(size);
339 variantTokens.resize(size);
341 OrderedSet<std::string> imLayouts;
342 for (decltype(size) i = 0; i < size; i++) {
343 if (layoutTokens[i].
empty()) {
346 std::string layoutName = layoutTokens[i];
347 if (!variantTokens[i].
empty()) {
348 layoutName = stringutils::concat(layoutName,
"-", variantTokens[i]);
352 if (!imManager_.entry(stringutils::concat(imNamePrefix, layoutName))) {
356 imLayouts.pushBack(layoutName);
360 auto lang = stripLanguage(getCurrentLanguage());
361 DefaultInputMethod defaultIMConfig;
362 readAsIni(defaultIMConfig, StandardPathsType::PkgData,
363 std::filesystem::path(
"default") / lang);
366 for (
const auto &extraLayout : defaultIMConfig.extraLayouts.value()) {
367 if (!imManager_.entry(stringutils::concat(imNamePrefix, extraLayout))) {
370 imLayouts.pushBack(extraLayout);
374 if (imLayouts.empty()) {
375 imLayouts.pushBack(
"us");
379 std::string defaultIM;
380 for (
const auto &im : defaultIMConfig.defaultInputMethods.value()) {
381 if (imManager_.entry(im)) {
388 std::vector<std::string> groupOrders;
389 for (
const auto &imLayout : imLayouts) {
390 std::string groupName;
391 if (imLayouts.size() == 1) {
392 groupName = _(
"Default");
394 groupName = _(
"Group {}", imManager_.groupCount() + 1);
396 imManager_.addEmptyGroup(groupName);
397 groupOrders.push_back(groupName);
398 InputMethodGroup group(groupName);
399 group.inputMethodList().emplace_back(
400 InputMethodGroupItem(stringutils::concat(imNamePrefix, imLayout)));
401 if (!defaultIM.empty()) {
402 group.inputMethodList().emplace_back(
403 InputMethodGroupItem(defaultIM));
405 FCITX_INFO() <<
"Items in " << groupName <<
": " 406 << group.inputMethodList();
407 group.setDefaultLayout(imLayout);
408 imManager_.setGroup(std::move(group));
410 FCITX_INFO() <<
"Generated groups: " << groupOrders;
411 imManager_.setGroupOrder(groupOrders);
414 void InstancePrivate::showInputMethodInformation(InputContext *ic) {
416 auto *inputState = ic->propertyFor(&inputStateFactory_);
417 auto *engine = q->inputMethodEngine(ic);
418 const auto *entry = q->inputMethodEntry(ic);
419 auto &imManager = q->inputMethodManager();
421 if (!inputState->isActive() &&
422 !globalConfig_.showFirstInputMethodInformation()) {
428 auto subMode = engine->subMode(*entry, *ic);
429 auto subModeLabel = engine->subModeLabel(*entry, *ic);
430 auto name = globalConfig_.compactInputMethodInformation() &&
431 !entry->label().empty()
434 if (globalConfig_.compactInputMethodInformation() &&
435 !subModeLabel.empty()) {
436 display = std::move(subModeLabel);
437 }
else if (subMode.empty()) {
438 display = std::move(name);
440 display = _(
"{0} ({1})", name, subMode);
443 display = _(
"{0} (Not available)", entry->name());
445 display = _(
"(Not available)");
447 if (!globalConfig_.compactInputMethodInformation() &&
448 imManager.groupCount() > 1) {
449 display = _(
"Group {0}: {1}", imManager.currentGroup().name(), display);
451 inputState->showInputMethodInformation(display);
454 bool InstancePrivate::canActivate(InputContext *ic) {
456 if (!q->canTrigger()) {
459 auto *inputState = ic->propertyFor(&inputStateFactory_);
460 return !inputState->isActive();
463 bool InstancePrivate::canDeactivate(InputContext *ic) {
465 if (!q->canTrigger()) {
468 auto *inputState = ic->propertyFor(&inputStateFactory_);
469 return inputState->isActive();
472 void InstancePrivate::navigateGroup(InputContext *ic,
const Key &key,
474 auto *inputState = ic->propertyFor(&inputStateFactory_);
475 inputState->pendingGroupIndex_ =
476 (inputState->pendingGroupIndex_ +
477 (forward ? 1 : imManager_.groupCount() - 1)) %
478 imManager_.groupCount();
479 FCITX_DEBUG() <<
"Switch to group " << inputState->pendingGroupIndex_;
481 if (notifications_ && !isSingleKey(key)) {
482 notifications_->call<INotifications::showTip>(
483 "enumerate-group", _(
"Input Method"),
"input-keyboard",
485 _(
"Switch group to {0}",
486 imManager_.groups()[inputState->pendingGroupIndex_]),
491 void InstancePrivate::acceptGroupChange(
const Key &key, InputContext *ic) {
492 FCITX_DEBUG() <<
"Accept group change, isSingleKey: " << key;
494 auto *inputState = ic->propertyFor(&inputStateFactory_);
495 auto groups = imManager_.groups();
496 if (groups.size() > inputState->pendingGroupIndex_) {
497 if (isSingleKey(key)) {
498 FCITX_DEBUG() <<
"EnumerateGroupTo: " 499 << inputState->pendingGroupIndex_ <<
" " << key;
500 imManager_.enumerateGroupTo(groups[inputState->pendingGroupIndex_]);
502 FCITX_DEBUG() <<
"SetCurrentGroup: " 503 << inputState->pendingGroupIndex_ <<
" " << key;
504 imManager_.setCurrentGroup(groups[inputState->pendingGroupIndex_]);
507 inputState->pendingGroupIndex_ = 0;
510 InputState::InputState(InstancePrivate *d, InputContext *ic)
511 : d_ptr(d), ic_(ic) {
512 active_ = d->globalConfig_.activeByDefault();
513 #ifdef ENABLE_KEYBOARD 514 if (d->xkbComposeTable_) {
515 xkbComposeState_.reset(xkb_compose_state_new(
516 d->xkbComposeTable_.get(), XKB_COMPOSE_STATE_NO_FLAGS));
521 void InputState::showInputMethodInformation(
const std::string &name) {
522 ic_->inputPanel().setAuxUp(Text(name));
525 imInfoTimer_ = d_ptr->eventLoop_.addTimeEvent(
526 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
527 [
this](EventSourceTime *, uint64_t) {
528 hideInputMethodInfo();
533 #ifdef ENABLE_KEYBOARD 534 xkb_state *InputState::customXkbState(
bool refresh) {
535 auto *instance = d_ptr->q_func();
536 const InputMethodGroup &group = d_ptr->imManager_.currentGroup();
537 const auto im = instance->inputMethod(ic_);
538 auto layout = group.layoutFor(im);
540 layout = im.substr(9);
542 if (layout.empty() || layout == group.defaultLayout()) {
545 modsAllReleased_ =
false;
546 lastXkbLayout_.clear();
550 if (layout == lastXkbLayout_ && !refresh) {
551 return xkbState_.get();
554 lastXkbLayout_ = layout;
555 const auto layoutAndVariant = parseLayout(layout);
556 if (
auto *keymap = d_ptr->keymap(ic_->display(), layoutAndVariant.first,
557 layoutAndVariant.second)) {
558 xkbState_.reset(xkb_state_new(keymap));
562 modsAllReleased_ =
false;
563 return xkbState_.get();
567 void InputState::setActive(
bool active) {
568 if (active_ != active) {
570 ic_->updateProperty(&d_ptr->inputStateFactory_);
574 void InputState::setLocalIM(
const std::string &localIM) {
575 if (localIM_ != localIM) {
577 ic_->updateProperty(&d_ptr->inputStateFactory_);
581 void InputState::copyTo(InputContextProperty *other) {
582 auto *otherState =
static_cast<InputState *
>(other);
583 if (otherState->active_ == active_ && otherState->localIM_ == localIM_) {
587 if (otherState->ic_->hasFocus()) {
588 FCITX_DEBUG() <<
"Sync state to focused ic: " 589 << otherState->ic_->program();
590 CheckInputMethodChanged imChangedRAII(otherState->ic_, d_ptr);
591 otherState->active_ = active_;
592 otherState->localIM_ = localIM_;
594 otherState->active_ = active_;
595 otherState->localIM_ = localIM_;
599 void InputState::reset() {
600 #ifdef ENABLE_KEYBOARD 601 if (xkbComposeState_) {
602 xkb_compose_state_reset(xkbComposeState_.get());
605 pendingGroupIndex_ = 0;
607 lastKeyPressed_ = Key();
608 lastKeyPressedTime_ = 0;
609 totallyReleased_ =
true;
612 void InputState::hideInputMethodInfo() {
616 imInfoTimer_.reset();
617 auto &panel = ic_->inputPanel();
618 if (panel.auxDown().empty() && panel.preedit().empty() &&
619 panel.clientPreedit().empty() &&
620 (!panel.candidateList() || panel.candidateList()->empty()) &&
621 panel.auxUp().size() == 1 && panel.auxUp().stringAt(0) == lastInfo_) {
627 #ifdef ENABLE_KEYBOARD 628 void InputState::resetXkbState() {
629 lastXkbLayout_.clear();
634 CheckInputMethodChanged::CheckInputMethodChanged(InputContext *ic,
635 InstancePrivate *instance)
636 : instance_(instance->q_func()), instancePrivate_(instance),
637 ic_(ic->watch()), inputMethod_(instance_->inputMethod(ic)),
639 auto *inputState = ic->propertyFor(&instance->inputStateFactory_);
640 if (!inputState->imChanged_) {
641 inputState->imChanged_ =
this;
647 CheckInputMethodChanged::~CheckInputMethodChanged() {
648 if (!ic_.isValid()) {
651 auto *ic = ic_.get();
652 auto *inputState = ic->propertyFor(&instancePrivate_->inputStateFactory_);
653 inputState->imChanged_ =
nullptr;
654 if (inputMethod_ != instance_->inputMethod(ic) && !ignore_) {
655 instance_->postEvent(
656 InputContextSwitchInputMethodEvent(reason_, inputMethod_, ic));
661 InstanceArgument arg;
662 arg.parseOption(argc, argv);
667 if (arg.runAsDaemon) {
671 if (arg.overrideDelay > 0) {
672 sleep(arg.overrideDelay);
676 d_ptr = std::make_unique<InstancePrivate>(
this);
679 d->eventDispatcher_.attach(&d->eventLoop_);
680 d->addonManager_.setInstance(
this);
681 d->addonManager_.setAddonOptions(arg.addonOptions_);
682 d->icManager_.setInstance(
this);
683 d->connections_.emplace_back(
684 d->imManager_.connect<InputMethodManager::CurrentGroupAboutToChange>(
685 [
this, d](
const std::string &lastGroup) {
686 d->icManager_.foreachFocused([this](InputContext *ic) {
687 assert(ic->hasFocus());
688 InputContextSwitchInputMethodEvent event(
689 InputMethodSwitchedReason::GroupChange, inputMethod(ic),
691 deactivateInputMethod(event);
694 d->lastGroup_ = lastGroup;
697 d->connections_.emplace_back(
698 d->imManager_.connect<InputMethodManager::CurrentGroupChanged>(
699 [
this, d](
const std::string &newGroup) {
700 d->icManager_.foreachFocused([this](InputContext *ic) {
701 assert(ic->hasFocus());
702 InputContextSwitchInputMethodEvent event(
703 InputMethodSwitchedReason::GroupChange,
"", ic);
704 activateInputMethod(event);
708 if (!d->lastGroup_.empty() && !newGroup.empty() &&
709 d->lastGroup_ != newGroup && d->notifications_ &&
710 d->imManager_.groupCount() > 1) {
711 d->notifications_->call<INotifications::showTip>(
712 "enumerate-group", _(
"Input Method"),
"input-keyboard",
714 _(
"Switched group to {0}",
715 d->imManager_.currentGroup().name()),
718 d->lastGroup_ = newGroup;
721 d->eventWatchers_.emplace_back(d->watchEvent(
722 EventType::InputContextCapabilityAboutToChange,
723 EventWatcherPhase::ReservedFirst, [
this](
Event &event) {
725 static_cast<CapabilityAboutToChangeEvent &>(event);
726 if (!capChanged.inputContext()->hasFocus()) {
730 if (!shouldSwitchIM(capChanged.oldFlags(), capChanged.newFlags())) {
736 inputMethod(capChanged.inputContext()),
737 capChanged.inputContext());
738 deactivateInputMethod(switchIM);
740 d->eventWatchers_.emplace_back(d->watchEvent(
741 EventType::InputContextCapabilityChanged,
742 EventWatcherPhase::ReservedFirst, [
this](
Event &event) {
743 auto &capChanged = static_cast<CapabilityChangedEvent &>(event);
744 if (!capChanged.inputContext()->hasFocus()) {
748 if (!shouldSwitchIM(capChanged.oldFlags(), capChanged.newFlags())) {
754 capChanged.inputContext());
755 activateInputMethod(switchIM);
758 d->eventWatchers_.emplace_back(watchEvent(
760 [
this, d](
Event &event) {
761 auto &keyEvent =
static_cast<KeyEvent &
>(event);
762 auto *ic = keyEvent.inputContext();
763 CheckInputMethodChanged imChangedRAII(ic, d);
764 auto origKey = keyEvent.origKey().normalize();
768 std::function<bool()> check;
769 std::function<void(bool)> trigger;
771 {.list = d->globalConfig_.triggerKeys(),
772 .check = [
this]() {
return canTrigger(); },
774 [
this, ic](
bool totallyReleased) {
775 return trigger(ic, totallyReleased);
777 {.list = d->globalConfig_.altTriggerKeys(),
778 .check = [
this, ic]() {
return canAltTrigger(ic); },
779 .trigger = [
this, ic](bool) {
return altTrigger(ic); }},
780 {.list = d->globalConfig_.activateKeys(),
781 .check = [ic, d]() {
return d->canActivate(ic); },
782 .trigger = [
this, ic](bool) {
return activate(ic); }},
783 {.list = d->globalConfig_.deactivateKeys(),
784 .check = [ic, d]() {
return d->canDeactivate(ic); },
785 .trigger = [
this, ic](bool) {
return deactivate(ic); }},
786 {.list = d->globalConfig_.enumerateForwardKeys(),
787 .check = [
this, ic]() {
return canEnumerate(ic); },
788 .trigger = [
this, ic](bool) {
return enumerate(ic,
true); }},
789 {.list = d->globalConfig_.enumerateBackwardKeys(),
790 .check = [
this, ic]() {
return canEnumerate(ic); },
791 .trigger = [
this, ic](bool) {
return enumerate(ic,
false); }},
792 {.list = d->globalConfig_.enumerateGroupForwardKeys(),
793 .check = [
this]() {
return canChangeGroup(); },
794 .trigger = [ic, d, origKey](
795 bool) { d->navigateGroup(ic, origKey,
true); }},
796 {.list = d->globalConfig_.enumerateGroupBackwardKeys(),
797 .check = [
this]() {
return canChangeGroup(); },
799 [ic, d, origKey](bool) {
800 d->navigateGroup(ic, origKey,
false);
804 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
805 int keyReleased = inputState->keyReleased_;
806 Key lastKeyPressed = inputState->lastKeyPressed_;
808 inputState->keyReleased_ = -1;
810 if (keyEvent.isRelease()) {
812 for (
auto &keyHandler : keyHandlers) {
813 if (keyReleased == idx &&
814 origKey.isReleaseOfModifier(lastKeyPressed) &&
815 keyHandler.check()) {
817 if (d->globalConfig_.checkModifierOnlyKeyTimeout(
818 inputState->lastKeyPressedTime_)) {
820 inputState->totallyReleased_);
822 inputState->lastKeyPressedTime_ = 0;
823 if (origKey.hasModifier()) {
824 inputState->totallyReleased_ =
false;
832 if (isSingleModifier(origKey)) {
833 inputState->totallyReleased_ =
true;
837 if (inputState->pendingGroupIndex_ &&
838 inputState->totallyReleased_) {
839 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
840 if (inputState->imChanged_) {
841 inputState->imChanged_->ignore();
843 d->acceptGroupChange(lastKeyPressed, ic);
844 inputState->lastKeyPressed_ =
Key();
847 if (!keyEvent.filtered() && !keyEvent.isRelease()) {
849 for (
auto &keyHandler : keyHandlers) {
850 auto keyIdx = origKey.keyListIndex(keyHandler.list);
851 if (keyIdx >= 0 && keyHandler.check()) {
852 inputState->keyReleased_ = idx;
853 inputState->lastKeyPressed_ = origKey;
855 inputState->lastKeyPressedTime_ =
856 now(CLOCK_MONOTONIC);
862 keyHandler.trigger(inputState->totallyReleased_);
863 if (origKey.hasModifier()) {
864 inputState->totallyReleased_ =
false;
866 keyEvent.filterAndAccept();
873 d->eventWatchers_.emplace_back(watchEvent(
876 auto &keyEvent =
static_cast<KeyEvent &
>(event);
877 auto *ic = keyEvent.inputContext();
878 if (!keyEvent.isRelease() &&
879 keyEvent.key().checkKeyList(
880 d->globalConfig_.togglePreeditKeys())) {
882 if (d->notifications_) {
883 d->notifications_->call<INotifications::showTip>(
884 "toggle-preedit", _(
"Input Method"),
"", _(
"Preedit"),
885 ic->isPreeditEnabled() ? _(
"Preedit enabled")
886 : _(
"Preedit disabled"),
889 keyEvent.filterAndAccept();
892 d->eventWatchers_.emplace_back(d->watchEvent(
896 d->idleStartTimestamp_ = now(CLOCK_MONOTONIC);
897 auto &keyEvent = static_cast<KeyEvent &>(event);
898 auto *ic = keyEvent.inputContext();
899 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
900 #ifdef ENABLE_KEYBOARD
901 auto *xkbState = inputState->customXkbState();
903 if (auto *mods = findValue(d->stateMask_, ic->display())) {
904 FCITX_KEYTRACE() <<
"Update mask to customXkbState";
908 if (inputState->isModsAllReleased()) {
909 depressed = xkb_state_serialize_mods(
910 xkbState, XKB_STATE_MODS_DEPRESSED);
912 depressed = std::get<0>(*mods);
914 if (std::get<0>(*mods) == 0) {
915 inputState->setModsAllReleased();
917 auto latched = xkb_state_serialize_mods(
918 xkbState, XKB_STATE_MODS_LATCHED);
919 auto locked = std::get<2>(*mods);
925 << depressed <<
" " << latched <<
" " << locked;
926 xkb_state_update_mask(xkbState, depressed, latched, locked,
929 const uint32_t effective = xkb_state_serialize_mods(
930 xkbState, XKB_STATE_MODS_EFFECTIVE);
931 auto newSym = xkb_state_key_get_one_sym(
932 xkbState, keyEvent.rawKey().code());
933 auto newModifier = KeyStates(effective);
934 auto *keymap = xkb_state_get_keymap(xkbState);
935 if (keyEvent.rawKey().states().test(KeyState::Repeat) &&
936 xkb_keymap_key_repeats(keymap, keyEvent.rawKey().code())) {
937 newModifier |= KeyState::Repeat;
940 const uint32_t modsDepressed = xkb_state_serialize_mods(
941 xkbState, XKB_STATE_MODS_DEPRESSED);
942 const uint32_t modsLatched =
943 xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED);
944 const uint32_t modsLocked =
945 xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED);
946 FCITX_KEYTRACE() <<
"Current mods: " << modsDepressed <<
" " 947 << modsLatched <<
" " << modsLocked;
948 auto newCode = keyEvent.rawKey().code();
949 Key key(static_cast<KeySym>(newSym), newModifier, newCode);
951 <<
"Custom Xkb translated Key: " << key.toString();
952 keyEvent.setRawKey(key);
955 FCITX_KEYTRACE() <<
"KeyEvent: " << keyEvent.key()
956 <<
" rawKey: " << keyEvent.rawKey()
957 <<
" origKey: " << keyEvent.origKey()
958 <<
" Release:" << keyEvent.isRelease()
959 <<
" keycode: " << keyEvent.origKey().code()
960 <<
" program: " << ic->program();
962 if (keyEvent.isRelease()) {
965 inputState->hideInputMethodInfo();
967 d->eventWatchers_.emplace_back(
969 EventWatcherPhase::InputMethod, [
this](
Event &event) {
970 auto &keyEvent =
static_cast<KeyEvent &
>(event);
971 auto *ic = keyEvent.inputContext();
972 auto *engine = inputMethodEngine(ic);
973 const auto *entry = inputMethodEntry(ic);
974 if (!engine || !entry) {
977 engine->keyEvent(*entry, keyEvent);
979 d->eventWatchers_.emplace_back(watchEvent(
980 EventType::InputContextVirtualKeyboardEvent,
981 EventWatcherPhase::InputMethod, [
this](
Event &event) {
983 auto *ic = keyEvent.inputContext();
984 auto *engine = inputMethodEngine(ic);
985 const auto *entry = inputMethodEntry(ic);
986 if (!engine || !entry) {
989 engine->virtualKeyboardEvent(*entry, keyEvent);
991 d->eventWatchers_.emplace_back(watchEvent(
993 [
this](
Event &event) {
995 auto *ic = invokeActionEvent.inputContext();
996 auto *engine = inputMethodEngine(ic);
997 const auto *entry = inputMethodEntry(ic);
998 if (!engine || !entry) {
1001 engine->invokeAction(*entry, invokeActionEvent);
1003 d->eventWatchers_.emplace_back(d->watchEvent(
1005 [
this](
Event &event) {
1006 auto &keyEvent = static_cast<KeyEvent &>(event);
1007 auto *ic = keyEvent.inputContext();
1008 auto *engine = inputMethodEngine(ic);
1009 const auto *entry = inputMethodEntry(ic);
1010 if (!engine || !entry) {
1013 engine->filterKey(*entry, keyEvent);
1014 emit<Instance::KeyEventResult>(keyEvent);
1015 #ifdef ENABLE_KEYBOARD 1016 if (keyEvent.forward()) {
1021 if (keyEvent.isRelease()) {
1025 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1026 if (
auto *xkbState = inputState->customXkbState()) {
1027 if (
auto utf32 = xkb_state_key_get_utf32(
1028 xkbState, keyEvent.key().code())) {
1030 if (utf32 ==
'\n' || utf32 ==
'\b' || utf32 ==
'\r' ||
1031 utf32 ==
'\t' || utf32 ==
'\033' ||
1035 if (keyEvent.key().states().test(KeyState::Ctrl) ||
1036 keyEvent.rawKey().sym() ==
1037 keyEvent.origKey().sym()) {
1040 FCITX_KEYTRACE() <<
"Will commit char: " << utf32;
1042 keyEvent.filterAndAccept();
1043 }
else if (!keyEvent.key().states().test(KeyState::Ctrl) &&
1044 keyEvent.rawKey().sym() !=
1045 keyEvent.origKey().sym() &&
1050 keyEvent.filterAndAccept();
1056 d->eventWatchers_.emplace_back(d->watchEvent(
1058 [
this, d](
Event &event) {
1059 auto &icEvent = static_cast<InputContextEvent &>(event);
1060 auto isSameProgram = [&icEvent, d]() {
1062 return (icEvent.inputContext() == d->lastUnFocusedIc_.get()) ||
1063 (!icEvent.inputContext()->program().empty() &&
1064 (icEvent.inputContext()->program() ==
1065 d->lastUnFocusedProgram_));
1068 if (d->globalConfig_.resetStateWhenFocusIn() ==
1069 PropertyPropagatePolicy::All ||
1070 (d->globalConfig_.resetStateWhenFocusIn() ==
1071 PropertyPropagatePolicy::Program &&
1072 !isSameProgram())) {
1073 if (d->globalConfig_.activeByDefault()) {
1074 activate(icEvent.inputContext());
1076 deactivate(icEvent.inputContext());
1080 activateInputMethod(icEvent);
1082 auto *inputContext = icEvent.inputContext();
1083 if (!inputContext->clientControlVirtualkeyboardShow()) {
1084 inputContext->showVirtualKeyboard();
1087 if (!d->globalConfig_.showInputMethodInformationWhenFocusIn() ||
1088 icEvent.inputContext()->capabilityFlags().test(
1094 d->focusInImInfoTimer_ = d->eventLoop_.addTimeEvent(
1095 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 30000, 0,
1099 if (
auto *ic = icRef.get(); ic && ic->hasFocus()) {
1100 d->showInputMethodInformation(ic);
1105 d->eventWatchers_.emplace_back(d->watchEvent(
1108 auto &icEvent = static_cast<InputContextEvent &>(event);
1109 auto *ic = icEvent.inputContext();
1110 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1111 inputState->reset();
1112 if (!ic->capabilityFlags().test(
1113 CapabilityFlag::ClientUnfocusCommit)) {
1116 ic->inputPanel().clientPreedit().toStringForCommit();
1117 if (!commit.empty()) {
1118 ic->commitString(commit);
1122 d->eventWatchers_.emplace_back(d->watchEvent(
1124 [
this, d](
Event &event) {
1125 auto &icEvent = static_cast<InputContextEvent &>(event);
1126 d->lastUnFocusedProgram_ = icEvent.inputContext()->program();
1127 d->lastUnFocusedIc_ = icEvent.inputContext()->watch();
1128 deactivateInputMethod(icEvent);
1130 auto *inputContext = icEvent.inputContext();
1131 if (!inputContext->clientControlVirtualkeyboardHide()) {
1132 inputContext->hideVirtualKeyboard();
1135 d->eventWatchers_.emplace_back(d->watchEvent(
1138 auto &icEvent = static_cast<InputContextEvent &>(event);
1139 auto *ic = icEvent.inputContext();
1140 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1141 inputState->reset();
1143 d->eventWatchers_.emplace_back(
1145 [
this](
Event &event) {
1147 auto *ic = icEvent.inputContext();
1148 if (!ic->hasFocus()) {
1151 auto *engine = inputMethodEngine(ic);
1152 const auto *entry = inputMethodEntry(ic);
1153 if (!engine || !entry) {
1156 engine->reset(*entry, icEvent);
1158 d->eventWatchers_.emplace_back(d->watchEvent(
1160 EventWatcherPhase::ReservedFirst, [
this](
Event &event) {
1162 static_cast<InputContextSwitchInputMethodEvent &>(event);
1163 auto *ic = icEvent.inputContext();
1164 if (!ic->hasFocus()) {
1167 deactivateInputMethod(icEvent);
1168 activateInputMethod(icEvent);
1170 d->eventWatchers_.emplace_back(d->watchEvent(
1172 EventWatcherPhase::ReservedLast, [
this, d](
Event &event) {
1174 static_cast<InputContextSwitchInputMethodEvent &>(event);
1175 auto *ic = icEvent.inputContext();
1176 if (!ic->hasFocus()) {
1180 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1181 inputState->lastIMChangeIsAltTrigger_ =
1193 showInputMethodInformation(ic);
1195 d->eventWatchers_.emplace_back(
1197 EventWatcherPhase::ReservedLast, [
this, d](
Event &) {
1200 d->imGroupInfoTimer_ = d->eventLoop_.addTimeEvent(
1201 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 30000, 0,
1202 [this](EventSourceTime *, uint64_t) {
1203 inputContextManager().foreachFocused(
1204 [this](InputContext *ic) {
1205 showInputMethodInformation(ic);
1212 d->eventWatchers_.emplace_back(d->watchEvent(
1213 EventType::InputContextUpdateUI, EventWatcherPhase::ReservedFirst,
1215 auto &icEvent = static_cast<InputContextUpdateUIEvent &>(event);
1216 if (icEvent.immediate()) {
1217 d->uiManager_.update(icEvent.component(),
1218 icEvent.inputContext());
1219 d->uiManager_.flush();
1221 d->uiManager_.update(icEvent.component(),
1222 icEvent.inputContext());
1223 d->uiUpdateEvent_->setOneShot();
1226 d->eventWatchers_.emplace_back(d->watchEvent(
1227 EventType::InputContextDestroyed, EventWatcherPhase::ReservedFirst,
1229 auto &icEvent = static_cast<InputContextEvent &>(event);
1230 d->uiManager_.expire(icEvent.inputContext());
1232 d->eventWatchers_.emplace_back(d->watchEvent(
1234 [d](
Event &) { d->uiManager_.updateAvailability(); }));
1235 d->uiUpdateEvent_ = d->eventLoop_.addDeferEvent([d](
EventSource *) {
1236 d->uiManager_.flush();
1239 d->uiUpdateEvent_->setEnabled(
false);
1240 d->periodicalSave_ = d->eventLoop_.addTimeEvent(
1241 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, AutoSaveIdleTime,
1248 auto currentTime = now(CLOCK_MONOTONIC);
1249 if (currentTime <= d->idleStartTimestamp_ ||
1250 currentTime - d->idleStartTimestamp_ < AutoSaveIdleTime) {
1252 time->setNextInterval(2 * AutoSaveIdleTime);
1257 FCITX_INFO() <<
"Running autosave...";
1259 FCITX_INFO() <<
"End autosave";
1260 if (d->globalConfig_.autoSavePeriod() > 0) {
1261 time->setNextInterval(d->globalConfig_.autoSavePeriod() *
1262 AutoSaveMinInUsecs);
1267 d->periodicalSave_->setEnabled(
false);
1270 Instance::~Instance() {
1272 d->icManager_.finalize();
1273 d->addonManager_.unload();
1274 d->notifications_ =
nullptr;
1275 d->icManager_.setInstance(
nullptr);
1278 void InstanceArgument::parseOption(
int argc,
char **argv) {
1284 struct option longOptions[] = {{
"enable", required_argument,
nullptr, 0},
1285 {
"disable", required_argument,
nullptr, 0},
1286 {
"verbose", required_argument,
nullptr, 0},
1287 {
"keep", no_argument,
nullptr,
'k'},
1288 {
"ui", required_argument,
nullptr,
'u'},
1289 {
"replace", no_argument,
nullptr,
'r'},
1290 {
"version", no_argument,
nullptr,
'v'},
1291 {
"help", no_argument,
nullptr,
'h'},
1292 {
"option", required_argument,
nullptr,
'o'},
1293 {
nullptr, 0, 0, 0}};
1295 int optionIndex = 0;
1297 std::string addonOptionString;
1298 while ((c = getopt_long(argc, argv,
"ru:dDs:hvo:", longOptions,
1299 &optionIndex)) != EOF) {
1302 switch (optionIndex) {
1310 Log::setLogRule(optarg);
1328 runAsDaemon =
false;
1331 exitWhenMainDisplayDisconnected =
false;
1334 overrideDelay = std::atoi(optarg);
1345 addonOptionString = optarg;
1356 std::unordered_map<std::string, std::vector<std::string>> addonOptions;
1357 for (
const std::string_view item :
1360 if (tokens.size() != 2) {
1365 addonOptions_ = std::move(addonOptions);
1373 d->signalPipe_ = fd;
1374 d->signalPipeEvent_ = d->eventLoop_.addIOEvent(
1384 return d->arg_.tryReplace;
1389 return d->arg_.exitWhenMainDisplayDisconnected;
1397 void Instance::handleSignal() {
1401 while (
fs::safeRead(d->signalPipe_, &signo,
sizeof(signo)) > 0) {
1402 if (signo == SIGINT || signo == SIGTERM || signo == SIGQUIT ||
1405 }
else if (signo == SIGUSR1) {
1407 }
else if (signo == SIGCHLD) {
1408 d->zombieReaper_->setNextInterval(2000000);
1409 d->zombieReaper_->setOneShot();
1417 if (!d->arg_.uiName.empty()) {
1418 d->arg_.enableList.push_back(d->arg_.uiName);
1421 d->icManager_.registerProperty(
"inputState", &d->inputStateFactory_);
1422 std::unordered_set<std::string> enabled;
1423 std::unordered_set<std::string> disabled;
1424 std::tie(enabled, disabled) = d->overrideAddons();
1425 FCITX_INFO() <<
"Override Enabled Addons: " << enabled;
1426 FCITX_INFO() <<
"Override Disabled Addons: " << disabled;
1427 d->addonManager_.load(enabled, disabled);
1432 d->uiManager_.load(d->arg_.uiName);
1434 const auto *entry = d->imManager_.entry(
"keyboard-us");
1435 FCITX_LOG_IF(Error, !entry) <<
"Couldn't find keyboard-us";
1436 d->preloadInputMethodEvent_ = d->eventLoop_.addTimeEvent(
1437 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
1440 if (d->exit_ || !d->globalConfig_.preloadInputMethod()) {
1444 if (!d->imManager_.currentGroup().inputMethodList().empty()) {
1445 if (
const auto *entry =
1446 d->imManager_.entry(d->imManager_.currentGroup()
1447 .inputMethodList()[0]
1449 d->addonManager_.addon(entry->addon(),
true);
1453 if (!d->imManager_.currentGroup().defaultInputMethod().empty()) {
1454 if (
const auto *entry = d->imManager_.entry(
1455 d->imManager_.currentGroup().defaultInputMethod())) {
1456 d->addonManager_.addon(entry->addon(),
true);
1462 d->zombieReaper_ = d->eventLoop_.addTimeEvent(
1463 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), 0,
1466 while ((res = waitpid(-1,
nullptr, WNOHANG)) > 0) {
1470 d->zombieReaper_->setEnabled(
false);
1473 d->exitEvent_ = d->eventLoop_.addExitEvent([
this](
EventSource *) {
1474 FCITX_DEBUG() <<
"Running save...";
1478 d->notifications_ = d->addonManager_.addon(
"notifications",
true);
1483 if (d->arg_.quietQuit) {
1490 return d->exitCode_;
1493 auto r = eventLoop().exec();
1494 d->running_ =
false;
1496 return r ? d->exitCode_ : 1;
1501 d->running_ = running;
1511 return d->inputMethodMode_;
1516 if (d->inputMethodMode_ == mode) {
1519 d->inputMethodMode_ = mode;
1528 bool Instance::virtualKeyboardAutoShow()
const {
1530 return d->virtualKeyboardAutoShow_;
1533 void Instance::setVirtualKeyboardAutoShow(
bool autoShow) {
1535 d->virtualKeyboardAutoShow_ = autoShow;
1538 bool Instance::virtualKeyboardAutoHide()
const {
1540 return d->virtualKeyboardAutoHide_;
1543 void Instance::setVirtualKeyboardAutoHide(
bool autoHide) {
1545 d->virtualKeyboardAutoHide_ = autoHide;
1548 VirtualKeyboardFunctionMode Instance::virtualKeyboardFunctionMode()
const {
1550 return d->virtualKeyboardFunctionMode_;
1553 void Instance::setVirtualKeyboardFunctionMode(
1554 VirtualKeyboardFunctionMode mode) {
1556 d->virtualKeyboardFunctionMode_ = mode;
1561 d->binaryMode_ =
true;
1566 const auto &addonNames = d->addonManager_.loadedAddonNames();
1567 return d->binaryMode_ &&
1568 std::all_of(addonNames.begin(), addonNames.end(),
1569 [d](
const std::string &name) {
1570 auto *addon = d->addonManager_.lookupAddon(name);
1574 return addon->canRestart();
1578 InstancePrivate *Instance::privateData() {
1585 return d->eventLoop_;
1590 return d->eventDispatcher_;
1595 return d->icManager_;
1600 return d->addonManager_;
1605 return d->imManager_;
1610 return d->imManager_;
1615 return d->uiManager_;
1620 return d->globalConfig_;
1623 bool Instance::postEvent(
Event &event) {
1624 return std::as_const(*this).postEvent(event);
1627 bool Instance::postEvent(
Event &event)
const {
1632 auto iter = d->eventHandlers_.find(event.
type());
1633 if (iter != d->eventHandlers_.end()) {
1634 const auto &handlers = iter->second;
1635 EventWatcherPhase phaseOrder[] = {
1636 EventWatcherPhase::ReservedFirst, EventWatcherPhase::PreInputMethod,
1637 EventWatcherPhase::InputMethod, EventWatcherPhase::PostInputMethod,
1638 EventWatcherPhase::ReservedLast};
1640 for (
auto phase : phaseOrder) {
1641 if (
auto iter2 = handlers.find(phase); iter2 != handlers.end()) {
1642 for (
auto &handler : iter2->second.view()) {
1657 auto &keyEvent =
static_cast<KeyEvent &
>(event);
1658 auto *ic = keyEvent.inputContext();
1659 #ifdef ENABLE_KEYBOARD 1661 if (!keyEvent.forward() && !keyEvent.origKey().code()) {
1664 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
1665 auto *xkbState = inputState->customXkbState();
1672 xkb_state_update_key(xkbState, keyEvent.origKey().code(),
1673 keyEvent.isRelease() ? XKB_KEY_UP
1677 if (ic->capabilityFlags().test(CapabilityFlag::KeyEventOrderFix) &&
1678 !keyEvent.accepted() && ic->hasPendingEventsStrictOrder()) {
1681 keyEvent.filterAndAccept();
1682 ic->forwardKey(keyEvent.origKey(), keyEvent.isRelease(),
1685 d_ptr->uiManager_.flush();
1688 return event.accepted();
1691 std::unique_ptr<HandlerTableEntry<EventHandler>>
1693 EventHandler callback) {
1695 if (phase == EventWatcherPhase::ReservedFirst ||
1696 phase == EventWatcherPhase::ReservedLast) {
1697 throw std::invalid_argument(
"Reserved Phase is only for internal use");
1699 return d->watchEvent(type, phase, std::move(callback));
1702 bool groupContains(
const InputMethodGroup &group,
const std::string &name) {
1703 const auto &list = group.inputMethodList();
1704 auto iter = std::find_if(list.begin(), list.end(),
1706 return item.name() == name;
1708 return iter != list.end();
1713 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
1716 if (!inputState->overrideDeactivateIM_.empty()) {
1717 return inputState->overrideDeactivateIM_;
1720 const auto &group = d->imManager_.currentGroup();
1723 !d->globalConfig_.allowInputMethodForPassword())) {
1724 auto defaultLayoutIM =
1725 stringutils::concat(
"keyboard-", group.defaultLayout());
1726 const auto *entry = d->imManager_.entry(defaultLayoutIM);
1728 entry = d->imManager_.entry(
"keyboard-us");
1730 return entry ? entry->uniqueName() :
"";
1733 if (group.inputMethodList().empty()) {
1736 if (inputState->isActive()) {
1737 if (!inputState->localIM_.empty() &&
1738 groupContains(group, inputState->localIM_)) {
1739 return inputState->localIM_;
1741 return group.defaultInputMethod();
1744 return group.inputMethodList()[0].name();
1749 auto imName = inputMethod(ic);
1750 if (imName.empty()) {
1753 return d->imManager_.entry(imName);
1758 const auto *entry = inputMethodEntry(ic);
1763 d->addonManager_.addon(entry->addon(),
true));
1768 const auto *entry = d->imManager_.entry(name);
1773 d->addonManager_.addon(entry->addon(),
true));
1778 const auto *entry = inputMethodEntry(ic);
1780 auto *engine = inputMethodEngine(ic);
1782 icon = engine->subModeIcon(*entry, *ic);
1785 icon = entry->icon();
1788 icon =
"input-keyboard";
1796 const auto *entry = inputMethodEntry(ic);
1797 auto *engine = inputMethodEngine(ic);
1799 if (engine && entry) {
1800 label = engine->subModeLabel(*entry, *ic);
1802 if (label.empty() && entry) {
1803 label = entry->label();
1809 #ifdef ENABLE_KEYBOARD 1811 auto *state = ic->
propertyFor(&d->inputStateFactory_);
1813 auto *xkbComposeState = state->xkbComposeState();
1814 if (!xkbComposeState) {
1818 auto keyval =
static_cast<xkb_keysym_t
>(keysym);
1820 enum xkb_compose_feed_result result =
1821 xkb_compose_state_feed(xkbComposeState, keyval);
1822 if (result == XKB_COMPOSE_FEED_IGNORED) {
1826 enum xkb_compose_status status =
1827 xkb_compose_state_get_status(xkbComposeState);
1828 if (status == XKB_COMPOSE_NOTHING) {
1831 if (status == XKB_COMPOSE_COMPOSED) {
1832 char buffer[FCITX_UTF8_MAX_LENGTH + 1] = {
'\0',
'\0',
'\0',
'\0',
1835 xkb_compose_state_get_utf8(xkbComposeState, buffer,
sizeof(buffer));
1836 xkb_compose_state_reset(xkbComposeState);
1838 return FCITX_INVALID_COMPOSE_RESULT;
1844 if (status == XKB_COMPOSE_CANCELLED) {
1845 xkb_compose_state_reset(xkbComposeState);
1848 return FCITX_INVALID_COMPOSE_RESULT;
1851 FCITX_UNUSED(keysym);
1858 #ifdef ENABLE_KEYBOARD 1860 auto *state = ic->
propertyFor(&d->inputStateFactory_);
1862 auto *xkbComposeState = state->xkbComposeState();
1863 if (!xkbComposeState) {
1864 return std::string();
1867 auto keyval =
static_cast<xkb_keysym_t
>(keysym);
1868 enum xkb_compose_feed_result result =
1869 xkb_compose_state_feed(xkbComposeState, keyval);
1871 if (result == XKB_COMPOSE_FEED_IGNORED) {
1872 return std::string();
1875 enum xkb_compose_status status =
1876 xkb_compose_state_get_status(xkbComposeState);
1877 if (status == XKB_COMPOSE_NOTHING) {
1878 return std::string();
1880 if (status == XKB_COMPOSE_COMPOSED) {
1882 std::array<char, 256> buffer;
1883 auto length = xkb_compose_state_get_utf8(xkbComposeState, buffer.data(),
1885 xkb_compose_state_reset(xkbComposeState);
1887 return std::nullopt;
1890 auto bufferBegin = buffer.begin();
1891 auto bufferEnd = std::next(bufferBegin,
length);
1893 return std::string(bufferBegin, bufferEnd);
1895 return std::nullopt;
1897 if (status == XKB_COMPOSE_CANCELLED) {
1898 xkb_compose_state_reset(xkbComposeState);
1900 return std::nullopt;
1903 FCITX_UNUSED(keysym);
1904 return std::string();
1909 #ifdef ENABLE_KEYBOARD 1911 auto *state = inputContext->
propertyFor(&d->inputStateFactory_);
1913 auto *xkbComposeState = state->xkbComposeState();
1914 if (!xkbComposeState) {
1918 return xkb_compose_state_get_status(xkbComposeState) ==
1919 XKB_COMPOSE_COMPOSING;
1921 FCITX_UNUSED(inputContext);
1927 #ifdef ENABLE_KEYBOARD 1929 auto *state = inputContext->
propertyFor(&d->inputStateFactory_);
1930 auto *xkbComposeState = state->xkbComposeState();
1931 if (!xkbComposeState) {
1934 xkb_compose_state_reset(xkbComposeState);
1936 FCITX_UNUSED(inputContext);
1943 d->idleStartTimestamp_ = now(CLOCK_MONOTONIC);
1944 d->imManager_.save();
1945 d->addonManager_.saveAll();
1950 if (
auto *ic = mostRecentInputContext()) {
1951 CheckInputMethodChanged imChangedRAII(ic, d);
1958 if (
const auto *entry = inputMethodManager().entry(imName)) {
1959 return entry->uniqueName();
1969 void Instance::configureAddon(
const std::string & ) {}
1971 void Instance::configureInputMethod(
const std::string & ) {}
1974 if (
auto *ic = mostRecentInputContext()) {
1975 if (
const auto *entry = inputMethodEntry(ic)) {
1976 return entry->uniqueName();
1984 return d->uiManager_.currentUI();
1989 if (
auto *ic = mostRecentInputContext()) {
1990 CheckInputMethodChanged imChangedRAII(ic, d);
2000 d->exitCode_ = exitCode;
2002 d->eventLoop_.exit();
2007 auto *addon = addonManager().addon(addonName);
2009 addon->reloadConfig();
2015 auto [enabled, disabled] = d->overrideAddons();
2016 d->addonManager_.load(enabled, disabled);
2017 d->imManager_.refresh();
2022 readAsIni(d->globalConfig_.config(), StandardPathsType::PkgConfig,
2024 FCITX_DEBUG() <<
"Trigger Key: " 2026 d->icManager_.setPropertyPropagatePolicy(
2027 d->globalConfig_.shareInputState());
2028 if (d->globalConfig_.preeditEnabledByDefault() !=
2029 d->icManager_.isPreeditEnabledByDefault()) {
2030 d->icManager_.setPreeditEnabledByDefault(
2031 d->globalConfig_.preeditEnabledByDefault());
2037 #ifdef ENABLE_KEYBOARD 2038 d->keymapCache_.clear();
2039 if (d->inputStateFactory_.registered()) {
2041 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2042 inputState->resetXkbState();
2051 if (d->globalConfig_.autoSavePeriod() <= 0) {
2052 d->periodicalSave_->setEnabled(
false);
2054 d->periodicalSave_->setNextInterval(AutoSaveMinInUsecs *
2055 d->globalConfig_.autoSavePeriod());
2056 d->periodicalSave_->setOneShot();
2067 if (!canRestart()) {
2075 setCurrentInputMethod(mostRecentInputContext(), name,
false);
2081 if (!canTrigger()) {
2085 auto &imManager = inputMethodManager();
2086 const auto &imList = imManager.currentGroup().inputMethodList();
2087 auto iter = std::find_if(imList.begin(), imList.end(),
2089 return item.name() == name;
2091 if (iter == imList.end()) {
2095 auto setGlobalDefaultInputMethod = [d](
const std::string &name) {
2096 std::vector<std::unique_ptr<CheckInputMethodChanged>> groupRAIICheck;
2097 d->icManager_.foreachFocused([d, &groupRAIICheck](
InputContext *ic) {
2099 groupRAIICheck.push_back(
2100 std::make_unique<CheckInputMethodChanged>(ic, d));
2103 d->imManager_.setDefaultInputMethod(name);
2106 auto idx = std::distance(imList.begin(), iter);
2108 CheckInputMethodChanged imChangedRAII(ic, d);
2109 auto currentIM = inputMethod(ic);
2110 if (currentIM == name) {
2113 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2117 inputState->setLocalIM(name);
2119 inputState->setLocalIM({});
2121 setGlobalDefaultInputMethod(name);
2123 inputState->setActive(
true);
2125 inputState->setActive(
false);
2127 if (inputState->imChanged_) {
2137 setGlobalDefaultInputMethod(name);
2145 if (
auto *ic = mostRecentInputContext()) {
2146 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2147 return inputState->isActive() ? 2 : 1;
2154 if (
auto *ic = mostRecentInputContext()) {
2155 CheckInputMethodChanged imChangedRAII(ic, d);
2162 if (
auto *ic = mostRecentInputContext()) {
2163 CheckInputMethodChanged imChangedRAII(ic, d);
2164 enumerate(ic, forward);
2168 bool Instance::canTrigger()
const {
2169 const auto &imManager = inputMethodManager();
2170 return (imManager.currentGroup().inputMethodList().size() > 1);
2174 if (!canTrigger()) {
2178 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2179 if (inputState->isActive()) {
2182 return inputState->lastIMChangeIsAltTrigger_;
2187 if (!canTrigger()) {
2191 if (d->globalConfig_.enumerateSkipFirst()) {
2192 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2193 if (!inputState->isActive()) {
2196 return d->imManager_.currentGroup().inputMethodList().size() > 2;
2202 bool Instance::canChangeGroup()
const {
2203 const auto &imManager = inputMethodManager();
2204 return (imManager.groupCount() > 1);
2209 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2210 if (!canTrigger()) {
2213 inputState->setActive(!inputState->isActive());
2214 if (inputState->imChanged_) {
2215 inputState->imChanged_->setReason(reason);
2220 bool Instance::trigger(
InputContext *ic,
bool totallyReleased) {
2222 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2223 if (!canTrigger()) {
2228 if (totallyReleased) {
2230 inputState->firstTrigger_ =
true;
2232 if (!d->globalConfig_.enumerateWithTriggerKeys() ||
2233 (inputState->firstTrigger_ && inputState->isActive()) ||
2234 (d->globalConfig_.enumerateSkipFirst() &&
2235 d->imManager_.currentGroup().inputMethodList().size() <= 2)) {
2238 enumerate(ic,
true);
2240 inputState->firstTrigger_ =
false;
2246 if (!canAltTrigger(ic)) {
2256 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2257 if (!canTrigger()) {
2260 if (inputState->isActive()) {
2263 inputState->setActive(
true);
2264 if (inputState->imChanged_) {
2272 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2273 if (!canTrigger()) {
2276 if (!inputState->isActive()) {
2279 inputState->setActive(
false);
2280 if (inputState->imChanged_) {
2281 inputState->imChanged_->setReason(
2289 auto &imManager = inputMethodManager();
2290 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2291 const auto &imList = imManager.currentGroup().inputMethodList();
2292 if (!canTrigger()) {
2296 if (d->globalConfig_.enumerateSkipFirst() && imList.size() <= 2) {
2300 auto currentIM = inputMethod(ic);
2302 auto iter = std::ranges::find_if(
2304 return item.name() == currentIM;
2306 if (iter == imList.end()) {
2309 int idx = std::distance(imList.begin(), iter);
2310 auto nextIdx = [forward, &imList](
int idx) {
2312 return (idx + (forward ? 1 : (imList.size() - 1))) % imList.size();
2316 if (d->globalConfig_.enumerateSkipFirst() && idx == 0) {
2320 std::vector<std::unique_ptr<CheckInputMethodChanged>> groupRAIICheck;
2321 d->icManager_.foreachFocused([d, &groupRAIICheck](
InputContext *ic) {
2323 groupRAIICheck.push_back(
2324 std::make_unique<CheckInputMethodChanged>(ic, d));
2327 imManager.setDefaultInputMethod(imList[idx].name());
2328 inputState->setActive(
true);
2329 inputState->setLocalIM({});
2331 inputState->setActive(
false);
2333 if (inputState->imChanged_) {
2341 const std::string &orig) {
2342 std::string result = orig;
2343 emit<Instance::CommitFilter>(inputContext, result);
2349 emit<Instance::OutputFilter>(inputContext, result);
2350 if ((&orig == &inputContext->inputPanel().clientPreedit() ||
2351 &orig == &inputContext->inputPanel().preedit()) &&
2352 !globalConfig().showPreeditForPassword() &&
2353 inputContext->capabilityFlags().test(CapabilityFlag::Password)) {
2355 for (
int i = 0, e = result.size(); i < e; i++) {
2360 dot +=
"\xe2\x80\xa2";
2363 newText.append(std::move(dot),
2364 result.formatAt(i) | TextFormatFlag::DontCommit);
2366 result = std::move(newText);
2373 return d->icManager_.lastFocusedInputContext();
2378 return d->icManager_.mostRecentInputContext();
2383 d->uiManager_.flush();
2386 int scoreForGroup(
FocusGroup *group,
const std::string &displayHint) {
2388 if (displayHint.empty()) {
2389 if (group->display() ==
"x11:") {
2395 if (group->display() ==
"wayland:") {
2402 if (group->display() == displayHint) {
2417 d->icManager_.foreachGroup(
2418 [&score, &displayHint, &defaultFocusGroup](
FocusGroup *group) {
2419 auto newScore = scoreForGroup(group, displayHint);
2420 if (newScore > score) {
2421 defaultFocusGroup = group;
2427 return defaultFocusGroup;
2432 FCITX_DEBUG() <<
"Instance::activateInputMethod";
2434 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2435 const auto *entry = inputMethodEntry(ic);
2437 FCITX_DEBUG() <<
"Activate: " 2438 <<
"[Last]:" << inputState->lastIM_
2439 <<
" [Activating]:" << entry->uniqueName();
2440 assert(inputState->lastIM_.empty());
2441 inputState->lastIM_ = entry->uniqueName();
2443 auto *engine = inputMethodEngine(ic);
2444 if (!engine || !entry) {
2447 #ifdef ENABLE_KEYBOARD 2448 if (
auto *xkbState = inputState->customXkbState(
true)) {
2449 if (
auto *mods = findValue(d->stateMask_, ic->
display())) {
2450 FCITX_KEYTRACE() <<
"Update mask to customXkbState";
2451 auto depressed = std::get<0>(*mods);
2452 auto latched = std::get<1>(*mods);
2453 auto locked = std::get<2>(*mods);
2458 FCITX_KEYTRACE() << depressed <<
" " << latched <<
" " << locked;
2459 if (depressed == 0) {
2460 inputState->setModsAllReleased();
2462 xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0,
2468 engine->activate(*entry, event);
2474 FCITX_DEBUG() <<
"Instance::deactivateInputMethod event_type=" 2475 <<
static_cast<uint32_t
>(
event.type());
2477 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2484 FCITX_DEBUG() <<
"Switch reason: " 2485 <<
static_cast<int>(icEvent.reason());
2486 FCITX_DEBUG() <<
"Old Input method: " << icEvent.oldInputMethod();
2487 entry = d->imManager_.entry(icEvent.oldInputMethod());
2489 entry = inputMethodEntry(ic);
2492 FCITX_DEBUG() <<
"Deactivate: " 2493 <<
"[Last]:" << inputState->lastIM_
2494 <<
" [Deactivating]:" << entry->uniqueName();
2495 assert(entry->uniqueName() == inputState->lastIM_);
2497 d->addonManager_.addon(entry->addon()));
2499 inputState->lastIM_.clear();
2500 if (!engine || !entry) {
2503 inputState->overrideDeactivateIM_ = entry->uniqueName();
2505 inputState->overrideDeactivateIM_.clear();
2509 bool Instance::enumerateGroup(
bool forward) {
2510 auto &imManager = inputMethodManager();
2511 auto groups = imManager.groups();
2512 if (groups.size() <= 1) {
2516 imManager.setCurrentGroup(groups[1]);
2518 imManager.setCurrentGroup(groups.back());
2524 FCITX_DEBUG() <<
"Input method switched";
2526 if (!d->globalConfig_.showInputMethodInformation()) {
2529 d->showInputMethodInformation(ic);
2533 const std::string &message) {
2534 FCITX_DEBUG() <<
"Input method switched";
2536 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2537 inputState->showInputMethodInformation(message);
2542 return (isInFlatpak() &&
2543 std::filesystem::is_regular_file(
"/app/.updated")) ||
2544 d->addonManager_.checkUpdate() || d->imManager_.checkUpdate() ||
2549 const std::string &rule,
2550 const std::string &model,
2551 const std::string &options) {
2552 #ifdef ENABLE_KEYBOARD 2554 bool resetState =
false;
2555 if (
auto *param = findValue(d->xkbParams_, display)) {
2556 if (std::get<0>(*param) != rule || std::get<1>(*param) != model ||
2557 std::get<2>(*param) != options) {
2558 std::get<0>(*param) = rule;
2559 std::get<1>(*param) = model;
2560 std::get<2>(*param) = options;
2564 d->xkbParams_.emplace(display, std::make_tuple(rule, model, options));
2568 d->keymapCache_[display].clear();
2569 d->icManager_.foreach([d, &display](
InputContext *ic) {
2570 if (ic->
display() == display ||
2571 !d->xkbParams_.contains(ic->
display())) {
2572 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2573 inputState->resetXkbState();
2579 FCITX_UNUSED(display);
2581 FCITX_UNUSED(model);
2582 FCITX_UNUSED(options);
2587 uint32_t depressed_mods,
2588 uint32_t latched_mods, uint32_t locked_mods) {
2590 d->stateMask_[display] =
2591 std::make_tuple(depressed_mods, latched_mods, locked_mods);
2596 d->stateMask_.erase(display);
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.
void restart()
Restart fcitx instance, this should only be used within a regular Fcitx server, not within embedded m...
void reloadAddonConfig(const std::string &addonName)
Reload certain addon config.
CapabilityFlags capabilityFlags() const
Returns the current capability flags.
FCITXCORE_DEPRECATED uint32_t processCompose(InputContext *ic, KeySym keysym)
Handle current XCompose state.
std::string inputMethodIcon(InputContext *ic)
Return the input method icon for input context.
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.
void activate()
Activate last focused input context. (Switch to the active input method)
EventType
Type of input method events.
Switched by alternative trigger key.
void resetCompose(InputContext *inputContext)
Reset the compose state.
T::PropertyType * propertyFor(const T *factory)
Helper function to return the input context property in specific type by given factory.
std::string UCS4ToUTF8(uint32_t code)
Convert UCS4 to UTF8 string.
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.
static uint32_t keySymToUnicode(KeySym sym)
Convert keysym to a unicode.
Switched by activate key.
void flushUI()
All user interface update is batched internally.
This is generated when input method group changed.
std::string commitFilter(InputContext *inputContext, const std::string &orig)
Update the commit string to frontend.
AddonManager & addonManager()
Get the addon manager.
std::string currentInputMethod()
Return the current input method of last focused input context.
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.
bool validate(Iter start, Iter end)
Check if the string iterator range is valid utf8 string.
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.
void deactivate()
Deactivate last focused input context.
int state()
Return a fcitx5-remote compatible value for the state.
InputMethodEngine * inputMethodEngine(InputContext *ic)
Return the input method engine object for given input context.
void setSignalPipe(int fd)
Set the pipe forwarding unix signal information.
InputMethodMode inputMethodMode() const
The current global input method mode.
void initialize()
Initialize fcitx.
EventLoop & eventLoop()
Get the fcitx event loop.
std::string currentUI()
Return the name of current user interface addon.
bool exitWhenMainDisplayDisconnected() const
Check whether command line specify whether to keep fcitx running.
C++ Utility functions for handling utf8 strings.
InputContext * mostRecentInputContext()
Return the most recent focused input context.
FCITX_NODISCARD std::unique_ptr< HandlerTableEntry< EventHandler > > watchEvent(EventType type, EventWatcherPhase phase, EventHandler callback)
Add a callback to for certain event type.
InputContextManager & inputContextManager()
Get the input context manager.
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.
void clearGroup(StatusGroup group)
Clear only given status group.
bool hasFocus() const
Returns whether the input context holds the input focus.
bool isRunning() const
Whether event loop is started and still running.
A class represents a formatted string.
bool willTryReplace() const
Check whether command line specify if it will replace an existing fcitx server.
Switched by group change.
bool isRestartRequested() const
Whether restart is requested.
Manager class for user interface.
Instance(int argc, char *argv[])
A main function like construct to be used to create Fcitx Instance.
Base class for fcitx event.
bool isValidChar(uint32_t c)
Check the chr value is not two invalid value above.
std::string inputMethodLabel(InputContext *ic)
Return the input method label for input context.
void save()
Save everything including input method profile and addon data.
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.
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.
Enum flag for text formatting.
void configure()
Launch configtool.
bool checkUpdate() const
Check if need to invoke Instance::refresh.
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.
InvokeAction event is generated when client click on the preedit.
InputMethodManager & inputMethodManager()
Get the input method manager.
void reloadConfig()
Reload global config.
int exec()
Start the event loop of Fcitx.
C-style utf8 utility functions.
void setBinaryMode()
Set if this instance is running as fcitx5 binary.
bool isModifier() const
Check if the key is a modifier press.
InputContext * lastFocusedInputContext()
Return a focused input context.
std::string display() const
Returns the display server of the client.
void toggle()
Toggle between the first input method and active input method.
void setCurrentInputMethod(const std::string &imName)
Set the input method of last focused input context.
void setRunning(bool running)
Let other know that event loop is already running.
bool canRestart() const
Check if fcitx 5 can safely restart by itself.
void showCustomInputMethodInformation(InputContext *ic, const std::string &message)
Show a small popup with input popup window with current input method information. ...
std::optional< std::string > processComposeString(InputContext *ic, KeySym keysym)
Handle current XCompose state.
void refresh()
Load newly installed input methods and addons.
Input method mode changed.
std::string inputMethod(InputContext *ic)
Return the unique name of input method for given input context.
UserInterfaceManager & userInterfaceManager()
Get the user interface manager.
void enumerate(bool forward)
Enumerate input method with in current group.
bool exiting() const
Check whether fcitx is in exiting process.
bool startsWith(std::string_view str, std::string_view prefix)
Check if a string starts with a prefix.
void showInputMethodInformation(InputContext *ic)
Show a small popup with input popup window with current input method information. ...
StatusArea & statusArea()
Returns the associated StatusArea.
EventType type() const
Type of event, can be used to decide event class.
bool empty() const
Whether input panel is totally empty.
void setInputMethodMode(InputMethodMode mode)
Set the current global input method mode.
ssize_t safeRead(int fd, void *data, size_t maxlen)
a simple wrapper around read(), ignore EINTR.
std::string addonForInputMethod(const std::string &imName)
Return the addon name of given input method.
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.
GlobalConfig & globalConfig()
Get the global config.
void exit()
Exit the fcitx event loop.
EventDispatcher & eventDispatcher()
Return a shared event dispatcher that is already attached to instance's event loop.
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.
Class to represent a key.
bool isComposing(InputContext *inputContext)
Check whether input context is composing or not.
Switched by deactivate key.
static std::string keyListToString(const Container &container, KeyStringFormat format=KeyStringFormat::Portable)
Convert a key list to string.
Notify the global config is reloaded.
when using lost focus this might be variance case to case.
Switched by enumerate key.
static const char * version()
Return the version string of Fcitx.
Text outputFilter(InputContext *inputContext, const Text &orig)
Update the string that will be displayed in user interface.