Fcitx
inputcontextmanager.cpp
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "inputcontextmanager.h"
9 #include <cassert>
10 #include <stdexcept>
11 #include <unordered_map>
12 #include "fcitx-utils/intrusivelist.h"
13 #include "fcitx-utils/log.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"
19 
20 namespace fcitx {
21 
22 namespace {
23 
24 // The dummy input context can be used to propagate context state when the
25 // PropertyPropagatePolicy is All
26 class DummyInputContext : public InputContext {
27 public:
28  DummyInputContext(InputContextManager &manager)
29  : InputContext(manager, "") {}
30 
31  ~DummyInputContext() { destroy(); }
32 
33  const char *frontend() const override { return "dummy"; }
34 
35  void commitStringImpl(const std::string &) override {}
36  void deleteSurroundingTextImpl(int, unsigned int) override {}
37  void forwardKeyImpl(const ForwardKeyEvent &) override {}
38  void updatePreeditImpl() override {}
39 };
40 
41 struct container_hasher {
42  template <class T>
43  std::size_t operator()(const T &c) const {
44  std::size_t seed = 0;
45  for (const auto &elem : c) {
46  hash_combine(seed, std::hash<typename T::value_type>()(elem));
47  }
48  return seed;
49  }
50 };
51 
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;
61 };
62 
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;
72 };
73 
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;
83 };
84 
85 inline InputContext *toInputContextPointer(InputContext *self) { return self; }
86 inline InputContext *toInputContextPointer(InputContext &self) { return &self; }
87 
88 } // namespace
89 
91 public:
92  static InputContextPrivate *toInputContextPrivate(InputContext &ic) {
93  return ic.d_func();
94  }
95  static FocusGroupPrivate *toFocusGroupPrivate(FocusGroup &group) {
96  return group.d_func();
97  }
98  static const InputContextPrivate *
99  toInputContextPrivate(const InputContext &ic) {
100  return ic.d_func();
101  }
102  static const FocusGroupPrivate *
103  toFocusGroupPrivate(const FocusGroup &group) {
104  return group.d_func();
105  }
106 
107  inline bool registerProperty(InputContextManager *q_ptr,
108  const std::string &name,
109  InputContextPropertyFactoryPrivate *factory) {
110  auto result = propertyFactories_.emplace(name, factory);
111  if (!result.second) {
112  return false;
113  }
114  factory->manager_ = q_ptr;
115  factory->slot_ = propertyFactoriesSlots_.size();
116  factory->name_ = name;
117 
118  propertyFactoriesSlots_.push_back(factory);
119  for (auto &inputContext : inputContexts_) {
120  inputContext.d_func()->registerProperty(
121  factory->slot_, factory->q_func()->create(inputContext));
122  }
123  return true;
124  }
125 
126  inline void unregisterProperty(const std::string &name) {
127  auto iter = propertyFactories_.find(name);
128  if (iter == propertyFactories_.end()) {
129  return;
130  }
131  auto *factory = iter->second;
132  auto slot = factory->slot_;
133  // move slot, logic need to be same as inputContext
134  propertyFactoriesSlots_[slot] = propertyFactoriesSlots_.back();
135  propertyFactoriesSlots_[slot]->slot_ = slot;
136  propertyFactoriesSlots_.pop_back();
137 
138  for (auto &inputContext : inputContexts_) {
139  inputContext.d_func()->unregisterProperty(slot);
140  }
141  propertyFactories_.erase(iter);
142 
143  factory->manager_ = nullptr;
144  factory->name_ = std::string();
145  factory->slot_ = -1;
146  }
147 
148  inline void registerInputContext(InputContext &inputContext) {
149  inputContexts_.push_back(inputContext);
150  int maxRetry = 3;
151  do {
152  generateUUID(inputContext.d_func()->uuid_.data());
153  maxRetry -= 1;
154  } while (!uuidMap_.emplace(inputContext.uuid(), &inputContext).second &&
155  maxRetry > 0);
156  if (!inputContext.program().empty()) {
157  programMap_[inputContext.program()].insert(&inputContext);
158  }
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) !=
171  &inputContext) {
172  toInputContextPointer(dstInputContext)
173  ->property(p.first)
174  ->copyTo(property);
175  break;
176  }
177  }
178  };
179  if (propertyPropagatePolicy_ == PropertyPropagatePolicy::All) {
180  copyProperty(inputContexts_);
181  } else {
182  auto iter = programMap_.find(inputContext.program());
183  if (iter != programMap_.end()) {
184  copyProperty(iter->second);
185  }
186  }
187  }
188  }
189  }
190 
191  std::unordered_map<ICUUID, InputContext *, container_hasher> uuidMap_;
194  focusedInputContexts_;
195  TrackableObjectReference<InputContext> mostRecentInputContext_;
197  // order matters, need to delete it before groups gone
198  Instance *instance_ = nullptr;
199  std::unordered_map<std::string, InputContextPropertyFactoryPrivate *>
200  propertyFactories_;
201  std::vector<InputContextPropertyFactoryPrivate *> propertyFactoriesSlots_;
202  std::unordered_map<std::string, std::unordered_set<InputContext *>>
203  programMap_;
204  PropertyPropagatePolicy propertyPropagatePolicy_ =
205  PropertyPropagatePolicy::No;
206  bool finalized_ = false;
207  bool preeditEnabledByDefault_ = true;
208  std::unique_ptr<InputContext> dummyInputContext_;
209 };
210 
211 #define DEFINE_LIST_HELPERS(HELPERTYPE, TYPE, MEMBER) \
212  IntrusiveListNode &HELPERTYPE::toNode(TYPE &value) noexcept { \
213  return InputContextManagerPrivate::to##TYPE##Private(value)->MEMBER; \
214  } \
215  TYPE &HELPERTYPE::toValue(IntrusiveListNode &node) noexcept { \
216  return *parentFromMember(&node, &TYPE##Private::MEMBER)->q_func(); \
217  } \
218  const IntrusiveListNode &HELPERTYPE::toNode(const TYPE &value) noexcept { \
219  return InputContextManagerPrivate::to##TYPE##Private(value)->MEMBER; \
220  } \
221  const TYPE &HELPERTYPE::toValue(const IntrusiveListNode &node) noexcept { \
222  return *parentFromMember(&node, &TYPE##Private::MEMBER)->q_func(); \
223  }
224 
225 DEFINE_LIST_HELPERS(InputContextListHelper, InputContext, listNode_)
226 DEFINE_LIST_HELPERS(InputContextFocusedListHelper, InputContext,
227  focusedListNode_)
228 DEFINE_LIST_HELPERS(FocusGroupListHelper, FocusGroup, listNode_)
229 
230 InputContextManager::InputContextManager()
231  : d_ptr(std::make_unique<InputContextManagerPrivate>()) {
232  FCITX_D();
233 
234  d->dummyInputContext_ = std::make_unique<DummyInputContext>(*this);
235 }
236 
237 InputContextManager::~InputContextManager() {
238  FCITX_D();
239  // Need to reset this before d becomes invalid.
240  d->dummyInputContext_.reset();
241 }
242 
244  FCITX_D();
245  auto iter = d->uuidMap_.find(uuid);
246  return (iter == d->uuidMap_.end()) ? nullptr : iter->second;
247 }
248 
249 bool InputContextManager::foreach(const InputContextVisitor &visitor) {
250  FCITX_D();
251  for (auto &ic : d->inputContexts_) {
252  if (!visitor(&ic)) {
253  return false;
254  }
255  }
256  return true;
257 }
258 
259 bool InputContextManager::foreachFocused(const InputContextVisitor &visitor) {
260  FCITX_D();
261  for (auto &ic : d->focusedInputContexts_) {
262  if (!visitor(&ic)) {
263  return false;
264  }
265  }
266  return true;
267 }
268 
269 bool InputContextManager::foreachGroup(const FocusGroupVisitor &visitor) {
270  FCITX_D();
271  for (auto &group : d->groups_) {
272  if (!visitor(&group)) {
273  return false;
274  }
275  }
276  return true;
277 }
278 
280  const std::string &name, InputContextPropertyFactory *factory) {
281  FCITX_D();
282  return d->registerProperty(this, name, factory->d_func());
283 }
284 
285 void InputContextManager::unregisterProperty(const std::string &name) {
286  FCITX_D();
287  return d->unregisterProperty(name);
288 }
289 
291  PropertyPropagatePolicy policy) {
292  FCITX_D();
293  d->propertyPropagatePolicy_ = policy;
294 }
295 
296 void InputContextManager::finalize() {
297  FCITX_D();
298  d->finalized_ = true;
299  d->dummyInputContext_.reset();
300  while (!d->inputContexts_.empty()) {
301  delete &d->inputContexts_.front();
302  }
303 }
304 
305 void InputContextManager::setInstance(Instance *instance) {
306  FCITX_D();
307  d->instance_ = instance;
308 }
309 
310 Instance *InputContextManager::instance() {
311  FCITX_D();
312  return d->instance_;
313 }
314 
315 void InputContextManager::registerInputContext(InputContext &inputContext) {
316  FCITX_D();
317  if (d->finalized_) {
318  throw std::runtime_error(
319  "Should not register input context after finalize");
320  }
321  d->registerInputContext(inputContext);
322 }
323 
324 void InputContextManager::unregisterInputContext(InputContext &inputContext) {
325  FCITX_D();
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);
332  }
333  }
334  }
335  d->uuidMap_.erase(inputContext.uuid());
336  d->inputContexts_.erase(d->inputContexts_.iterator_to(inputContext));
337 
338  if (d->focusedInputContexts_.isInList(inputContext)) {
339  d->focusedInputContexts_.erase(
340  d->focusedInputContexts_.iterator_to(inputContext));
341  }
342 }
343 
344 void InputContextManager::registerFocusGroup(FocusGroup &group) {
345  FCITX_D();
346  FCITX_DEBUG() << "Register focus group for display: " << group.display();
347  d->groups_.push_back(group);
348 }
349 
350 void InputContextManager::unregisterFocusGroup(FocusGroup &group) {
351  FCITX_D();
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();
358  }
359  }
360 }
362 InputContextManager::factoryForName(const std::string &name) {
363  FCITX_D();
364  auto iter = d->propertyFactories_.find(name);
365  if (iter == d->propertyFactories_.end()) {
366  return nullptr;
367  }
368  return iter->second->q_func();
369 }
371 InputContextManager::property(InputContext &inputContext,
372  const InputContextPropertyFactory *factory) {
373  assert(factory->d_func()->manager_ == this);
374  return InputContextManagerPrivate::toInputContextPrivate(inputContext)
375  ->property(factory->d_func()->slot_);
376 }
377 
378 void InputContextManager::propagateProperty(
379  InputContext &inputContext, const InputContextPropertyFactory *factory) {
380  FCITX_D();
381  assert(factory->d_func()->manager_ == this);
382  if (d->propertyPropagatePolicy_ == PropertyPropagatePolicy::No ||
383  (inputContext.program().empty() &&
384  d->propertyPropagatePolicy_ == PropertyPropagatePolicy::Program)) {
385  return;
386  }
387 
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));
397  }
398  }
399  }
400  };
401 
402  if (d->propertyPropagatePolicy_ == PropertyPropagatePolicy::All) {
403  copyProperty(d->inputContexts_);
404  } else {
405  auto iter = d->programMap_.find(inputContext.program());
406  if (iter != d->programMap_.end()) {
407  copyProperty(iter->second);
408  }
409  }
410 }
411 
412 void InputContextManager::notifyFocus(InputContext &inputContext,
413  bool hasFocus) {
414  FCITX_D();
415  if (hasFocus) {
416  if (d->focusedInputContexts_.isInList(inputContext)) {
417  if (&d->focusedInputContexts_.front() == &inputContext) {
418  return;
419  }
420  auto iter = d->focusedInputContexts_.iterator_to(inputContext);
421  d->focusedInputContexts_.erase(iter);
422  }
423  d->focusedInputContexts_.push_front(inputContext);
424  d->mostRecentInputContext_.unwatch();
425  } else {
426  if (d->focusedInputContexts_.isInList(inputContext)) {
427  auto iter = d->focusedInputContexts_.iterator_to(inputContext);
428  d->focusedInputContexts_.erase(iter);
429  }
430  // If this is the last ic. watch it.
431  if (d->focusedInputContexts_.empty()) {
432  d->mostRecentInputContext_ = inputContext.watch();
433  }
434  }
435 }
436 
438  FCITX_D();
439  return d->focusedInputContexts_.empty() ? nullptr
440  : &d->focusedInputContexts_.front();
441 }
442 
444  FCITX_D();
445  auto *ic = lastFocusedInputContext();
446  if (!ic) {
447  ic = d->mostRecentInputContext_.get();
448  }
449 
450  if (!ic && d->propertyPropagatePolicy_ == PropertyPropagatePolicy::All) {
451  ic = d->dummyInputContext_.get();
452  }
453 
454  return ic;
455 }
456 
458  FCITX_D();
459  return d->dummyInputContext_.get();
460 }
461 
462 void InputContextManager::setPreeditEnabledByDefault(bool enable) {
463  FCITX_D();
464  d->preeditEnabledByDefault_ = enable;
465 }
466 
467 bool InputContextManager::isPreeditEnabledByDefault() const {
468  FCITX_D();
469  return d->preeditEnabledByDefault_;
470 }
471 } // namespace fcitx
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.
Definition: instance.h:86
const ICUUID & uuid() const
Returns the uuid of this input context.
Definition: action.cpp:12
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.
Definition: inputcontext.h:45
Log utilities.
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.