Fcitx
objectvtable.h
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _FCITX_UTILS_DBUS_OBJECTVTABLE_H_
8 #define _FCITX_UTILS_DBUS_OBJECTVTABLE_H_
9 
10 #include <cstdint>
11 #include <exception>
12 #include <functional>
13 #include <memory>
14 #include <mutex>
15 #include <string>
16 #include <tuple>
17 #include <type_traits>
19 #include <fcitx-utils/fcitxutils_export.h>
20 #include <fcitx-utils/flags.h>
21 #include <fcitx-utils/macros.h>
23 
24 /// \addtogroup FcitxUtils
25 /// \{
26 /// \file
27 /// \brief High level API for dbus objects.
28 
29 namespace fcitx::dbus {
30 class Message;
31 class ObjectVTableBase;
32 class Slot;
33 class Bus;
34 class ObjectVTablePrivate;
35 
36 using ObjectMethod = std::function<bool(Message)>;
37 using ObjectMethodClosure = std::function<bool(Message, const ObjectMethod &)>;
38 using PropertyGetMethod = std::function<void(Message &)>;
39 using PropertySetMethod = std::function<bool(Message &)>;
40 
41 /**
42  * An exception if you want message to return a DBus error.
43  *
44  * In the registered property or method, you may throw this exception if a DBus
45  * error happens.
46  *
47  * E.g.
48  * @code
49  * throw dbus::MethodCallError("org.freedesktop.DBus.Error.InvalidArgs", ...);
50  * @endcode
51  */
52 class FCITXUTILS_EXPORT MethodCallError : public std::exception {
53 public:
54  MethodCallError(const char *name, const char *error)
55  : name_(name), error_(error) {}
56 
57  const char *what() const noexcept override { return error_.c_str(); }
58 
59  const char *name() const { return name_.c_str(); }
60 
61 private:
62  std::string name_;
63  std::string error_;
64 };
65 
66 /**
67  * An exception to not reply a the D-Bus method call.
68  *
69  * This can be useful if there is a cascade of method calls and you want to
70  * reply later.
71  */
72 class FCITXUTILS_EXPORT MethodCallNoReply : public std::exception {
73 public:
74  explicit MethodCallNoReply();
75 
76  const char *what() const noexcept override { return "MethodCallNoReply"; }
77 };
78 
79 class ObjectVTableMethodPrivate;
80 
81 /**
82  * Register a DBus method to current DBus VTable.
83  *
84  * Usually this class should not be used directly in the code.
85  *
86  * @see FCITX_OBJECT_VTABLE_METHOD
87  */
88 class FCITXUTILS_EXPORT ObjectVTableMethod {
89 public:
90  ObjectVTableMethod(ObjectVTableBase *vtable, const std::string &name,
91  const std::string &signature, const std::string &ret,
92  ObjectMethod handler);
93 
94  virtual ~ObjectVTableMethod();
95 
96  const std::string &name() const;
97  const std::string &signature() const;
98  const std::string &ret() const;
99  const ObjectMethod &handler() const;
100  ObjectVTableBase *vtable() const;
101 
102  /**
103  * Set a closure function to call the handler with in it.
104  *
105  * This is useful when you want to do something before and after the dbus
106  * message delivery.
107  *
108  * @param wrapper wrapper function.
109  */
110  void setClosureFunction(ObjectMethodClosure closure);
111 
112 private:
113  std::unique_ptr<ObjectVTableMethodPrivate> d_ptr;
114  FCITX_DECLARE_PRIVATE(ObjectVTableMethod);
115 };
116 
117 template <typename T>
119  using type = T;
120  type ret;
121 
122  template <typename U>
123  void call(U u) {
124  ret = u();
125  }
126 };
127 
128 template <>
129 struct ReturnValueHelper<void> {
130  using type = std::tuple<>;
131  type ret;
132  template <typename U>
133  void call(U u) {
134  u();
135  }
136 };
137 
138 /**
139  * Register a class member function as a DBus method.
140  *
141  * It will also check if the dbus signature matches the function type.
142  *
143  * @param FUNCTION a member function of the class
144  * @param FUNCTION_NAME a string of DBus method name
145  * @param SIGNATURE The dbus signature of arguments.
146  * @param RET The dbus signature of the return value.
147  *
148  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
149  */
150 #define FCITX_OBJECT_VTABLE_METHOD(FUNCTION, FUNCTION_NAME, SIGNATURE, RET) \
151  ::fcitx::dbus::ObjectVTableMethod FUNCTION##Method { \
152  this, FUNCTION_NAME, SIGNATURE, RET, \
153  ::fcitx::dbus::makeObjectVTablePropertyObjectMethodAdaptor< \
154  FCITX_STRING_TO_DBUS_TYPE(RET), \
155  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>( \
156  this, [this](auto &&...args) { \
157  return this->FUNCTION( \
158  std::forward<decltype(args)>(args)...); \
159  }) \
160  }
161 
162 /**
163  * Register a new DBus signal.
164  *
165  * This macro will define two new function, SIGNAL and SIGNALTo.
166  *
167  * The latter one will only be send to one DBus destination.
168  *
169  * @param SIGNAL will be used to define two member functions.
170  * @param SIGNAL_NAME a string of DBus signal name
171  * @param SIGNATURE The dbus signature of the signal.
172  *
173  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
174  */
175 #define FCITX_OBJECT_VTABLE_SIGNAL(SIGNAL, SIGNAL_NAME, SIGNATURE) \
176  ::fcitx::dbus::ObjectVTableSignal SIGNAL##Signal{this, SIGNAL_NAME, \
177  SIGNATURE}; \
178  using SIGNAL##ArgType = FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE); \
179  template <typename... Args> \
180  void SIGNAL(Args &&...args) { \
181  auto msg = SIGNAL##Signal.createSignal(); \
182  SIGNAL##ArgType tupleArg{std::forward<Args>(args)...}; \
183  msg << tupleArg; \
184  msg.send(); \
185  } \
186  template <typename... Args> \
187  void SIGNAL##To(const std::string &dest, Args &&...args) { \
188  auto msg = SIGNAL##Signal.createSignal(); \
189  msg.setDestination(dest); \
190  SIGNAL##ArgType tupleArg{std::forward<Args>(args)...}; \
191  msg << tupleArg; \
192  msg.send(); \
193  }
194 
195 /**
196  * Register a new DBus read-only property.
197  *
198  * @param PROPERTY will be used to define class member.
199  * @param NAME a string of DBus property name
200  * @param SIGNATURE The dbus signature of the property.
201  * @param GETMETHOD The method used to return the value of the property
202  *
203  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
204  */
205 #define FCITX_OBJECT_VTABLE_PROPERTY(PROPERTY, NAME, SIGNATURE, GETMETHOD, \
206  ...) \
207  ::fcitx::dbus::ObjectVTableProperty PROPERTY##Property{ \
208  this, NAME, SIGNATURE, \
209  ::fcitx::dbus::makeObjectVTablePropertyGetMethodAdaptor< \
210  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, GETMETHOD), \
211  ::fcitx::dbus::PropertyOptions{__VA_ARGS__}};
212 
213 /**
214  * Register a new DBus read-only property.
215  *
216  * @param PROPERTY will be used to define class member.
217  * @param NAME a string of DBus property name
218  * @param SIGNATURE The dbus signature of the property.
219  * @param GETMETHOD The method used to return the value of the property
220  * @param SETMETHOD The method used to update the value of the property
221  *
222  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
223  */
224 #define FCITX_OBJECT_VTABLE_WRITABLE_PROPERTY(PROPERTY, NAME, SIGNATURE, \
225  GETMETHOD, SETMETHOD, ...) \
226  ::fcitx::dbus::ObjectVTableWritableProperty PROPERTY##Property{ \
227  this, \
228  NAME, \
229  SIGNATURE, \
230  ::fcitx::dbus::makeObjectVTablePropertyGetMethodAdaptor< \
231  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, GETMETHOD), \
232  ::fcitx::dbus::makeObjectVTablePropertySetMethodAdaptor< \
233  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, SETMETHOD), \
234  ::fcitx::dbus::PropertyOptions{__VA_ARGS__}};
235 
236 class ObjectVTableSignalPrivate;
237 
238 /**
239  * Register a DBus signal to current DBus VTable.
240  *
241  * Usually this class should not be used directly in the code.
242  *
243  * @see FCITX_OBJECT_VTABLE_SIGNAL
244  */
245 class FCITXUTILS_EXPORT ObjectVTableSignal {
246 public:
247  ObjectVTableSignal(ObjectVTableBase *vtable, std::string name,
248  std::string signature);
249  virtual ~ObjectVTableSignal();
250 
251  Message createSignal();
252  const std::string &name() const;
253  const std::string &signature() const;
254 
255 private:
256  std::unique_ptr<ObjectVTableSignalPrivate> d_ptr;
257  FCITX_DECLARE_PRIVATE(ObjectVTableSignal);
258 };
259 
260 enum class PropertyOption : uint32_t { Hidden = (1 << 0) };
261 
263 
264 class ObjectVTablePropertyPrivate;
265 
266 /**
267  * Register a DBus read-only property to current DBus VTable.
268  *
269  * Usually this class should not be used directly in the code.
270  *
271  * @see FCITX_OBJECT_VTABLE_PROPERTY
272  */
273 class FCITXUTILS_EXPORT ObjectVTableProperty {
274 public:
275  ObjectVTableProperty(ObjectVTableBase *vtable, std::string name,
276  std::string signature, PropertyGetMethod getMethod,
277  PropertyOptions options);
278  virtual ~ObjectVTableProperty();
279 
280  const std::string &name() const;
281  const std::string &signature() const;
282  bool writable() const;
283  const PropertyGetMethod &getMethod() const;
284  const PropertyOptions &options() const;
285 
286 protected:
287  ObjectVTableProperty(std::unique_ptr<ObjectVTablePropertyPrivate> d);
288 
289  std::unique_ptr<ObjectVTablePropertyPrivate> d_ptr;
290  FCITX_DECLARE_PRIVATE(ObjectVTableProperty);
291 };
292 
293 /**
294  * Register a DBus property to current DBus VTable.
295  *
296  * Usually this class should not be used directly in the code.
297  *
298  * @see FCITX_OBJECT_VTABLE_WRITABLE_PROPERTY
299  */
300 class FCITXUTILS_EXPORT ObjectVTableWritableProperty
301  : public ObjectVTableProperty {
302 public:
303  ObjectVTableWritableProperty(ObjectVTableBase *vtable, std::string name,
304  std::string signature,
305  PropertyGetMethod getMethod,
306  PropertySetMethod setMethod,
307  PropertyOptions options);
308 
309  const PropertySetMethod &setMethod() const;
310 };
311 
313 class MessageSetter;
314 
315 class FCITXUTILS_EXPORT ObjectVTableBase
316  : public TrackableObject<ObjectVTableBase> {
317  friend class Bus;
318  friend class MessageSetter;
319 
320 public:
322  virtual ~ObjectVTableBase();
323 
324  void addMethod(ObjectVTableMethod *method);
325  void addSignal(ObjectVTableSignal *sig);
326  void addProperty(ObjectVTableProperty *property);
327 
328  /**
329  * Unregister the dbus object from the bus.
330  *
331  * The object will automatically unregister itself upon destruction. So this
332  * method should only be used if you want to temporarily remove a object
333  * from dbus.
334  */
335  void releaseSlot();
336 
337  /// Return the bus that the object is registered to.
338  Bus *bus();
339  Bus *bus() const;
340  /// Return whether this object is registered to a bus.
341  bool isRegistered() const;
342  /// Return the registered dbus object path of the object.
343  const std::string &path() const;
344  /// Return the registered dbus interface of the object.
345  const std::string &interface() const;
346 
347  /**
348  * Return the current dbus message for current method.
349  *
350  * This should only be used with in a registered callback.
351  *
352  * @return DBus message
353  */
354  Message *currentMessage() const;
355 
356  /**
357  * Set the current dbus message.
358  *
359  * This is only used by internal dbus class and not supposed to be used
360  * anywhere else.
361  *
362  * @param message current message.
363  */
364  void setCurrentMessage(Message *message);
365 
366  ObjectVTableMethod *findMethod(const std::string &name);
367  ObjectVTableProperty *findProperty(const std::string &name);
368 
369 protected:
370  virtual std::mutex &privateDataMutexForType() = 0;
371  virtual ObjectVTablePrivate *privateDataForType() = 0;
372  static std::shared_ptr<ObjectVTablePrivate> newSharedPrivateData();
373 
374 private:
375  void setSlot(Slot *slot);
376 
377  std::unique_ptr<ObjectVTableBasePrivate> d_ptr;
378  FCITX_DECLARE_PRIVATE(ObjectVTableBase);
379 };
380 
381 /**
382  * Base class of any DBus object.
383  *
384  * This should be used with curiously recurring template pattern. Like:
385  *
386  * @code
387  * class Object : public ObjectVTable<OBject> {};
388  * @endcode
389  *
390  * It will instantiate the related shared data for this type.
391  *
392  */
393 template <typename T>
395 public:
396  std::mutex &privateDataMutexForType() override {
397  return privateDataMutex();
398  }
399  ObjectVTablePrivate *privateDataForType() override { return privateData(); }
400  static std::mutex &privateDataMutex() {
401  static std::mutex mutex;
402  return mutex;
403  }
404  static ObjectVTablePrivate *privateData() {
405  static std::shared_ptr<ObjectVTablePrivate> d(newSharedPrivateData());
406  return d.get();
407  }
408 };
409 
410 template <typename Ret, typename Args, typename Callback>
412 public:
414  Callback callback)
415  : base_(base), callback_(std::move(callback)) {}
416 
417  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
419 
420  bool operator()(Message msg) {
421  base_->setCurrentMessage(&msg);
422  auto watcher = base_->watch();
423  Args args;
424  msg >> args;
425  try {
426  using ReturnType = decltype(callWithTuple(callback_, args));
427  static_assert(std::is_same<Ret, ReturnType>::value,
428  "Return type does not match.");
430  helper.call(
431  [this, &args]() { return callWithTuple(callback_, args); });
432  auto reply = msg.createReply();
433  reply << helper.ret;
434  reply.send();
435  } catch (const MethodCallError &error) {
436  auto reply = msg.createError(error.name(), error.what());
437  reply.send();
438  } catch (
439  const MethodCallNoReply &noReply) { // NOLINT(bugprone-empty-catch)
440  }
441  if (watcher.isValid()) {
442  watcher.get()->setCurrentMessage(nullptr);
443  }
444  return true;
445  }
446 
447 private:
448  ObjectVTableBase *base_;
449  Callback callback_;
450 };
451 
452 template <typename Ret, typename Args, typename Callback>
453 auto makeObjectVTablePropertyObjectMethodAdaptor(ObjectVTableBase *base,
454  Callback &&callback) {
456  base, std::forward<Callback>(callback));
457 }
458 
459 template <typename Ret, typename Callback>
461 public:
463  Callback callback)
464  : base_(base), callback_(std::move(callback)) {}
465 
466  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
468 
469  void operator()(Message &msg) {
470  Ret property = callback_();
471  msg << property;
472  }
473 
474 private:
475  ObjectVTableBase *base_;
476  Callback callback_;
477 };
478 
479 template <typename Ret, typename Callback>
480 auto makeObjectVTablePropertyGetMethodAdaptor(ObjectVTableBase *base,
481  Callback &&callback) {
483  base, std::forward<Callback>(callback));
484 }
485 
486 template <typename Ret, typename Callback>
488 public:
490  Callback callback)
491  : base_(base), callback_(std::move(callback)) {}
492 
493  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
495 
496  bool operator()(Message &msg) {
497  base_->setCurrentMessage(&msg);
498  auto watcher = base_->watch();
499  Ret args;
500  msg >> args;
501  callWithTuple(callback_, args);
502  auto reply = msg.createReply();
503  reply.send();
504  if (watcher.isValid()) {
505  watcher.get()->setCurrentMessage(nullptr);
506  }
507  return true;
508  }
509 
510 private:
511  ObjectVTableBase *base_;
512  Callback callback_;
513 };
514 
515 template <typename Ret, typename Callback>
516 auto makeObjectVTablePropertySetMethodAdaptor(ObjectVTableBase *base,
517  Callback &&callback) {
519  base, std::forward<Callback>(callback));
520 }
521 
522 } // namespace fcitx::dbus
523 
524 #endif // _FCITX_UTILS_DBUS_OBJECTVTABLE_H_
bool send()
Send this message.
Definition: message.cpp:261
Basic DBus type of a DBus message.
Definition: message.h:224
Base class of any DBus object.
Definition: objectvtable.h:394
Register a DBus read-only property to current DBus VTable.
Definition: objectvtable.h:273
Message createError(const char *name, const char *message) const
Create a error reply to this message.
Definition: message.cpp:62
Virtual base class represent some internal registration of the bus.
Definition: bus.h:33
Utitliy classes for statically tracking the life of a object.
A class that represents a connection to the Bus.
Definition: bus.h:51
API for DBus message.
Message createReply() const
Create a reply to this message.
Definition: message.cpp:53
Register a DBus property to current DBus VTable.
Definition: objectvtable.h:300
An exception if you want message to return a DBus error.
Definition: objectvtable.h:52
Helper class to be used with TrackableObjectReference.
Register a DBus method to current DBus VTable.
Definition: objectvtable.h:88
Class provides bit flag support for Enum.
Definition: flags.h:33
Helper template class to make easier to use type safe enum flags.
An exception to not reply a the D-Bus method call.
Definition: objectvtable.h:72
Register a DBus signal to current DBus VTable.
Definition: objectvtable.h:245