Как мне использовать интерфейс Emacs DBUS?
Я посмотрел пакет dbus, и кажется, что все функции встроены в исходный код C и для них нет документации.
Как я могу использовать dbus-call-method
функционировать?
4 ответа
Гугл в помощь... Перейдите по ссылке для примера, это не мой код, поэтому я не буду помещать его здесь.
http://emacs-fu.blogspot.com/2009/01/using-d-bus-example.html
У меня была та же проблема, и я нашел статью emacs-fu, которая появляется, когда поискать в Google немного слишком просто для моих нужд.
В частности, я хотел экспортировать свои собственные методы elisp через dbus, и у меня были проблемы с пониманием терминологии dbus и ее применения к интерфейсу emacs dbus.
Первое, что нужно проверить, документация по emacs, Ch 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 как настольное приложение, я полагаю).
SERVICE - это уникальное имя приложения на шине, например, адрес или доменное имя. Dbus.el определяет dbus-service-emacs
как "org.gnu.Emacs".
PATH предназначен для различных типов функциональных возможностей приложений, как SERVICE для различных приложений. Например, определенный модуль emacs может предоставлять функциональность в /ModuleName 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)
(ссылка на спецификацию http://dbus.freedesktop.org/doc/dbus-specification.html)
И это метод, который вызывается клиентами, чтобы выяснить, какие приложения выставляются на dbus. Таким образом, мы можем использовать этот метод, чтобы посмотреть, как другие приложения рекламируют свои вещи на dbus, а затем мы можем реализовать наш собственный метод Introspect, просто имитирующий то, что делают другие, и все будет хорошо.
Однако обратите внимание, что в спецификации сказано, что приложения могут реализовывать интроспективный интерфейс, они не обязаны это делать. На самом деле вы можете позвонить dbus-register-method
просто отлично с пустой строкой в качестве интерфейса (кажется, все будет делать). Вы сможете вызвать свой метод. Однако я всегда получал ошибки NoReply и проблемы с приложениями, зависшими в ожидании ответа от dbus, который исчез, когда я понял, как сделать свои вещи интроспективными. Поэтому я предполагаю, что Introspect() ожидается довольно часто.
Итак, давайте сделаем это:
(defun say-world ()
;; you need to map between dbus and emacs datatypes, that's 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)
Вот безопасный способ проверить возможности 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")))