У меня была та же проблема, и я нашел статью 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#standard-interfaces-introspectable)
И это методкоторый вызывается клиентами для выяснения того, какие приложения выставляются на dbus, поэтому мы можем использовать этот метод, чтобы посмотреть, как другие приложения рекламируют свои вещи на dbus, а затем мы можем реализовать наш собственный метод Introspect, просто имитирующий то, что делают другиеи все будет хорошо.
Обратите внимание, однако, что спецификация говорит, что приложения могут реализовать IntrВероятный интерфейс, они не должны.На самом деле вы можете просто вызвать 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)
Это то, что мы хотим реализовать и поэтому хотим определить интерфейс для (named)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.
Evaluateвсе это и поэкспериментируйте с 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)