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 class ObjectVTableMethodPrivate;
67 
68 /**
69  * Register a DBus method to current DBus VTable.
70  *
71  * Usually this class should not be used directly in the code.
72  *
73  * @see FCITX_OBJECT_VTABLE_METHOD
74  */
75 class FCITXUTILS_EXPORT ObjectVTableMethod {
76 public:
77  ObjectVTableMethod(ObjectVTableBase *vtable, const std::string &name,
78  const std::string &signature, const std::string &ret,
79  ObjectMethod handler);
80 
81  virtual ~ObjectVTableMethod();
82 
83  const std::string &name() const;
84  const std::string &signature() const;
85  const std::string &ret() const;
86  const ObjectMethod &handler() const;
87  ObjectVTableBase *vtable() const;
88 
89  /**
90  * Set a closure function to call the handler with in it.
91  *
92  * This is useful when you want to do something before and after the dbus
93  * message delivery.
94  *
95  * @param wrapper wrapper function.
96  */
97  void setClosureFunction(ObjectMethodClosure closure);
98 
99 private:
100  std::unique_ptr<ObjectVTableMethodPrivate> d_ptr;
101  FCITX_DECLARE_PRIVATE(ObjectVTableMethod);
102 };
103 
104 template <typename T>
106  using type = T;
107  type ret;
108 
109  template <typename U>
110  void call(U u) {
111  ret = u();
112  }
113 };
114 
115 template <>
116 struct ReturnValueHelper<void> {
117  using type = std::tuple<>;
118  type ret;
119  template <typename U>
120  void call(U u) {
121  u();
122  }
123 };
124 
125 /**
126  * Register a class member function as a DBus method.
127  *
128  * It will also check if the dbus signature matches the function type.
129  *
130  * @param FUNCTION a member function of the class
131  * @param FUNCTION_NAME a string of DBus method name
132  * @param SIGNATURE The dbus signature of arguments.
133  * @param RET The dbus signature of the return value.
134  *
135  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
136  */
137 #define FCITX_OBJECT_VTABLE_METHOD(FUNCTION, FUNCTION_NAME, SIGNATURE, RET) \
138  ::fcitx::dbus::ObjectVTableMethod FUNCTION##Method { \
139  this, FUNCTION_NAME, SIGNATURE, RET, \
140  ::fcitx::dbus::makeObjectVTablePropertyObjectMethodAdaptor< \
141  FCITX_STRING_TO_DBUS_TYPE(RET), \
142  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>( \
143  this, [this](auto &&...args) { \
144  return this->FUNCTION( \
145  std::forward<decltype(args)>(args)...); \
146  }) \
147  }
148 
149 /**
150  * Register a new DBus signal.
151  *
152  * This macro will define two new function, SIGNAL and SIGNALTo.
153  *
154  * The latter one will only be send to one DBus destination.
155  *
156  * @param SIGNAL will be used to define two member functions.
157  * @param SIGNAL_NAME a string of DBus signal name
158  * @param SIGNATURE The dbus signature of the signal.
159  *
160  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
161  */
162 #define FCITX_OBJECT_VTABLE_SIGNAL(SIGNAL, SIGNAL_NAME, SIGNATURE) \
163  ::fcitx::dbus::ObjectVTableSignal SIGNAL##Signal{this, SIGNAL_NAME, \
164  SIGNATURE}; \
165  using SIGNAL##ArgType = FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE); \
166  template <typename... Args> \
167  void SIGNAL(Args &&...args) { \
168  auto msg = SIGNAL##Signal.createSignal(); \
169  SIGNAL##ArgType tupleArg{std::forward<Args>(args)...}; \
170  msg << tupleArg; \
171  msg.send(); \
172  } \
173  template <typename... Args> \
174  void SIGNAL##To(const std::string &dest, Args &&...args) { \
175  auto msg = SIGNAL##Signal.createSignal(); \
176  msg.setDestination(dest); \
177  SIGNAL##ArgType tupleArg{std::forward<Args>(args)...}; \
178  msg << tupleArg; \
179  msg.send(); \
180  }
181 
182 /**
183  * Register a new DBus read-only property.
184  *
185  * @param PROPERTY will be used to define class member.
186  * @param NAME a string of DBus property name
187  * @param SIGNATURE The dbus signature of the property.
188  * @param GETMETHOD The method used to return the value of the property
189  *
190  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
191  */
192 #define FCITX_OBJECT_VTABLE_PROPERTY(PROPERTY, NAME, SIGNATURE, GETMETHOD, \
193  ...) \
194  ::fcitx::dbus::ObjectVTableProperty PROPERTY##Property{ \
195  this, NAME, SIGNATURE, \
196  ::fcitx::dbus::makeObjectVTablePropertyGetMethodAdaptor< \
197  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, GETMETHOD), \
198  ::fcitx::dbus::PropertyOptions{__VA_ARGS__}};
199 
200 /**
201  * Register a new DBus read-only property.
202  *
203  * @param PROPERTY will be used to define class member.
204  * @param NAME a string of DBus property name
205  * @param SIGNATURE The dbus signature of the property.
206  * @param GETMETHOD The method used to return the value of the property
207  * @param SETMETHOD The method used to update the value of the property
208  *
209  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
210  */
211 #define FCITX_OBJECT_VTABLE_WRITABLE_PROPERTY(PROPERTY, NAME, SIGNATURE, \
212  GETMETHOD, SETMETHOD, ...) \
213  ::fcitx::dbus::ObjectVTableWritableProperty PROPERTY##Property{ \
214  this, \
215  NAME, \
216  SIGNATURE, \
217  ::fcitx::dbus::makeObjectVTablePropertyGetMethodAdaptor< \
218  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, GETMETHOD), \
219  ::fcitx::dbus::makeObjectVTablePropertySetMethodAdaptor< \
220  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, SETMETHOD), \
221  ::fcitx::dbus::PropertyOptions{__VA_ARGS__}};
222 
223 class ObjectVTableSignalPrivate;
224 
225 /**
226  * Register a DBus signal to current DBus VTable.
227  *
228  * Usually this class should not be used directly in the code.
229  *
230  * @see FCITX_OBJECT_VTABLE_SIGNAL
231  */
232 class FCITXUTILS_EXPORT ObjectVTableSignal {
233 public:
234  ObjectVTableSignal(ObjectVTableBase *vtable, std::string name,
235  std::string signature);
236  virtual ~ObjectVTableSignal();
237 
238  Message createSignal();
239  const std::string &name() const;
240  const std::string &signature() const;
241 
242 private:
243  std::unique_ptr<ObjectVTableSignalPrivate> d_ptr;
244  FCITX_DECLARE_PRIVATE(ObjectVTableSignal);
245 };
246 
247 enum class PropertyOption : uint32_t { Hidden = (1 << 0) };
248 
250 
251 class ObjectVTablePropertyPrivate;
252 
253 /**
254  * Register a DBus read-only property to current DBus VTable.
255  *
256  * Usually this class should not be used directly in the code.
257  *
258  * @see FCITX_OBJECT_VTABLE_PROPERTY
259  */
260 class FCITXUTILS_EXPORT ObjectVTableProperty {
261 public:
262  ObjectVTableProperty(ObjectVTableBase *vtable, std::string name,
263  std::string signature, PropertyGetMethod getMethod,
264  PropertyOptions options);
265  virtual ~ObjectVTableProperty();
266 
267  const std::string &name() const;
268  const std::string &signature() const;
269  bool writable() const;
270  const PropertyGetMethod &getMethod() const;
271  const PropertyOptions &options() const;
272 
273 protected:
274  ObjectVTableProperty(std::unique_ptr<ObjectVTablePropertyPrivate> d);
275 
276  std::unique_ptr<ObjectVTablePropertyPrivate> d_ptr;
277  FCITX_DECLARE_PRIVATE(ObjectVTableProperty);
278 };
279 
280 /**
281  * Register a DBus property to current DBus VTable.
282  *
283  * Usually this class should not be used directly in the code.
284  *
285  * @see FCITX_OBJECT_VTABLE_WRITABLE_PROPERTY
286  */
287 class FCITXUTILS_EXPORT ObjectVTableWritableProperty
288  : public ObjectVTableProperty {
289 public:
290  ObjectVTableWritableProperty(ObjectVTableBase *vtable, std::string name,
291  std::string signature,
292  PropertyGetMethod getMethod,
293  PropertySetMethod setMethod,
294  PropertyOptions options);
295 
296  const PropertySetMethod &setMethod() const;
297 };
298 
300 class MessageSetter;
301 
302 class FCITXUTILS_EXPORT ObjectVTableBase
303  : public TrackableObject<ObjectVTableBase> {
304  friend class Bus;
305  friend class MessageSetter;
306 
307 public:
309  virtual ~ObjectVTableBase();
310 
311  void addMethod(ObjectVTableMethod *method);
312  void addSignal(ObjectVTableSignal *sig);
313  void addProperty(ObjectVTableProperty *property);
314 
315  /**
316  * Unregister the dbus object from the bus.
317  *
318  * The object will automatically unregister itself upon destruction. So this
319  * method should only be used if you want to temporarily remove a object
320  * from dbus.
321  */
322  void releaseSlot();
323 
324  /// Return the bus that the object is registered to.
325  Bus *bus();
326  Bus *bus() const;
327  /// Return whether this object is registered to a bus.
328  bool isRegistered() const;
329  /// Return the registered dbus object path of the object.
330  const std::string &path() const;
331  /// Return the registered dbus interface of the object.
332  const std::string &interface() const;
333 
334  /**
335  * Return the current dbus message for current method.
336  *
337  * This should only be used with in a registered callback.
338  *
339  * @return DBus message
340  */
341  Message *currentMessage() const;
342 
343  /**
344  * Set the current dbus message.
345  *
346  * This is only used by internal dbus class and not supposed to be used
347  * anywhere else.
348  *
349  * @param message current message.
350  */
351  void setCurrentMessage(Message *message);
352 
353  ObjectVTableMethod *findMethod(const std::string &name);
354  ObjectVTableProperty *findProperty(const std::string &name);
355 
356 protected:
357  virtual std::mutex &privateDataMutexForType() = 0;
358  virtual ObjectVTablePrivate *privateDataForType() = 0;
359  static std::shared_ptr<ObjectVTablePrivate> newSharedPrivateData();
360 
361 private:
362  void setSlot(Slot *slot);
363 
364  std::unique_ptr<ObjectVTableBasePrivate> d_ptr;
365  FCITX_DECLARE_PRIVATE(ObjectVTableBase);
366 };
367 
368 /**
369  * Base class of any DBus object.
370  *
371  * This should be used with curiously recurring template pattern. Like:
372  *
373  * @code
374  * class Object : public ObjectVTable<OBject> {};
375  * @endcode
376  *
377  * It will instantiate the related shared data for this type.
378  *
379  */
380 template <typename T>
382 public:
383  std::mutex &privateDataMutexForType() override {
384  return privateDataMutex();
385  }
386  ObjectVTablePrivate *privateDataForType() override { return privateData(); }
387  static std::mutex &privateDataMutex() {
388  static std::mutex mutex;
389  return mutex;
390  }
391  static ObjectVTablePrivate *privateData() {
392  static std::shared_ptr<ObjectVTablePrivate> d(newSharedPrivateData());
393  return d.get();
394  }
395 };
396 
397 template <typename Ret, typename Args, typename Callback>
399 public:
401  Callback callback)
402  : base_(base), callback_(std::move(callback)) {}
403 
404  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
406 
407  bool operator()(Message msg) {
408  base_->setCurrentMessage(&msg);
409  auto watcher = base_->watch();
410  Args args;
411  msg >> args;
412  try {
413  using ReturnType = decltype(callWithTuple(callback_, args));
414  static_assert(std::is_same<Ret, ReturnType>::value,
415  "Return type does not match.");
417  helper.call(
418  [this, &args]() { return callWithTuple(callback_, args); });
419  auto reply = msg.createReply();
420  reply << helper.ret;
421  reply.send();
422  } catch (const ::fcitx::dbus::MethodCallError &error) {
423  auto reply = msg.createError(error.name(), error.what());
424  reply.send();
425  }
426  if (watcher.isValid()) {
427  watcher.get()->setCurrentMessage(nullptr);
428  }
429  return true;
430  }
431 
432 private:
433  ObjectVTableBase *base_;
434  Callback callback_;
435 };
436 
437 template <typename Ret, typename Args, typename Callback>
438 auto makeObjectVTablePropertyObjectMethodAdaptor(ObjectVTableBase *base,
439  Callback &&callback) {
441  base, std::forward<Callback>(callback));
442 }
443 
444 template <typename Ret, typename Callback>
446 public:
448  Callback callback)
449  : base_(base), callback_(std::move(callback)) {}
450 
451  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
453 
454  void operator()(Message &msg) {
455  Ret property = callback_();
456  msg << property;
457  }
458 
459 private:
460  ObjectVTableBase *base_;
461  Callback callback_;
462 };
463 
464 template <typename Ret, typename Callback>
465 auto makeObjectVTablePropertyGetMethodAdaptor(ObjectVTableBase *base,
466  Callback &&callback) {
468  base, std::forward<Callback>(callback));
469 }
470 
471 template <typename Ret, typename Callback>
473 public:
475  Callback callback)
476  : base_(base), callback_(std::move(callback)) {}
477 
478  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
480 
481  bool operator()(Message &msg) {
482  base_->setCurrentMessage(&msg);
483  auto watcher = base_->watch();
484  Ret args;
485  msg >> args;
486  callWithTuple(callback_, args);
487  auto reply = msg.createReply();
488  reply.send();
489  if (watcher.isValid()) {
490  watcher.get()->setCurrentMessage(nullptr);
491  }
492  return true;
493  }
494 
495 private:
496  ObjectVTableBase *base_;
497  Callback callback_;
498 };
499 
500 template <typename Ret, typename Callback>
501 auto makeObjectVTablePropertySetMethodAdaptor(ObjectVTableBase *base,
502  Callback &&callback) {
504  base, std::forward<Callback>(callback));
505 }
506 
507 } // namespace fcitx::dbus
508 
509 #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:381
Register a DBus read-only property to current DBus VTable.
Definition: objectvtable.h:260
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:287
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:75
Class provides bit flag support for Enum.
Definition: flags.h:33
Helper template class to make easier to use type safe enum flags.
Register a DBus signal to current DBus VTable.
Definition: objectvtable.h:232