8 #include "inputcontextmanager.h" 11 #include <unordered_map> 12 #include "fcitx-utils/intrusivelist.h" 14 #include "fcitx/misc_p.h" 15 #include "focusgroup.h" 16 #include "focusgroup_p.h" 17 #include "inputcontext_p.h" 18 #include "inputcontextproperty_p.h" 26 class DummyInputContext :
public InputContext {
28 DummyInputContext(InputContextManager &manager)
29 : InputContext(manager,
"") {}
31 ~DummyInputContext() { destroy(); }
33 const char *frontend()
const override {
return "dummy"; }
35 void commitStringImpl(
const std::string &)
override {}
36 void deleteSurroundingTextImpl(
int,
unsigned int)
override {}
37 void forwardKeyImpl(
const ForwardKeyEvent &)
override {}
38 void updatePreeditImpl()
override {}
41 struct container_hasher {
43 std::size_t operator()(
const T &c)
const {
45 for (
const auto &elem : c) {
46 hash_combine(seed, std::hash<typename T::value_type>()(elem));
52 struct InputContextListHelper {
53 [[maybe_unused]]
static IntrusiveListNode &
54 toNode(InputContext &value) noexcept;
55 [[maybe_unused]]
static InputContext &
56 toValue(IntrusiveListNode &node) noexcept;
57 [[maybe_unused]]
static const IntrusiveListNode &
58 toNode(
const InputContext &value) noexcept;
59 [[maybe_unused]]
static const InputContext &
60 toValue(
const IntrusiveListNode &node) noexcept;
63 struct InputContextFocusedListHelper {
64 [[maybe_unused]]
static IntrusiveListNode &
65 toNode(InputContext &value) noexcept;
66 [[maybe_unused]]
static InputContext &
67 toValue(IntrusiveListNode &node) noexcept;
68 [[maybe_unused]]
static const IntrusiveListNode &
69 toNode(
const InputContext &value) noexcept;
70 [[maybe_unused]]
static const InputContext &
71 toValue(
const IntrusiveListNode &node) noexcept;
74 struct FocusGroupListHelper {
75 [[maybe_unused]]
static IntrusiveListNode &
76 toNode(FocusGroup &value) noexcept;
77 [[maybe_unused]]
static FocusGroup &
78 toValue(IntrusiveListNode &node) noexcept;
79 [[maybe_unused]]
static const IntrusiveListNode &
80 toNode(
const FocusGroup &value) noexcept;
81 [[maybe_unused]]
static const FocusGroup &
82 toValue(
const IntrusiveListNode &node) noexcept;
85 inline InputContext *toInputContextPointer(InputContext *
self) {
return self; }
86 inline InputContext *toInputContextPointer(InputContext &
self) {
return &
self; }
92 static InputContextPrivate *toInputContextPrivate(
InputContext &ic) {
95 static FocusGroupPrivate *toFocusGroupPrivate(
FocusGroup &group) {
96 return group.d_func();
98 static const InputContextPrivate *
102 static const FocusGroupPrivate *
103 toFocusGroupPrivate(
const FocusGroup &group) {
104 return group.d_func();
108 const std::string &name,
109 InputContextPropertyFactoryPrivate *factory) {
110 auto result = propertyFactories_.emplace(name, factory);
111 if (!result.second) {
114 factory->manager_ = q_ptr;
115 factory->slot_ = propertyFactoriesSlots_.size();
116 factory->name_ = name;
118 propertyFactoriesSlots_.push_back(factory);
119 for (
auto &inputContext : inputContexts_) {
121 factory->slot_, factory->q_func()->create(inputContext));
126 inline void unregisterProperty(
const std::string &name) {
127 auto iter = propertyFactories_.find(name);
128 if (iter == propertyFactories_.end()) {
131 auto *factory = iter->second;
132 auto slot = factory->slot_;
134 propertyFactoriesSlots_[slot] = propertyFactoriesSlots_.back();
135 propertyFactoriesSlots_[slot]->slot_ = slot;
136 propertyFactoriesSlots_.pop_back();
138 for (
auto &inputContext : inputContexts_) {
139 inputContext.d_func()->unregisterProperty(slot);
141 propertyFactories_.erase(iter);
143 factory->manager_ =
nullptr;
144 factory->name_ = std::string();
148 inline void registerInputContext(
InputContext &inputContext) {
149 inputContexts_.push_back(inputContext);
152 generateUUID(inputContext.d_func()->uuid_.data());
154 }
while (!uuidMap_.emplace(inputContext.
uuid(), &inputContext).second &&
156 if (!inputContext.
program().empty()) {
157 programMap_[inputContext.
program()].insert(&inputContext);
159 for (
auto &p : propertyFactories_) {
160 auto *
property = p.second->q_func()->create(inputContext);
161 inputContext.d_func()->registerProperty(p.second->slot_, property);
162 if (property->needCopy() &&
163 (propertyPropagatePolicy_ == PropertyPropagatePolicy::All ||
164 (!inputContext.
program().empty() &&
165 propertyPropagatePolicy_ ==
166 PropertyPropagatePolicy::Program))) {
167 auto copyProperty = [&p, &inputContext,
168 &property](
auto &container) {
169 for (
auto &dstInputContext : container) {
170 if (toInputContextPointer(dstInputContext) !=
172 toInputContextPointer(dstInputContext)
179 if (propertyPropagatePolicy_ == PropertyPropagatePolicy::All) {
180 copyProperty(inputContexts_);
182 auto iter = programMap_.find(inputContext.program());
183 if (iter != programMap_.end()) {
184 copyProperty(iter->second);
191 std::unordered_map<ICUUID, InputContext *, container_hasher> uuidMap_;
194 focusedInputContexts_;
199 std::unordered_map<std::string, InputContextPropertyFactoryPrivate *>
201 std::vector<InputContextPropertyFactoryPrivate *> propertyFactoriesSlots_;
202 std::unordered_map<std::string, std::unordered_set<InputContext *>>
204 PropertyPropagatePolicy propertyPropagatePolicy_ =
205 PropertyPropagatePolicy::No;
206 bool finalized_ =
false;
207 bool preeditEnabledByDefault_ =
true;
208 std::unique_ptr<InputContext> dummyInputContext_;
211 #define DEFINE_LIST_HELPERS(HELPERTYPE, TYPE, MEMBER) \ 212 IntrusiveListNode &HELPERTYPE::toNode(TYPE &value) noexcept { \ 213 return InputContextManagerPrivate::to##TYPE##Private(value)->MEMBER; \ 215 TYPE &HELPERTYPE::toValue(IntrusiveListNode &node) noexcept { \ 216 return *parentFromMember(&node, &TYPE##Private::MEMBER)->q_func(); \ 218 const IntrusiveListNode &HELPERTYPE::toNode(const TYPE &value) noexcept { \ 219 return InputContextManagerPrivate::to##TYPE##Private(value)->MEMBER; \ 221 const TYPE &HELPERTYPE::toValue(const IntrusiveListNode &node) noexcept { \ 222 return *parentFromMember(&node, &TYPE##Private::MEMBER)->q_func(); \ 225 DEFINE_LIST_HELPERS(InputContextListHelper,
InputContext, listNode_)
226 DEFINE_LIST_HELPERS(InputContextFocusedListHelper,
InputContext,
228 DEFINE_LIST_HELPERS(FocusGroupListHelper,
FocusGroup, listNode_)
230 InputContextManager::InputContextManager()
231 : d_ptr(std::make_unique<InputContextManagerPrivate>()) {
234 d->dummyInputContext_ = std::make_unique<DummyInputContext>(*this);
237 InputContextManager::~InputContextManager() {
240 d->dummyInputContext_.reset();
245 auto iter = d->uuidMap_.find(uuid);
246 return (iter == d->uuidMap_.end()) ?
nullptr : iter->second;
249 bool InputContextManager::foreach(
const InputContextVisitor &visitor) {
251 for (
auto &ic : d->inputContexts_) {
259 bool InputContextManager::foreachFocused(
const InputContextVisitor &visitor) {
261 for (
auto &ic : d->focusedInputContexts_) {
269 bool InputContextManager::foreachGroup(
const FocusGroupVisitor &visitor) {
271 for (
auto &group : d->groups_) {
272 if (!visitor(&group)) {
282 return d->registerProperty(
this, name, factory->d_func());
285 void InputContextManager::unregisterProperty(
const std::string &name) {
287 return d->unregisterProperty(name);
291 PropertyPropagatePolicy policy) {
293 d->propertyPropagatePolicy_ = policy;
296 void InputContextManager::finalize() {
298 d->finalized_ =
true;
299 d->dummyInputContext_.reset();
300 while (!d->inputContexts_.empty()) {
301 delete &d->inputContexts_.front();
305 void InputContextManager::setInstance(
Instance *instance) {
307 d->instance_ = instance;
310 Instance *InputContextManager::instance() {
315 void InputContextManager::registerInputContext(
InputContext &inputContext) {
318 throw std::runtime_error(
319 "Should not register input context after finalize");
321 d->registerInputContext(inputContext);
324 void InputContextManager::unregisterInputContext(
InputContext &inputContext) {
326 if (!inputContext.
program().empty()) {
327 auto iter = d->programMap_.find(inputContext.
program());
328 if (iter != d->programMap_.end()) {
329 iter->second.erase(&inputContext);
330 if (iter->second.empty()) {
331 d->programMap_.erase(iter);
335 d->uuidMap_.erase(inputContext.
uuid());
336 d->inputContexts_.erase(d->inputContexts_.iterator_to(inputContext));
338 if (d->focusedInputContexts_.isInList(inputContext)) {
339 d->focusedInputContexts_.erase(
340 d->focusedInputContexts_.iterator_to(inputContext));
344 void InputContextManager::registerFocusGroup(
FocusGroup &group) {
346 FCITX_DEBUG() <<
"Register focus group for display: " << group.display();
347 d->groups_.push_back(group);
350 void InputContextManager::unregisterFocusGroup(
FocusGroup &group) {
352 d->groups_.erase(d->groups_.iterator_to(group));
353 if (d->instance_ && d->instance_->exitWhenMainDisplayDisconnected() &&
354 d->groups_.empty()) {
355 if (!d->instance_->exiting()) {
356 FCITX_INFO() <<
"All display connections are gone, exit now.";
357 d->instance_->exit();
362 InputContextManager::factoryForName(
const std::string &name) {
364 auto iter = d->propertyFactories_.find(name);
365 if (iter == d->propertyFactories_.end()) {
368 return iter->second->q_func();
371 InputContextManager::property(
InputContext &inputContext,
373 assert(factory->d_func()->manager_ ==
this);
374 return InputContextManagerPrivate::toInputContextPrivate(inputContext)
375 ->property(factory->d_func()->slot_);
378 void InputContextManager::propagateProperty(
381 assert(factory->d_func()->manager_ ==
this);
382 if (d->propertyPropagatePolicy_ == PropertyPropagatePolicy::No ||
383 (inputContext.
program().empty() &&
384 d->propertyPropagatePolicy_ == PropertyPropagatePolicy::Program)) {
388 auto *
property = this->property(inputContext, factory);
389 auto factoryRef = factory->watch();
390 auto copyProperty = [
this, &factoryRef, &inputContext,
391 &property](
auto &container) {
392 for (
auto &dstInputContext_ : container) {
393 if (
const auto *factory = factoryRef.get()) {
394 auto dstInputContext = toInputContextPointer(dstInputContext_);
395 if (dstInputContext != &inputContext) {
396 property->copyTo(this->property(*dstInputContext, factory));
402 if (d->propertyPropagatePolicy_ == PropertyPropagatePolicy::All) {
403 copyProperty(d->inputContexts_);
405 auto iter = d->programMap_.find(inputContext.program());
406 if (iter != d->programMap_.end()) {
407 copyProperty(iter->second);
412 void InputContextManager::notifyFocus(
InputContext &inputContext,
416 if (d->focusedInputContexts_.isInList(inputContext)) {
417 if (&d->focusedInputContexts_.front() == &inputContext) {
420 auto iter = d->focusedInputContexts_.iterator_to(inputContext);
421 d->focusedInputContexts_.erase(iter);
423 d->focusedInputContexts_.push_front(inputContext);
424 d->mostRecentInputContext_.unwatch();
426 if (d->focusedInputContexts_.isInList(inputContext)) {
427 auto iter = d->focusedInputContexts_.iterator_to(inputContext);
428 d->focusedInputContexts_.erase(iter);
431 if (d->focusedInputContexts_.empty()) {
432 d->mostRecentInputContext_ = inputContext.watch();
439 return d->focusedInputContexts_.empty() ? nullptr
440 : &d->focusedInputContexts_.front();
445 auto *ic = lastFocusedInputContext();
447 ic = d->mostRecentInputContext_.get();
450 if (!ic && d->propertyPropagatePolicy_ == PropertyPropagatePolicy::All) {
451 ic = d->dummyInputContext_.get();
459 return d->dummyInputContext_.get();
462 void InputContextManager::setPreeditEnabledByDefault(
bool enable) {
464 d->preeditEnabledByDefault_ = enable;
467 bool InputContextManager::isPreeditEnabledByDefault()
const {
469 return d->preeditEnabledByDefault_;
Utility class provides a weak reference to the object.
InputContext * findByUUID(ICUUID uuid)
Find the input context by UUID.
An instance represents a standalone Fcitx instance.
const ICUUID & uuid() const
Returns the uuid of this input context.
virtual void copyTo(InputContextProperty *)
copy state to another property.
InputContextProperty * property(const std::string &name)
Returns the input context property by name.
bool registerProperty(const std::string &name, InputContextPropertyFactory *factory)
Register a named property for input context.
InputContext * lastFocusedInputContext()
Get the last focused input context.
const std::string & program() const
Returns the program name of input context.
InputContext * mostRecentInputContext()
Get the last used input context.
Factory class for input context property.
InputContext * dummyInputContext() const
Return a dummy input context registered with this input method manager.
An input context represents a client of Fcitx.
This is a class that designed to store state that is specific to certain input context.
void setPropertyPropagatePolicy(PropertyPropagatePolicy policy)
Set the property propgate policy.