Как использовать интерфейс Emacs DBUS?

Я просмотрел пакет dbus, и кажется, что все функции встроены в исходный код C, и для них нет документации.

Как использовать функцию dbus-call-method?

Ответ 2

У меня была одна и та же проблема, и я нашел статью emacs-fu, которая появляется, когда поиск в Google слишком прост для моих нужд.

В частности, я хотел экспортировать свои собственные методы elisp через dbus и имел проблемы с пониманием терминологии dbus и того, как она применяется к интерфейсу emacs dbus.

Первое, что нужно проверить, документация emacs, C-h f dbus-register-method

dbus-register-method is a built-in function in `C source code'.

(dbus-register-method BUS SERVICE PATH INTERFACE METHOD HANDLER)

Register for method METHOD on the D-Bus BUS.

BUS is either the symbol `:system' or the symbol `:session'.

SERVICE is the D-Bus service name of the D-Bus object METHOD is
registered for.  It must be a known name.

PATH is the D-Bus object path SERVICE is registered.  INTERFACE is the
interface offered by SERVICE.  It must provide METHOD.  HANDLER is a
Lisp function to be called when a method call is received.  It must
accept the input arguments of METHOD.  The return value of HANDLER is
used for composing the returning D-Bus message.

BUS просто будет: session или: system (где вы, вероятно, почти всегда хотите использовать: session, как и настольное приложение, я полагаю).

СЕРВИС - это уникальное имя для приложения на шине, например адрес или доменное имя. Dbus.el определяет dbus-service-emacs как "org.gnu.Emacs".

PATH относится к различным типам приложений, что SERVICE относится к разным приложениям. Например, определенный модуль emacs может выставить функциональность в /PATH модуля в службе org.gnu.Emacs SERVICE.

ИНТЕРФЕЙС - это как интерфейс в программировании. Это спецификация, которая сообщает другим клиентам dbus, как взаимодействовать с объектом (объектами), которое предоставляет ваша приложение. Он содержит, например, подписи типов для ваших методов. Таким образом, у вас может быть интерфейс, который говорит что-то вроде: под сервисом org.gnu.Emacs в пути /ModuleName вы найдете метод с именем helloworld, который примет нулевые аргументы и вернет строку.

Трудная вещь для меня выяснить: как определить интерфейс для моего метода?

Выбирая dbus.el, вы обнаружите, что существует dbus-interface-introspectable (среди прочего), который содержит строку "org.freedesktop.DBus.Introspectable", которая называет стандартный интерфейс, который только предоставляет один метод:

org.freedesktop.DBus.Introspectable.Introspect (out STRING xml_data)

(ссылка на spec http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable)

И это метод, который вызывается клиентами, чтобы узнать, какие приложения выставляются на dbus. Поэтому мы можем использовать этот метод, чтобы посмотреть, как другие приложения рекламируют свои материалы на dbus, а затем мы можем реализовать собственный метод Introspect, просто подражая тем, что делают другие, и все будет хорошо.

Обратите внимание, однако, что спецификация говорит, что приложения могут реализовывать интерфейс Introspectable, им это не нужно. Фактически вы можете называть dbus-register-method просто отлично с пустой строкой в ​​качестве интерфейса (что-то будет похоже на это). Вы сможете вызвать свой метод. Однако у меня всегда возникали ошибки NoReply и проблемы с приложениями, висящими в ожидании ответа от dbus, который ушел, когда я понял, как сделать мои вещи интроспективными. Поэтому я предполагаю, что Introspect() ожидается довольно часто.

Итак, давайте сделаем следующее:

(defun say-world ()
  ;; you need to map between dbus and emacs datatypes, that what :string is for
  ;; if you're returning just one value that should work automatically, otherwise
  ;; you're expected to put your return values in a list like I am doing here
  (list :string "world"))

