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 const auto &locale = getCurrentLocale();
189 assert(!locale.empty());
190 xkbContext_.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
192 xkb_context_set_log_level(xkbContext_.get(), XKB_LOG_LEVEL_CRITICAL);
193 xkbComposeTable_.reset(xkb_compose_table_new_from_locale(
194 xkbContext_.get(), locale.data(), XKB_COMPOSE_COMPILE_NO_FLAGS));
195 if (!xkbComposeTable_) {
197 <<
"Trying to fallback to compose table for en_US.UTF-8";
198 xkbComposeTable_.reset(xkb_compose_table_new_from_locale(
199 xkbContext_.get(),
"en_US.UTF-8",
200 XKB_COMPOSE_COMPILE_NO_FLAGS));
202 if (!xkbComposeTable_) {
204 <<
"No compose table is loaded, you may want to check your " 211 std::unique_ptr<HandlerTableEntry<EventHandler>>
212 InstancePrivate::watchEvent(
EventType type, EventWatcherPhase phase,
213 EventHandler callback) {
214 return eventHandlers_[type][phase].add(std::move(callback));
217 #ifdef ENABLE_KEYBOARD 218 xkb_keymap *InstancePrivate::keymap(
const std::string &display,
219 const std::string &layout,
220 const std::string &variant) {
221 auto layoutAndVariant = stringutils::concat(layout,
"-", variant);
222 if (
auto *keymapPtr = findValue(keymapCache_[display], layoutAndVariant)) {
223 return (*keymapPtr).get();
225 struct xkb_rule_names names;
226 names.layout = layout.c_str();
227 names.variant = variant.c_str();
228 std::tuple<std::string, std::string, std::string> xkbParam;
229 if (
auto *param = findValue(xkbParams_, display)) {
232 if (!xkbParams_.empty()) {
233 xkbParam = xkbParams_.begin()->second;
235 xkbParam = std::make_tuple(DEFAULT_XKB_RULES,
"pc101",
"");
238 if (globalConfig_.overrideXkbOption()) {
239 std::get<2>(xkbParam) = globalConfig_.customXkbOption();
241 names.rules = std::get<0>(xkbParam).c_str();
242 names.model = std::get<1>(xkbParam).c_str();
243 names.options = std::get<2>(xkbParam).c_str();
244 UniqueCPtr<xkb_keymap, xkb_keymap_unref> keymap(xkb_keymap_new_from_names(
245 xkbContext_.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS));
247 keymapCache_[display].emplace(layoutAndVariant, std::move(keymap));
248 assert(result.second);
249 return result.first->second.get();
253 std::pair<std::unordered_set<std::string>, std::unordered_set<std::string>>
254 InstancePrivate::overrideAddons() {
255 std::unordered_set<std::string> enabled;
256 std::unordered_set<std::string> disabled;
257 for (
const auto &addon : globalConfig_.enabledAddons()) {
258 enabled.insert(addon);
260 for (
const auto &addon : globalConfig_.disabledAddons()) {
261 enabled.erase(addon);
262 disabled.insert(addon);
264 for (
const auto &addon : arg_.enableList) {
265 disabled.erase(addon);
266 enabled.insert(addon);
268 for (
const auto &addon : arg_.disableList) {
269 enabled.erase(addon);
270 disabled.insert(addon);
272 return {enabled, disabled};
275 void InstancePrivate::buildDefaultGroup() {
277 auto *defaultGroup = q_func()->defaultFocusGroup();
278 bool infoFound =
false;
280 std::string variants;
281 auto guessLayout = [
this, &layouts, &variants,
282 &infoFound](FocusGroup *focusGroup) {
288 auto *xcb = addonManager_.addon(
"xcb");
289 auto x11Name = focusGroup->display().substr(4);
291 auto rules = xcb->call<IXCBModule::xkbRulesNames>(x11Name);
292 if (!rules[2].
empty()) {
301 FCITX_UNUSED(layouts);
302 FCITX_UNUSED(variants);
303 FCITX_UNUSED(infoFound);
307 if (!defaultGroup || guessLayout(defaultGroup)) {
308 icManager_.foreachGroup(
309 [defaultGroup, &guessLayout](FocusGroup *focusGroup) {
310 if (defaultGroup == focusGroup) {
313 return guessLayout(focusGroup);
322 constexpr
char imNamePrefix[] =
"keyboard-";
326 variants,
",", stringutils::SplitBehavior::KeepEmpty);
327 auto size = std::max(layoutTokens.size(), variantTokens.size());
329 layoutTokens.resize(size);
330 variantTokens.resize(size);
332 OrderedSet<std::string> imLayouts;
333 for (decltype(size) i = 0; i < size; i++) {
334 if (layoutTokens[i].
empty()) {
337 std::string layoutName = layoutTokens[i];
338 if (!variantTokens[i].
empty()) {
339 layoutName = stringutils::concat(layoutName,
"-", variantTokens[i]);
343 if (!imManager_.entry(stringutils::concat(imNamePrefix, layoutName))) {
347 imLayouts.pushBack(layoutName);
351 auto lang = stripLanguage(getCurrentLanguage());
352 DefaultInputMethod defaultIMConfig;
353 readAsIni(defaultIMConfig, StandardPathsType::PkgData,
354 std::filesystem::path(
"default") / lang);
357 for (
const auto &extraLayout : defaultIMConfig.extraLayouts.value()) {
358 if (!imManager_.entry(stringutils::concat(imNamePrefix, extraLayout))) {
361 imLayouts.pushBack(extraLayout);
365 if (imLayouts.empty()) {
366 imLayouts.pushBack(
"us");
370 std::string defaultIM;
371 for (
const auto &im : defaultIMConfig.defaultInputMethods.value()) {
372 if (imManager_.entry(im)) {
379 std::vector<std::string> groupOrders;
380 for (
const auto &imLayout : imLayouts) {
381 std::string groupName;
382 if (imLayouts.size() == 1) {
383 groupName = _(
"Default");
385 groupName = _(
"Group {}", imManager_.groupCount() + 1);
387 imManager_.addEmptyGroup(groupName);
388 groupOrders.push_back(groupName);
389 InputMethodGroup group(groupName);
390 group.inputMethodList().emplace_back(
391 InputMethodGroupItem(stringutils::concat(imNamePrefix, imLayout)));
392 if (!defaultIM.empty()) {
393 group.inputMethodList().emplace_back(
394 InputMethodGroupItem(defaultIM));
396 FCITX_INFO() <<
"Items in " << groupName <<
": " 397 << group.inputMethodList();
398 group.setDefaultLayout(imLayout);
399 imManager_.setGroup(std::move(group));
401 FCITX_INFO() <<
"Generated groups: " << groupOrders;
402 imManager_.setGroupOrder(groupOrders);
405 void InstancePrivate::showInputMethodInformation(InputContext *ic) {
407 auto *inputState = ic->propertyFor(&inputStateFactory_);
408 auto *engine = q->inputMethodEngine(ic);
409 const auto *entry = q->inputMethodEntry(ic);
410 auto &imManager = q->inputMethodManager();
412 if (!inputState->isActive() &&
413 !globalConfig_.showFirstInputMethodInformation()) {
419 auto subMode = engine->subMode(*entry, *ic);
420 auto subModeLabel = engine->subModeLabel(*entry, *ic);
421 auto name = globalConfig_.compactInputMethodInformation() &&
422 !entry->label().empty()
425 if (globalConfig_.compactInputMethodInformation() &&
426 !subModeLabel.empty()) {
427 display = std::move(subModeLabel);
428 }
else if (subMode.empty()) {
429 display = std::move(name);
431 display = _(
"{0} ({1})", name, subMode);
434 display = _(
"{0} (Not available)", entry->name());
436 display = _(
"(Not available)");
438 if (!globalConfig_.compactInputMethodInformation() &&
439 imManager.groupCount() > 1) {
440 display = _(
"Group {0}: {1}", imManager.currentGroup().name(), display);
442 inputState->showInputMethodInformation(display);
445 bool InstancePrivate::canActivate(InputContext *ic) {
447 if (!q->canTrigger()) {
450 auto *inputState = ic->propertyFor(&inputStateFactory_);
451 return !inputState->isActive();
454 bool InstancePrivate::canDeactivate(InputContext *ic) {
456 if (!q->canTrigger()) {
459 auto *inputState = ic->propertyFor(&inputStateFactory_);
460 return inputState->isActive();
463 void InstancePrivate::navigateGroup(InputContext *ic,
const Key &key,
465 auto *inputState = ic->propertyFor(&inputStateFactory_);
466 inputState->pendingGroupIndex_ =
467 (inputState->pendingGroupIndex_ +
468 (forward ? 1 : imManager_.groupCount() - 1)) %
469 imManager_.groupCount();
470 FCITX_DEBUG() <<
"Switch to group " << inputState->pendingGroupIndex_;
472 if (notifications_ && !isSingleKey(key)) {
473 notifications_->call<INotifications::showTip>(
474 "enumerate-group", _(
"Input Method"),
"input-keyboard",
476 _(
"Switch group to {0}",
477 imManager_.groups()[inputState->pendingGroupIndex_]),
482 void InstancePrivate::acceptGroupChange(
const Key &key, InputContext *ic) {
483 FCITX_DEBUG() <<
"Accept group change, isSingleKey: " << key;
485 auto *inputState = ic->propertyFor(&inputStateFactory_);
486 auto groups = imManager_.groups();
487 if (groups.size() > inputState->pendingGroupIndex_) {
488 if (isSingleKey(key)) {
489 FCITX_DEBUG() <<
"EnumerateGroupTo: " 490 << inputState->pendingGroupIndex_ <<
" " << key;
491 imManager_.enumerateGroupTo(groups[inputState->pendingGroupIndex_]);
493 FCITX_DEBUG() <<
"SetCurrentGroup: " 494 << inputState->pendingGroupIndex_ <<
" " << key;
495 imManager_.setCurrentGroup(groups[inputState->pendingGroupIndex_]);
498 inputState->pendingGroupIndex_ = 0;
501 InputState::InputState(InstancePrivate *d, InputContext *ic)
502 : d_ptr(d), ic_(ic) {
503 active_ = d->globalConfig_.activeByDefault();
504 #ifdef ENABLE_KEYBOARD 505 if (d->xkbComposeTable_) {
506 xkbComposeState_.reset(xkb_compose_state_new(
507 d->xkbComposeTable_.get(), XKB_COMPOSE_STATE_NO_FLAGS));
512 void InputState::showInputMethodInformation(
const std::string &name) {
513 ic_->inputPanel().setAuxUp(Text(name));
516 imInfoTimer_ = d_ptr->eventLoop_.addTimeEvent(
517 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
518 [
this](EventSourceTime *, uint64_t) {
519 hideInputMethodInfo();
524 #ifdef ENABLE_KEYBOARD 525 xkb_state *InputState::customXkbState(
bool refresh) {
526 auto *instance = d_ptr->q_func();
527 const InputMethodGroup &group = d_ptr->imManager_.currentGroup();
528 const auto im = instance->inputMethod(ic_);
529 auto layout = group.layoutFor(im);
531 layout = im.substr(9);
533 if (layout.empty() || layout == group.defaultLayout()) {
536 modsAllReleased_ =
false;
537 lastXkbLayout_.clear();
541 if (layout == lastXkbLayout_ && !refresh) {
542 return xkbState_.get();
545 lastXkbLayout_ = layout;
546 const auto layoutAndVariant = parseLayout(layout);
547 if (
auto *keymap = d_ptr->keymap(ic_->display(), layoutAndVariant.first,
548 layoutAndVariant.second)) {
549 xkbState_.reset(xkb_state_new(keymap));
553 modsAllReleased_ =
false;
554 return xkbState_.get();
558 void InputState::setActive(
bool active) {
559 if (active_ != active) {
561 ic_->updateProperty(&d_ptr->inputStateFactory_);
565 void InputState::setLocalIM(
const std::string &localIM) {
566 if (localIM_ != localIM) {
568 ic_->updateProperty(&d_ptr->inputStateFactory_);
572 void InputState::copyTo(InputContextProperty *other) {
573 auto *otherState =
static_cast<InputState *
>(other);
574 if (otherState->active_ == active_ && otherState->localIM_ == localIM_) {
578 if (otherState->ic_->hasFocus()) {
579 FCITX_DEBUG() <<
"Sync state to focused ic: " 580 << otherState->ic_->program();
581 CheckInputMethodChanged imChangedRAII(otherState->ic_, d_ptr);
582 otherState->active_ = active_;
583 otherState->localIM_ = localIM_;
585 otherState->active_ = active_;
586 otherState->localIM_ = localIM_;
590 void InputState::reset() {
591 #ifdef ENABLE_KEYBOARD 592 if (xkbComposeState_) {
593 xkb_compose_state_reset(xkbComposeState_.get());
596 pendingGroupIndex_ = 0;
598 lastKeyPressed_ = Key();
599 lastKeyPressedTime_ = 0;
600 totallyReleased_ =
true;
603 void InputState::hideInputMethodInfo() {
607 imInfoTimer_.reset();
608 auto &panel = ic_->inputPanel();
609 if (panel.auxDown().empty() && panel.preedit().empty() &&
610 panel.clientPreedit().empty() &&
611 (!panel.candidateList() || panel.candidateList()->empty()) &&
612 panel.auxUp().size() == 1 && panel.auxUp().stringAt(0) == lastInfo_) {
618 #ifdef ENABLE_KEYBOARD 619 void InputState::resetXkbState() {
620 lastXkbLayout_.clear();
625 CheckInputMethodChanged::CheckInputMethodChanged(InputContext *ic,
626 InstancePrivate *instance)
627 : instance_(instance->q_func()), instancePrivate_(instance),
628 ic_(ic->watch()), inputMethod_(instance_->inputMethod(ic)),
630 auto *inputState = ic->propertyFor(&instance->inputStateFactory_);
631 if (!inputState->imChanged_) {
632 inputState->imChanged_ =
this;
638 CheckInputMethodChanged::~CheckInputMethodChanged() {
639 if (!ic_.isValid()) {
642 auto *ic = ic_.get();
643 auto *inputState = ic->propertyFor(&instancePrivate_->inputStateFactory_);
644 inputState->imChanged_ =
nullptr;
645 if (inputMethod_ != instance_->inputMethod(ic) && !ignore_) {
646 instance_->postEvent(
647 InputContextSwitchInputMethodEvent(reason_, inputMethod_, ic));
652 InstanceArgument arg;
653 arg.parseOption(argc, argv);
658 if (arg.runAsDaemon) {
662 if (arg.overrideDelay > 0) {
663 sleep(arg.overrideDelay);
667 d_ptr = std::make_unique<InstancePrivate>(
this);
670 d->eventDispatcher_.attach(&d->eventLoop_);
671 d->addonManager_.setInstance(
this);
672 d->addonManager_.setAddonOptions(arg.addonOptions_);
673 d->icManager_.setInstance(
this);
674 d->connections_.emplace_back(
675 d->imManager_.connect<InputMethodManager::CurrentGroupAboutToChange>(
676 [
this, d](
const std::string &lastGroup) {
677 d->icManager_.foreachFocused([this](InputContext *ic) {
678 assert(ic->hasFocus());
679 InputContextSwitchInputMethodEvent event(
680 InputMethodSwitchedReason::GroupChange, inputMethod(ic),
682 deactivateInputMethod(event);
685 d->lastGroup_ = lastGroup;
688 d->connections_.emplace_back(
689 d->imManager_.connect<InputMethodManager::CurrentGroupChanged>(
690 [
this, d](
const std::string &newGroup) {
691 d->icManager_.foreachFocused([this](InputContext *ic) {
692 assert(ic->hasFocus());
693 InputContextSwitchInputMethodEvent event(
694 InputMethodSwitchedReason::GroupChange,
"", ic);
695 activateInputMethod(event);
699 if (!d->lastGroup_.empty() && !newGroup.empty() &&
700 d->lastGroup_ != newGroup && d->notifications_ &&
701 d->imManager_.groupCount() > 1) {
702 d->notifications_->call<INotifications::showTip>(
703 "enumerate-group", _(
"Input Method"),
"input-keyboard",
705 _(
"Switched group to {0}",
706 d->imManager_.currentGroup().name()),
709 d->lastGroup_ = newGroup;
712 d->eventWatchers_.emplace_back(d->watchEvent(
713 EventType::InputContextCapabilityAboutToChange,
714 EventWatcherPhase::ReservedFirst, [
this](
Event &event) {
716 static_cast<CapabilityAboutToChangeEvent &>(event);
717 if (!capChanged.inputContext()->hasFocus()) {
721 if (!shouldSwitchIM(capChanged.oldFlags(), capChanged.newFlags())) {
727 inputMethod(capChanged.inputContext()),
728 capChanged.inputContext());
729 deactivateInputMethod(switchIM);
731 d->eventWatchers_.emplace_back(d->watchEvent(
732 EventType::InputContextCapabilityChanged,
733 EventWatcherPhase::ReservedFirst, [
this](
Event &event) {
734 auto &capChanged = static_cast<CapabilityChangedEvent &>(event);
735 if (!capChanged.inputContext()->hasFocus()) {
739 if (!shouldSwitchIM(capChanged.oldFlags(), capChanged.newFlags())) {
745 capChanged.inputContext());
746 activateInputMethod(switchIM);
749 d->eventWatchers_.emplace_back(watchEvent(
751 [
this, d](
Event &event) {
752 auto &keyEvent =
static_cast<KeyEvent &
>(event);
753 auto *ic = keyEvent.inputContext();
754 CheckInputMethodChanged imChangedRAII(ic, d);
755 auto origKey = keyEvent.origKey().normalize();
759 std::function<bool()> check;
760 std::function<void(bool)> trigger;
762 {.list = d->globalConfig_.triggerKeys(),
763 .check = [
this]() {
return canTrigger(); },
765 [
this, ic](
bool totallyReleased) {
766 return trigger(ic, totallyReleased);
768 {.list = d->globalConfig_.altTriggerKeys(),
769 .check = [
this, ic]() {
return canAltTrigger(ic); },
770 .trigger = [
this, ic](bool) {
return altTrigger(ic); }},
771 {.list = d->globalConfig_.activateKeys(),
772 .check = [ic, d]() {
return d->canActivate(ic); },
773 .trigger = [
this, ic](bool) {
return activate(ic); }},
774 {.list = d->globalConfig_.deactivateKeys(),
775 .check = [ic, d]() {
return d->canDeactivate(ic); },
776 .trigger = [
this, ic](bool) {
return deactivate(ic); }},
777 {.list = d->globalConfig_.enumerateForwardKeys(),
778 .check = [
this, ic]() {
return canEnumerate(ic); },
779 .trigger = [
this, ic](bool) {
return enumerate(ic,
true); }},
780 {.list = d->globalConfig_.enumerateBackwardKeys(),
781 .check = [
this, ic]() {
return canEnumerate(ic); },
782 .trigger = [
this, ic](bool) {
return enumerate(ic,
false); }},
783 {.list = d->globalConfig_.enumerateGroupForwardKeys(),
784 .check = [
this]() {
return canChangeGroup(); },
785 .trigger = [ic, d, origKey](
786 bool) { d->navigateGroup(ic, origKey,
true); }},
787 {.list = d->globalConfig_.enumerateGroupBackwardKeys(),
788 .check = [
this]() {
return canChangeGroup(); },
790 [ic, d, origKey](bool) {
791 d->navigateGroup(ic, origKey,
false);
795 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
796 int keyReleased = inputState->keyReleased_;
797 Key lastKeyPressed = inputState->lastKeyPressed_;
799 inputState->keyReleased_ = -1;
801 if (keyEvent.isRelease()) {
803 for (
auto &keyHandler : keyHandlers) {
804 if (keyReleased == idx &&
805 origKey.isReleaseOfModifier(lastKeyPressed) &&
806 keyHandler.check()) {
808 if (d->globalConfig_.checkModifierOnlyKeyTimeout(
809 inputState->lastKeyPressedTime_)) {
811 inputState->totallyReleased_);
813 inputState->lastKeyPressedTime_ = 0;
814 if (origKey.hasModifier()) {
815 inputState->totallyReleased_ =
false;
823 if (isSingleModifier(origKey)) {
824 inputState->totallyReleased_ =
true;
828 if (inputState->pendingGroupIndex_ &&
829 inputState->totallyReleased_) {
830 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
831 if (inputState->imChanged_) {
832 inputState->imChanged_->ignore();
834 d->acceptGroupChange(lastKeyPressed, ic);
835 inputState->lastKeyPressed_ =
Key();
838 if (!keyEvent.filtered() && !keyEvent.isRelease()) {
840 for (
auto &keyHandler : keyHandlers) {
841 auto keyIdx = origKey.keyListIndex(keyHandler.list);
842 if (keyIdx >= 0 && keyHandler.check()) {
843 inputState->keyReleased_ = idx;
844 inputState->lastKeyPressed_ = origKey;
846 inputState->lastKeyPressedTime_ =
847 now(CLOCK_MONOTONIC);
853 keyHandler.trigger(inputState->totallyReleased_);
854 if (origKey.hasModifier()) {
855 inputState->totallyReleased_ =
false;
857 keyEvent.filterAndAccept();
864 d->eventWatchers_.emplace_back(watchEvent(
867 auto &keyEvent =
static_cast<KeyEvent &
>(event);
868 auto *ic = keyEvent.inputContext();
869 if (!keyEvent.isRelease() &&
870 keyEvent.key().checkKeyList(
871 d->globalConfig_.togglePreeditKeys())) {
874 ic->setEnablePreedit(!ic->isPreeditEnabled());
875 if (d->notifications_) {
876 d->notifications_->call<INotifications::showTip>(
877 "toggle-preedit", _(
"Input Method"),
"", _(
"Preedit"),
878 ic->isPreeditEnabled() ? _(
"Preedit enabled")
879 : _(
"Preedit disabled"),
882 keyEvent.filterAndAccept();
885 d->eventWatchers_.emplace_back(d->watchEvent(
889 d->idleStartTimestamp_ = now(CLOCK_MONOTONIC);
890 auto &keyEvent = static_cast<KeyEvent &>(event);
891 auto *ic = keyEvent.inputContext();
892 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
893 #ifdef ENABLE_KEYBOARD
894 auto *xkbState = inputState->customXkbState();
896 if (auto *mods = findValue(d->stateMask_, ic->display())) {
897 FCITX_KEYTRACE() <<
"Update mask to customXkbState";
901 if (inputState->isModsAllReleased()) {
902 depressed = xkb_state_serialize_mods(
903 xkbState, XKB_STATE_MODS_DEPRESSED);
905 depressed = std::get<0>(*mods);
907 if (std::get<0>(*mods) == 0) {
908 inputState->setModsAllReleased();
910 auto latched = xkb_state_serialize_mods(
911 xkbState, XKB_STATE_MODS_LATCHED);
912 auto locked = std::get<2>(*mods);
918 << depressed <<
" " << latched <<
" " << locked;
919 xkb_state_update_mask(xkbState, depressed, latched, locked,
922 const uint32_t effective = xkb_state_serialize_mods(
923 xkbState, XKB_STATE_MODS_EFFECTIVE);
924 auto newSym = xkb_state_key_get_one_sym(
925 xkbState, keyEvent.rawKey().code());
926 auto newModifier = KeyStates(effective);
927 auto *keymap = xkb_state_get_keymap(xkbState);
928 if (keyEvent.rawKey().states().test(KeyState::Repeat) &&
929 xkb_keymap_key_repeats(keymap, keyEvent.rawKey().code())) {
930 newModifier |= KeyState::Repeat;
933 const uint32_t modsDepressed = xkb_state_serialize_mods(
934 xkbState, XKB_STATE_MODS_DEPRESSED);
935 const uint32_t modsLatched =
936 xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED);
937 const uint32_t modsLocked =
938 xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED);
939 FCITX_KEYTRACE() <<
"Current mods: " << modsDepressed <<
" " 940 << modsLatched <<
" " << modsLocked;
941 auto newCode = keyEvent.rawKey().code();
942 Key key(static_cast<KeySym>(newSym), newModifier, newCode);
944 <<
"Custom Xkb translated Key: " << key.toString();
945 keyEvent.setRawKey(key);
948 FCITX_KEYTRACE() <<
"KeyEvent: " << keyEvent.key()
949 <<
" rawKey: " << keyEvent.rawKey()
950 <<
" origKey: " << keyEvent.origKey()
951 <<
" Release:" << keyEvent.isRelease()
952 <<
" keycode: " << keyEvent.origKey().code()
953 <<
" program: " << ic->program();
955 if (keyEvent.isRelease()) {
958 inputState->hideInputMethodInfo();
960 d->eventWatchers_.emplace_back(
962 EventWatcherPhase::InputMethod, [
this](
Event &event) {
963 auto &keyEvent =
static_cast<KeyEvent &
>(event);
964 auto *ic = keyEvent.inputContext();
965 auto *engine = inputMethodEngine(ic);
966 const auto *entry = inputMethodEntry(ic);
967 if (!engine || !entry) {
970 engine->keyEvent(*entry, keyEvent);
972 d->eventWatchers_.emplace_back(watchEvent(
973 EventType::InputContextVirtualKeyboardEvent,
974 EventWatcherPhase::InputMethod, [
this](
Event &event) {
976 auto *ic = keyEvent.inputContext();
977 auto *engine = inputMethodEngine(ic);
978 const auto *entry = inputMethodEntry(ic);
979 if (!engine || !entry) {
982 engine->virtualKeyboardEvent(*entry, keyEvent);
984 d->eventWatchers_.emplace_back(watchEvent(
986 [
this](
Event &event) {
988 auto *ic = invokeActionEvent.inputContext();
989 auto *engine = inputMethodEngine(ic);
990 const auto *entry = inputMethodEntry(ic);
991 if (!engine || !entry) {
994 engine->invokeAction(*entry, invokeActionEvent);
996 d->eventWatchers_.emplace_back(d->watchEvent(
998 [
this](
Event &event) {
999 auto &keyEvent = static_cast<KeyEvent &>(event);
1000 auto *ic = keyEvent.inputContext();
1001 auto *engine = inputMethodEngine(ic);
1002 const auto *entry = inputMethodEntry(ic);
1003 if (!engine || !entry) {
1006 engine->filterKey(*entry, keyEvent);
1007 emit<Instance::KeyEventResult>(keyEvent);
1008 #ifdef ENABLE_KEYBOARD 1009 if (keyEvent.forward()) {
1014 if (keyEvent.isRelease()) {
1018 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1019 if (
auto *xkbState = inputState->customXkbState()) {
1020 if (
auto utf32 = xkb_state_key_get_utf32(
1021 xkbState, keyEvent.key().code())) {
1023 if (utf32 ==
'\n' || utf32 ==
'\b' || utf32 ==
'\r' ||
1024 utf32 ==
'\t' || utf32 ==
'\033' ||
1028 if (keyEvent.key().states().test(KeyState::Ctrl) ||
1029 keyEvent.rawKey().sym() ==
1030 keyEvent.origKey().sym()) {
1033 FCITX_KEYTRACE() <<
"Will commit char: " << utf32;
1035 keyEvent.filterAndAccept();
1036 }
else if (!keyEvent.key().states().test(KeyState::Ctrl) &&
1037 keyEvent.rawKey().sym() !=
1038 keyEvent.origKey().sym() &&
1043 keyEvent.filterAndAccept();
1049 d->eventWatchers_.emplace_back(d->watchEvent(
1051 [
this, d](
Event &event) {
1052 auto &icEvent = static_cast<InputContextEvent &>(event);
1053 auto isSameProgram = [&icEvent, d]() {
1055 return (icEvent.inputContext() == d->lastUnFocusedIc_.get()) ||
1056 (!icEvent.inputContext()->program().empty() &&
1057 (icEvent.inputContext()->program() ==
1058 d->lastUnFocusedProgram_));
1061 if (d->globalConfig_.resetStateWhenFocusIn() ==
1062 PropertyPropagatePolicy::All ||
1063 (d->globalConfig_.resetStateWhenFocusIn() ==
1064 PropertyPropagatePolicy::Program &&
1065 !isSameProgram())) {
1066 if (d->globalConfig_.activeByDefault()) {
1067 activate(icEvent.inputContext());
1069 deactivate(icEvent.inputContext());
1073 activateInputMethod(icEvent);
1075 auto *inputContext = icEvent.inputContext();
1076 if (!inputContext->clientControlVirtualkeyboardShow()) {
1077 inputContext->showVirtualKeyboard();
1080 if (!d->globalConfig_.showInputMethodInformationWhenFocusIn() ||
1081 icEvent.inputContext()->capabilityFlags().test(
1087 d->focusInImInfoTimer_ = d->eventLoop_.addTimeEvent(
1088 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 30000, 0,
1092 if (
auto *ic = icRef.get(); ic && ic->hasFocus()) {
1093 d->showInputMethodInformation(ic);
1098 d->eventWatchers_.emplace_back(d->watchEvent(
1101 auto &icEvent = static_cast<InputContextEvent &>(event);
1102 auto *ic = icEvent.inputContext();
1103 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1104 inputState->reset();
1105 if (!ic->capabilityFlags().test(
1106 CapabilityFlag::ClientUnfocusCommit)) {
1109 ic->inputPanel().clientPreedit().toStringForCommit();
1110 if (!commit.empty()) {
1111 ic->commitString(commit);
1115 d->eventWatchers_.emplace_back(d->watchEvent(
1117 [
this, d](
Event &event) {
1118 auto &icEvent = static_cast<InputContextEvent &>(event);
1119 d->lastUnFocusedProgram_ = icEvent.inputContext()->program();
1120 d->lastUnFocusedIc_ = icEvent.inputContext()->watch();
1121 deactivateInputMethod(icEvent);
1123 auto *inputContext = icEvent.inputContext();
1124 if (!inputContext->clientControlVirtualkeyboardHide()) {
1125 inputContext->hideVirtualKeyboard();
1128 d->eventWatchers_.emplace_back(d->watchEvent(
1131 auto &icEvent = static_cast<InputContextEvent &>(event);
1132 auto *ic = icEvent.inputContext();
1133 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1134 inputState->reset();
1136 d->eventWatchers_.emplace_back(
1138 [
this](
Event &event) {
1140 auto *ic = icEvent.inputContext();
1141 if (!ic->hasFocus()) {
1144 auto *engine = inputMethodEngine(ic);
1145 const auto *entry = inputMethodEntry(ic);
1146 if (!engine || !entry) {
1149 engine->reset(*entry, icEvent);
1151 d->eventWatchers_.emplace_back(d->watchEvent(
1153 EventWatcherPhase::ReservedFirst, [
this](
Event &event) {
1155 static_cast<InputContextSwitchInputMethodEvent &>(event);
1156 auto *ic = icEvent.inputContext();
1157 if (!ic->hasFocus()) {
1160 deactivateInputMethod(icEvent);
1161 activateInputMethod(icEvent);
1163 d->eventWatchers_.emplace_back(d->watchEvent(
1165 EventWatcherPhase::ReservedLast, [
this, d](
Event &event) {
1167 static_cast<InputContextSwitchInputMethodEvent &>(event);
1168 auto *ic = icEvent.inputContext();
1169 if (!ic->hasFocus()) {
1173 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
1174 inputState->lastIMChangeIsAltTrigger_ =
1186 showInputMethodInformation(ic);
1188 d->eventWatchers_.emplace_back(
1190 EventWatcherPhase::ReservedLast, [
this, d](
Event &) {
1193 d->imGroupInfoTimer_ = d->eventLoop_.addTimeEvent(
1194 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 30000, 0,
1195 [this](EventSourceTime *, uint64_t) {
1196 inputContextManager().foreachFocused(
1197 [this](InputContext *ic) {
1198 showInputMethodInformation(ic);
1205 d->eventWatchers_.emplace_back(d->watchEvent(
1206 EventType::InputContextUpdateUI, EventWatcherPhase::ReservedFirst,
1208 auto &icEvent = static_cast<InputContextUpdateUIEvent &>(event);
1209 if (icEvent.immediate()) {
1210 d->uiManager_.update(icEvent.component(),
1211 icEvent.inputContext());
1212 d->uiManager_.flush();
1214 d->uiManager_.update(icEvent.component(),
1215 icEvent.inputContext());
1216 d->uiUpdateEvent_->setOneShot();
1219 d->eventWatchers_.emplace_back(d->watchEvent(
1220 EventType::InputContextDestroyed, EventWatcherPhase::ReservedFirst,
1222 auto &icEvent = static_cast<InputContextEvent &>(event);
1223 d->uiManager_.expire(icEvent.inputContext());
1225 d->eventWatchers_.emplace_back(d->watchEvent(
1227 [d](
Event &) { d->uiManager_.updateAvailability(); }));
1228 d->uiUpdateEvent_ = d->eventLoop_.addDeferEvent([d](
EventSource *) {
1229 d->uiManager_.flush();
1232 d->uiUpdateEvent_->setEnabled(
false);
1233 d->periodicalSave_ = d->eventLoop_.addTimeEvent(
1234 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, AutoSaveIdleTime,
1241 auto currentTime = now(CLOCK_MONOTONIC);
1242 if (currentTime <= d->idleStartTimestamp_ ||
1243 currentTime - d->idleStartTimestamp_ < AutoSaveIdleTime) {
1245 time->setNextInterval(2 * AutoSaveIdleTime);
1250 FCITX_INFO() <<
"Running autosave...";
1252 FCITX_INFO() <<
"End autosave";
1253 if (d->globalConfig_.autoSavePeriod() > 0) {
1254 time->setNextInterval(d->globalConfig_.autoSavePeriod() *
1255 AutoSaveMinInUsecs);
1260 d->periodicalSave_->setEnabled(
false);
1263 Instance::~Instance() {
1265 d->icManager_.finalize();
1266 d->addonManager_.unload();
1267 d->notifications_ =
nullptr;
1268 d->icManager_.setInstance(
nullptr);
1271 void InstanceArgument::parseOption(
int argc,
char **argv) {
1277 struct option longOptions[] = {{
"enable", required_argument,
nullptr, 0},
1278 {
"disable", required_argument,
nullptr, 0},
1279 {
"verbose", required_argument,
nullptr, 0},
1280 {
"keep", no_argument,
nullptr,
'k'},
1281 {
"ui", required_argument,
nullptr,
'u'},
1282 {
"replace", no_argument,
nullptr,
'r'},
1283 {
"version", no_argument,
nullptr,
'v'},
1284 {
"help", no_argument,
nullptr,
'h'},
1285 {
"option", required_argument,
nullptr,
'o'},
1286 {
nullptr, 0, 0, 0}};
1288 int optionIndex = 0;
1290 std::string addonOptionString;
1291 while ((c = getopt_long(argc, argv,
"ru:dDs:hvo:k", longOptions,
1292 &optionIndex)) != EOF) {
1295 switch (optionIndex) {
1303 Log::setLogRule(optarg);
1321 runAsDaemon =
false;
1324 exitWhenMainDisplayDisconnected =
false;
1327 overrideDelay = std::atoi(optarg);
1338 addonOptionString = optarg;
1349 std::unordered_map<std::string, std::vector<std::string>> addonOptions;
1350 for (
const std::string_view item :
1353 if (tokens.size() != 2) {
1358 addonOptions_ = std::move(addonOptions);
1366 d->signalPipe_ = fd;
1367 d->signalPipeEvent_ = d->eventLoop_.addIOEvent(
1377 return d->arg_.tryReplace;
1382 return d->arg_.exitWhenMainDisplayDisconnected;
1390 void Instance::handleSignal() {
1394 while (
fs::safeRead(d->signalPipe_, &signo,
sizeof(signo)) > 0) {
1395 if (signo == SIGINT || signo == SIGTERM || signo == SIGQUIT ||
1398 }
else if (signo == SIGUSR1) {
1400 }
else if (signo == SIGCHLD) {
1401 d->zombieReaper_->setNextInterval(2000000);
1402 d->zombieReaper_->setOneShot();
1410 if (!d->arg_.uiName.empty()) {
1411 d->arg_.enableList.push_back(d->arg_.uiName);
1414 d->icManager_.registerProperty(
"inputState", &d->inputStateFactory_);
1415 std::unordered_set<std::string> enabled;
1416 std::unordered_set<std::string> disabled;
1417 std::tie(enabled, disabled) = d->overrideAddons();
1418 FCITX_INFO() <<
"Override Enabled Addons: " << enabled;
1419 FCITX_INFO() <<
"Override Disabled Addons: " << disabled;
1420 d->addonManager_.load(enabled, disabled);
1425 d->uiManager_.load(d->arg_.uiName);
1427 const auto *entry = d->imManager_.entry(
"keyboard-us");
1428 FCITX_LOG_IF(Error, !entry) <<
"Couldn't find keyboard-us";
1429 d->preloadInputMethodEvent_ = d->eventLoop_.addTimeEvent(
1430 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
1433 if (d->exit_ || !d->globalConfig_.preloadInputMethod()) {
1437 if (!d->imManager_.currentGroup().inputMethodList().empty()) {
1438 if (
const auto *entry =
1439 d->imManager_.entry(d->imManager_.currentGroup()
1440 .inputMethodList()[0]
1442 d->addonManager_.addon(entry->addon(),
true);
1446 if (!d->imManager_.currentGroup().defaultInputMethod().empty()) {
1447 if (
const auto *entry = d->imManager_.entry(
1448 d->imManager_.currentGroup().defaultInputMethod())) {
1449 d->addonManager_.addon(entry->addon(),
true);
1455 d->zombieReaper_ = d->eventLoop_.addTimeEvent(
1456 CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), 0,
1459 while ((res = waitpid(-1,
nullptr, WNOHANG)) > 0) {
1463 d->zombieReaper_->setEnabled(
false);
1466 d->exitEvent_ = d->eventLoop_.addExitEvent([
this](
EventSource *) {
1467 FCITX_DEBUG() <<
"Running save...";
1471 d->notifications_ = d->addonManager_.addon(
"notifications",
true);
1476 if (d->arg_.quietQuit) {
1483 return d->exitCode_;
1486 auto r = eventLoop().exec();
1487 d->running_ =
false;
1489 return r ? d->exitCode_ : 1;
1494 d->running_ = running;
1504 return d->inputMethodMode_;
1509 if (d->inputMethodMode_ == mode) {
1512 d->inputMethodMode_ = mode;
1521 bool Instance::virtualKeyboardAutoShow()
const {
1523 return d->virtualKeyboardAutoShow_;
1526 void Instance::setVirtualKeyboardAutoShow(
bool autoShow) {
1528 d->virtualKeyboardAutoShow_ = autoShow;
1531 bool Instance::virtualKeyboardAutoHide()
const {
1533 return d->virtualKeyboardAutoHide_;
1536 void Instance::setVirtualKeyboardAutoHide(
bool autoHide) {
1538 d->virtualKeyboardAutoHide_ = autoHide;
1541 VirtualKeyboardFunctionMode Instance::virtualKeyboardFunctionMode()
const {
1543 return d->virtualKeyboardFunctionMode_;
1546 void Instance::setVirtualKeyboardFunctionMode(
1547 VirtualKeyboardFunctionMode mode) {
1549 d->virtualKeyboardFunctionMode_ = mode;
1554 d->binaryMode_ =
true;
1559 const auto &addonNames = d->addonManager_.loadedAddonNames();
1560 return d->binaryMode_ &&
1561 std::all_of(addonNames.begin(), addonNames.end(),
1562 [d](
const std::string &name) {
1563 auto *addon = d->addonManager_.lookupAddon(name);
1567 return addon->canRestart();
1571 InstancePrivate *Instance::privateData() {
1578 return d->eventLoop_;
1583 return d->eventDispatcher_;
1588 return d->icManager_;
1593 return d->addonManager_;
1598 return d->imManager_;
1603 return d->imManager_;
1608 return d->uiManager_;
1613 return d->globalConfig_;
1616 bool Instance::postEvent(
Event &event) {
1617 return std::as_const(*this).postEvent(event);
1620 bool Instance::postEvent(
Event &event)
const {
1625 auto iter = d->eventHandlers_.find(event.
type());
1626 if (iter != d->eventHandlers_.end()) {
1627 const auto &handlers = iter->second;
1628 EventWatcherPhase phaseOrder[] = {
1629 EventWatcherPhase::ReservedFirst, EventWatcherPhase::PreInputMethod,
1630 EventWatcherPhase::InputMethod, EventWatcherPhase::PostInputMethod,
1631 EventWatcherPhase::ReservedLast};
1633 for (
auto phase : phaseOrder) {
1634 if (
auto iter2 = handlers.find(phase); iter2 != handlers.end()) {
1635 for (
auto &handler : iter2->second.view()) {
1650 auto &keyEvent =
static_cast<KeyEvent &
>(event);
1651 auto *ic = keyEvent.inputContext();
1652 #ifdef ENABLE_KEYBOARD 1654 if (!keyEvent.forward() && !keyEvent.origKey().code()) {
1657 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
1658 auto *xkbState = inputState->customXkbState();
1665 xkb_state_update_key(xkbState, keyEvent.origKey().code(),
1666 keyEvent.isRelease() ? XKB_KEY_UP
1670 if (ic->capabilityFlags().test(CapabilityFlag::KeyEventOrderFix) &&
1671 !keyEvent.accepted() && ic->hasPendingEventsStrictOrder()) {
1674 keyEvent.filterAndAccept();
1675 ic->forwardKey(keyEvent.origKey(), keyEvent.isRelease(),
1678 d_ptr->uiManager_.flush();
1681 return event.accepted();
1684 std::unique_ptr<HandlerTableEntry<EventHandler>>
1686 EventHandler callback) {
1688 if (phase == EventWatcherPhase::ReservedFirst ||
1689 phase == EventWatcherPhase::ReservedLast) {
1690 throw std::invalid_argument(
"Reserved Phase is only for internal use");
1692 return d->watchEvent(type, phase, std::move(callback));
1695 bool groupContains(
const InputMethodGroup &group,
const std::string &name) {
1696 const auto &list = group.inputMethodList();
1697 auto iter = std::find_if(list.begin(), list.end(),
1699 return item.name() == name;
1701 return iter != list.end();
1706 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
1709 if (!inputState->overrideDeactivateIM_.empty()) {
1710 return inputState->overrideDeactivateIM_;
1713 const auto &group = d->imManager_.currentGroup();
1716 !d->globalConfig_.allowInputMethodForPassword())) {
1717 auto defaultLayoutIM =
1718 stringutils::concat(
"keyboard-", group.defaultLayout());
1719 const auto *entry = d->imManager_.entry(defaultLayoutIM);
1721 entry = d->imManager_.entry(
"keyboard-us");
1723 return entry ? entry->uniqueName() :
"";
1726 if (group.inputMethodList().empty()) {
1729 if (inputState->isActive()) {
1730 if (!inputState->localIM_.empty() &&
1731 groupContains(group, inputState->localIM_)) {
1732 return inputState->localIM_;
1734 return group.defaultInputMethod();
1737 return group.inputMethodList()[0].name();
1742 auto imName = inputMethod(ic);
1743 if (imName.empty()) {
1746 return d->imManager_.entry(imName);
1751 const auto *entry = inputMethodEntry(ic);
1756 d->addonManager_.addon(entry->addon(),
true));
1761 const auto *entry = d->imManager_.entry(name);
1766 d->addonManager_.addon(entry->addon(),
true));
1771 const auto *entry = inputMethodEntry(ic);
1773 auto *engine = inputMethodEngine(ic);
1775 icon = engine->subModeIcon(*entry, *ic);
1778 icon = entry->icon();
1781 icon =
"input-keyboard";
1789 const auto *entry = inputMethodEntry(ic);
1790 auto *engine = inputMethodEngine(ic);
1792 if (engine && entry) {
1793 label = engine->subModeLabel(*entry, *ic);
1795 if (label.empty() && entry) {
1796 label = entry->label();
1802 #ifdef ENABLE_KEYBOARD 1804 auto *state = ic->
propertyFor(&d->inputStateFactory_);
1806 auto *xkbComposeState = state->xkbComposeState();
1807 if (!xkbComposeState) {
1811 auto keyval =
static_cast<xkb_keysym_t
>(keysym);
1813 enum xkb_compose_feed_result result =
1814 xkb_compose_state_feed(xkbComposeState, keyval);
1815 if (result == XKB_COMPOSE_FEED_IGNORED) {
1819 enum xkb_compose_status status =
1820 xkb_compose_state_get_status(xkbComposeState);
1821 if (status == XKB_COMPOSE_NOTHING) {
1824 if (status == XKB_COMPOSE_COMPOSED) {
1825 char buffer[FCITX_UTF8_MAX_LENGTH + 1] = {
'\0',
'\0',
'\0',
'\0',
1828 xkb_compose_state_get_utf8(xkbComposeState, buffer,
sizeof(buffer));
1829 xkb_compose_state_reset(xkbComposeState);
1831 return FCITX_INVALID_COMPOSE_RESULT;
1837 if (status == XKB_COMPOSE_CANCELLED) {
1838 xkb_compose_state_reset(xkbComposeState);
1841 return FCITX_INVALID_COMPOSE_RESULT;
1844 FCITX_UNUSED(keysym);
1851 #ifdef ENABLE_KEYBOARD 1853 auto *state = ic->
propertyFor(&d->inputStateFactory_);
1855 auto *xkbComposeState = state->xkbComposeState();
1856 if (!xkbComposeState) {
1857 return std::string();
1860 auto keyval =
static_cast<xkb_keysym_t
>(keysym);
1861 enum xkb_compose_feed_result result =
1862 xkb_compose_state_feed(xkbComposeState, keyval);
1864 if (result == XKB_COMPOSE_FEED_IGNORED) {
1865 return std::string();
1868 enum xkb_compose_status status =
1869 xkb_compose_state_get_status(xkbComposeState);
1870 if (status == XKB_COMPOSE_NOTHING) {
1871 return std::string();
1873 if (status == XKB_COMPOSE_COMPOSED) {
1875 std::array<char, 256> buffer;
1876 auto length = xkb_compose_state_get_utf8(xkbComposeState, buffer.data(),
1878 xkb_compose_state_reset(xkbComposeState);
1880 return std::nullopt;
1883 auto bufferBegin = buffer.begin();
1884 auto bufferEnd = std::next(bufferBegin,
length);
1886 return std::string(bufferBegin, bufferEnd);
1888 return std::nullopt;
1890 if (status == XKB_COMPOSE_CANCELLED) {
1891 xkb_compose_state_reset(xkbComposeState);
1893 return std::nullopt;
1896 FCITX_UNUSED(keysym);
1897 return std::string();
1902 #ifdef ENABLE_KEYBOARD 1904 auto *state = inputContext->
propertyFor(&d->inputStateFactory_);
1906 auto *xkbComposeState = state->xkbComposeState();
1907 if (!xkbComposeState) {
1911 return xkb_compose_state_get_status(xkbComposeState) ==
1912 XKB_COMPOSE_COMPOSING;
1914 FCITX_UNUSED(inputContext);
1920 #ifdef ENABLE_KEYBOARD 1922 auto *state = inputContext->
propertyFor(&d->inputStateFactory_);
1923 auto *xkbComposeState = state->xkbComposeState();
1924 if (!xkbComposeState) {
1927 xkb_compose_state_reset(xkbComposeState);
1929 FCITX_UNUSED(inputContext);
1936 d->idleStartTimestamp_ = now(CLOCK_MONOTONIC);
1937 d->imManager_.save();
1938 d->addonManager_.saveAll();
1943 if (
auto *ic = mostRecentInputContext()) {
1944 CheckInputMethodChanged imChangedRAII(ic, d);
1951 if (
const auto *entry = inputMethodManager().entry(imName)) {
1952 return entry->uniqueName();
1962 void Instance::configureAddon(
const std::string & ) {}
1964 void Instance::configureInputMethod(
const std::string & ) {}
1967 if (
auto *ic = mostRecentInputContext()) {
1968 if (
const auto *entry = inputMethodEntry(ic)) {
1969 return entry->uniqueName();
1977 return d->uiManager_.currentUI();
1982 if (
auto *ic = mostRecentInputContext()) {
1983 CheckInputMethodChanged imChangedRAII(ic, d);
1993 d->exitCode_ = exitCode;
1995 d->eventLoop_.exit();
2000 auto *addon = addonManager().addon(addonName);
2002 addon->reloadConfig();
2008 auto [enabled, disabled] = d->overrideAddons();
2009 d->addonManager_.load(enabled, disabled);
2010 d->imManager_.refresh();
2015 readAsIni(d->globalConfig_.config(), StandardPathsType::PkgConfig,
2017 FCITX_DEBUG() <<
"Trigger Key: " 2019 d->icManager_.setPropertyPropagatePolicy(
2020 d->globalConfig_.shareInputState());
2021 if (d->globalConfig_.preeditEnabledByDefault() !=
2022 d->icManager_.isPreeditEnabledByDefault()) {
2023 d->icManager_.setPreeditEnabledByDefault(
2024 d->globalConfig_.preeditEnabledByDefault());
2030 #ifdef ENABLE_KEYBOARD 2031 d->keymapCache_.clear();
2032 if (d->inputStateFactory_.registered()) {
2034 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2035 inputState->resetXkbState();
2044 if (d->globalConfig_.autoSavePeriod() <= 0) {
2045 d->periodicalSave_->setEnabled(
false);
2047 d->periodicalSave_->setNextInterval(AutoSaveMinInUsecs *
2048 d->globalConfig_.autoSavePeriod());
2049 d->periodicalSave_->setOneShot();
2060 if (!canRestart()) {
2068 setCurrentInputMethod(mostRecentInputContext(), name,
false);
2074 if (!canTrigger()) {
2078 auto &imManager = inputMethodManager();
2079 const auto &imList = imManager.currentGroup().inputMethodList();
2080 auto iter = std::find_if(imList.begin(), imList.end(),
2082 return item.name() == name;
2084 if (iter == imList.end()) {
2088 auto setGlobalDefaultInputMethod = [d](
const std::string &name) {
2089 std::vector<std::unique_ptr<CheckInputMethodChanged>> groupRAIICheck;
2090 d->icManager_.foreachFocused([d, &groupRAIICheck](
InputContext *ic) {
2092 groupRAIICheck.push_back(
2093 std::make_unique<CheckInputMethodChanged>(ic, d));
2096 d->imManager_.setDefaultInputMethod(name);
2099 auto idx = std::distance(imList.begin(), iter);
2101 CheckInputMethodChanged imChangedRAII(ic, d);
2102 auto currentIM = inputMethod(ic);
2103 if (currentIM == name) {
2106 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2110 inputState->setLocalIM(name);
2112 inputState->setLocalIM({});
2114 setGlobalDefaultInputMethod(name);
2116 inputState->setActive(
true);
2118 inputState->setActive(
false);
2120 if (inputState->imChanged_) {
2130 setGlobalDefaultInputMethod(name);
2138 if (
auto *ic = mostRecentInputContext()) {
2139 auto *inputState = ic->propertyFor(&d->inputStateFactory_);
2140 return inputState->isActive() ? 2 : 1;
2147 if (
auto *ic = mostRecentInputContext()) {
2148 CheckInputMethodChanged imChangedRAII(ic, d);
2155 if (
auto *ic = mostRecentInputContext()) {
2156 CheckInputMethodChanged imChangedRAII(ic, d);
2157 enumerate(ic, forward);
2161 bool Instance::canTrigger()
const {
2162 const auto &imManager = inputMethodManager();
2163 return (imManager.currentGroup().inputMethodList().size() > 1);
2167 if (!canTrigger()) {
2171 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2172 if (inputState->isActive()) {
2175 return inputState->lastIMChangeIsAltTrigger_;
2180 if (!canTrigger()) {
2184 if (d->globalConfig_.enumerateSkipFirst()) {
2185 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2186 if (!inputState->isActive()) {
2189 return d->imManager_.currentGroup().inputMethodList().size() > 2;
2195 bool Instance::canChangeGroup()
const {
2196 const auto &imManager = inputMethodManager();
2197 return (imManager.groupCount() > 1);
2202 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2203 if (!canTrigger()) {
2206 inputState->setActive(!inputState->isActive());
2207 if (inputState->imChanged_) {
2208 inputState->imChanged_->setReason(reason);
2213 bool Instance::trigger(
InputContext *ic,
bool totallyReleased) {
2215 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2216 if (!canTrigger()) {
2221 if (totallyReleased) {
2223 inputState->firstTrigger_ =
true;
2225 if (!d->globalConfig_.enumerateWithTriggerKeys() ||
2226 (inputState->firstTrigger_ && inputState->isActive()) ||
2227 (d->globalConfig_.enumerateSkipFirst() &&
2228 d->imManager_.currentGroup().inputMethodList().size() <= 2)) {
2231 enumerate(ic,
true);
2233 inputState->firstTrigger_ =
false;
2239 if (!canAltTrigger(ic)) {
2249 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2250 if (!canTrigger()) {
2253 if (inputState->isActive()) {
2256 inputState->setActive(
true);
2257 if (inputState->imChanged_) {
2265 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2266 if (!canTrigger()) {
2269 if (!inputState->isActive()) {
2272 inputState->setActive(
false);
2273 if (inputState->imChanged_) {
2274 inputState->imChanged_->setReason(
2282 auto &imManager = inputMethodManager();
2283 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2284 const auto &imList = imManager.currentGroup().inputMethodList();
2285 if (!canTrigger()) {
2289 if (d->globalConfig_.enumerateSkipFirst() && imList.size() <= 2) {
2293 auto currentIM = inputMethod(ic);
2295 auto iter = std::ranges::find_if(
2297 return item.name() == currentIM;
2299 if (iter == imList.end()) {
2302 int idx = std::distance(imList.begin(), iter);
2303 auto nextIdx = [forward, &imList](
int idx) {
2305 return (idx + (forward ? 1 : (imList.size() - 1))) % imList.size();
2309 if (d->globalConfig_.enumerateSkipFirst() && idx == 0) {
2313 std::vector<std::unique_ptr<CheckInputMethodChanged>> groupRAIICheck;
2314 d->icManager_.foreachFocused([d, &groupRAIICheck](
InputContext *ic) {
2316 groupRAIICheck.push_back(
2317 std::make_unique<CheckInputMethodChanged>(ic, d));
2320 imManager.setDefaultInputMethod(imList[idx].name());
2321 inputState->setActive(
true);
2322 inputState->setLocalIM({});
2324 inputState->setActive(
false);
2326 if (inputState->imChanged_) {
2334 const std::string &orig) {
2335 std::string result = orig;
2336 emit<Instance::CommitFilter>(inputContext, result);
2342 emit<Instance::OutputFilter>(inputContext, result);
2343 if ((&orig == &inputContext->inputPanel().clientPreedit() ||
2344 &orig == &inputContext->inputPanel().preedit()) &&
2345 !globalConfig().showPreeditForPassword() &&
2346 inputContext->capabilityFlags().test(CapabilityFlag::Password)) {
2348 for (
int i = 0, e = result.size(); i < e; i++) {
2353 dot +=
"\xe2\x80\xa2";
2356 newText.append(std::move(dot),
2357 result.formatAt(i) | TextFormatFlag::DontCommit);
2359 result = std::move(newText);
2366 return d->icManager_.lastFocusedInputContext();
2371 return d->icManager_.mostRecentInputContext();
2376 d->uiManager_.flush();
2379 int scoreForGroup(
FocusGroup *group,
const std::string &displayHint) {
2381 if (displayHint.empty()) {
2382 if (group->display() ==
"x11:") {
2388 if (group->display() ==
"wayland:") {
2395 if (group->display() == displayHint) {
2410 d->icManager_.foreachGroup(
2411 [&score, &displayHint, &defaultFocusGroup](
FocusGroup *group) {
2412 auto newScore = scoreForGroup(group, displayHint);
2413 if (newScore > score) {
2414 defaultFocusGroup = group;
2420 return defaultFocusGroup;
2425 FCITX_DEBUG() <<
"Instance::activateInputMethod";
2427 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2428 const auto *entry = inputMethodEntry(ic);
2430 FCITX_DEBUG() <<
"Activate: " 2431 <<
"[Last]:" << inputState->lastIM_
2432 <<
" [Activating]:" << entry->uniqueName();
2433 assert(inputState->lastIM_.empty());
2434 inputState->lastIM_ = entry->uniqueName();
2436 auto *engine = inputMethodEngine(ic);
2437 if (!engine || !entry) {
2440 #ifdef ENABLE_KEYBOARD 2441 if (
auto *xkbState = inputState->customXkbState(
true)) {
2442 if (
auto *mods = findValue(d->stateMask_, ic->
display())) {
2443 FCITX_KEYTRACE() <<
"Update mask to customXkbState";
2444 auto depressed = std::get<0>(*mods);
2445 auto latched = std::get<1>(*mods);
2446 auto locked = std::get<2>(*mods);
2451 FCITX_KEYTRACE() << depressed <<
" " << latched <<
" " << locked;
2452 if (depressed == 0) {
2453 inputState->setModsAllReleased();
2455 xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0,
2461 engine->activate(*entry, event);
2467 FCITX_DEBUG() <<
"Instance::deactivateInputMethod event_type=" 2468 <<
static_cast<uint32_t
>(
event.type());
2470 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2477 FCITX_DEBUG() <<
"Switch reason: " 2478 <<
static_cast<int>(icEvent.reason());
2479 FCITX_DEBUG() <<
"Old Input method: " << icEvent.oldInputMethod();
2480 entry = d->imManager_.entry(icEvent.oldInputMethod());
2482 entry = inputMethodEntry(ic);
2485 FCITX_DEBUG() <<
"Deactivate: " 2486 <<
"[Last]:" << inputState->lastIM_
2487 <<
" [Deactivating]:" << entry->uniqueName();
2488 assert(entry->uniqueName() == inputState->lastIM_);
2490 d->addonManager_.addon(entry->addon()));
2492 inputState->lastIM_.clear();
2493 if (!engine || !entry) {
2496 inputState->overrideDeactivateIM_ = entry->uniqueName();
2498 inputState->overrideDeactivateIM_.clear();
2502 bool Instance::enumerateGroup(
bool forward) {
2503 auto &imManager = inputMethodManager();
2504 auto groups = imManager.groups();
2505 if (groups.size() <= 1) {
2509 imManager.setCurrentGroup(groups[1]);
2511 imManager.setCurrentGroup(groups.back());
2517 FCITX_DEBUG() <<
"Input method switched";
2519 if (!d->globalConfig_.showInputMethodInformation()) {
2522 d->showInputMethodInformation(ic);
2526 const std::string &message) {
2527 FCITX_DEBUG() <<
"Input method switched";
2529 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2530 inputState->showInputMethodInformation(message);
2535 return (isInFlatpak() &&
2536 std::filesystem::is_regular_file(
"/app/.updated")) ||
2537 d->addonManager_.checkUpdate() || d->imManager_.checkUpdate() ||
2542 const std::string &rule,
2543 const std::string &model,
2544 const std::string &options) {
2545 #ifdef ENABLE_KEYBOARD 2547 bool resetState =
false;
2548 if (
auto *param = findValue(d->xkbParams_, display)) {
2549 if (std::get<0>(*param) != rule || std::get<1>(*param) != model ||
2550 std::get<2>(*param) != options) {
2551 std::get<0>(*param) = rule;
2552 std::get<1>(*param) = model;
2553 std::get<2>(*param) = options;
2557 d->xkbParams_.emplace(display, std::make_tuple(rule, model, options));
2561 d->keymapCache_[display].clear();
2562 d->icManager_.foreach([d, &display](
InputContext *ic) {
2563 if (ic->
display() == display ||
2564 !d->xkbParams_.contains(ic->
display())) {
2565 auto *inputState = ic->
propertyFor(&d->inputStateFactory_);
2566 inputState->resetXkbState();
2572 FCITX_UNUSED(display);
2574 FCITX_UNUSED(model);
2575 FCITX_UNUSED(options);
2580 uint32_t depressed_mods,
2581 uint32_t latched_mods, uint32_t locked_mods) {
2583 d->stateMask_[display] =
2584 std::make_tuple(depressed_mods, latched_mods, locked_mods);
2589 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.
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.
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.