(dbus-register-method
  :session
  "org.test.emacs"
  "/helloworld"
  "org.test.emacs"
  "hello"
  'say-world)

Это то, что мы хотим реализовать, и поэтому хотим определить интерфейс для (с именем "org.test.emacs" ). Вы можете использовать его именно так и попытаться вызвать метод hello с помощью qdbus org.test.emacs /helloworld org.test.emacs.hello. Он должен работать, для меня он работает только после 20 секунд ожидания (что делает приложение зависает), но оно работает.

Теперь сделаем его интроспективным:

(defun dbus-test-slash-introspect ()
  "<node name='/'>
  <interface name='org.freedesktop.DBus.Introspectable'>
  <method name='Introspect'>
  <arg name='xml_data' type='s' direction='out'/>
  </method>
  </interface>
  <node name='helloworld'>
  </node>
  </node>")

(dbus-register-method
  :session
  "org.test.emacs"
  "/"
  dbus-interface-introspectable
  "Introspect"
  'dbus-test-slash-introspect)

(defun dbus-test-slash-helloworld-introspect ()
  "<node name='/helloworld'>
  <interface name='org.freedesktop.DBus.Introspectable'>
  <method name='Introspect'>
  <arg name='xml_data' type='s' direction='out'/>
  </method>
  </interface>
  <interface name='org.test.emacs'>
  <method name='hello'>
  <arg name='' direction='out' type='s' />
  </method>
  </interface>
  </node>")

(dbus-register-method
  :session
  "org.test.emacs"
  "/helloworld"
  dbus-interface-introspectable
  "Introspect"
  'dbus-test-slash-helloworld-introspect)

Там мы идем. Мы просто определяем два метода Introspect (по одному для каждого уровня нашего иерархического пути) и возвращаем написанный вручную xml, рассказывающий другим приложениям о пути /helloworld и методе hello внутри него. Обратите внимание, что dbus-test-slash-helloworld-introspect содержит <interface name="org.test.emacs">...</interface>, у которого есть сигнатура типа для нашего метода, то есть, насколько мне известно, определение интерфейса, которое мы использовали, когда мы зарегистрировали наш метод с dbus.

Оцените все это и сориентируйте с помощью qdbus:

~> qdbus org.test.emacs
/
/helloworld

~> qdbus org.test.emacs /
method QString org.freedesktop.DBus.Introspectable.Introspect()

~> qdbus org.test.emacs /helloworld
method QString org.freedesktop.DBus.Introspectable.Introspect()
method QString org.test.emacs.helloworld()

~> qdbus org.test.emacs /helloworld org.test.emacs.hello
world

Ура, работает так, как ожидалось, без зависания или ошибок NoReply.

Последнее, вы можете попробовать проверить свой метод следующим образом:

(dbus-call-method :session "org.test.emacs" "/helloworld" "org.test.emacs" "hello" :timeout 1000)

и обнаружите, что он просто перерывает время и удивляется, почему. Дело в том, что если вы зарегистрируетесь и вызовите метод из одного экземпляра emacs, то emacs будет ждать ответа. Нет никакой причудливой пронизывания, вы всегда получите ответ NoReply в этой ситуации.

Если вам нужно вызвать и зарегистрировать метод в одном экземпляре emacs, вы можете использовать dbus-call-method-asynchronously следующим образом:

(defun handle-hello (hello)
  (print hello))

(dbus-call-method-asynchronously :session "org.test.emacs" "/helloworld" "org.test.emacs" "hello" 'handle-hello)

Ответ 3

Вот безопасный способ тестирования возможностей dbus:

(defun dbus-capable ()
  "Check if dbus is available"
  (unwind-protect
      (let (retval)
        (condition-case ex
            (setq retval (dbus-ping :session "org.freedesktop.Notifications"))
          ('error
           (message (format "Error: %s - No dbus" ex))))
        retval)))

И вот способ отправки сообщения dbus:

(defun mbug-desktop-notification (summary body timeout icon)
  "call notification-daemon method METHOD with ARGS over dbus"
  (if (dbus-capable)
      (dbus-call-method
       :session                                 ; Session (not system) bus
       "org.freedesktop.Notifications"          ; Service name
       "/org/freedesktop/Notifications"         ; Service path
       "org.freedesktop.Notifications" "Notify" ; Method
       "emacs"
       0
       icon
       summary
       body
       '(:array)
       '(:array :signature "{sv}")
       ':int32 timeout)
    (message "Oh well, you're still notified")))

Ответ 4

Или просто оцените следующее в Emacs:

(info "(dbus)")