unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Inclusion of dbus-proxy
@ 2010-08-21 22:03 Jan Moringen
  2010-08-22 14:47 ` joakim
  2010-08-22 16:20 ` Michael Albinus
  0 siblings, 2 replies; 12+ messages in thread
From: Jan Moringen @ 2010-08-21 22:03 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 2355 bytes --]

Hi,

I recently developed dbus-proxy, a framework that makes accessing remote
D-Bus objects from Emacs Lisp easier and more transparent than the
current `dbus-call-method' mechanism. Since I was encouraged to propose
it for inclusion in Emacs, I'm hereby doing so. Besides being attached,
the code is also available here:
http://bazaar.launchpad.net/~scymtym/+junk/dbus-proxy/files

Let me start with a simple example of how a typical usage of dbus-proxy
looks:

(let ((device-kit (dbus-proxy-make-remote-proxy
                   :system
                   "org.freedesktop.DeviceKit"
                   "/org/freedesktop/DeviceKit")))

  ;; Retrieve the daemon-version property.
  (slot-value device-kit :daemon-version)
  (oref device-kit :daemon-version)

  ;; Connect to the device-event signal.
  (connect device-kit 'device-event
    (lambda (&rest args)
      (message "Device event %s" args)))

  ;; Enumerate subsystems.
  (enumerate-by-subsystem device-kit '("sound")))

This example creates a proxy object for the device kit D-Bus object and
uses its methods, properties and signals.

dbus-proxy uses D-Bus introspection information to dynamically create
classes and methods that reflect the structure of the remote interface
in Emacs Lisp as naturally as possible. For example, `describe-class'
can used to inspect the methods supported by a particular D-Bus object.

dbus-proxy consists of the following components:

+ dbus-proxy.el
  + public programming interface
  + interface analysis
  + proxy superclasses
  + code generation
  + unit tests
+ dbus-introspection.el
  + accessors for D-Bus introspection data
    (similar to those in dbus.el)
  + parsing of signature strings
  + unit tests

I am aware of the following problems with respect to the inclusion in
Emacs:
+ Names:
  + Generated class names tend to be long and ugly and do not follow 
    usual Lisp conventions
  + `connect' and `disconnect' may need a `dbus-proxy-' prefix?
+ The generated class hierarchies only work with the :c3 method 
  resolution order which was added to EIEIO upstream a few months ago 
  but does not seem to have been merged yet
+ The unit tests use ert which is also not currently included in Emacs
+ The use of the cl library may or may not be acceptable

I have signed the copyright assignment for Emacs.

What do you think?

Kind regards,
Jan

[-- Attachment #2: ChangeLog --]
[-- Type: text/x-changelog, Size: 14373 bytes --]

2010-08-21  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Added tests for type parsers in dbus-introspection.el
	* dbus-introspection.el (header): added version 0.2 to history
	section
	(test dbus-introspection-test-parse-simple-type-smoke): new test;
	smoke test for `dbus-proxy-parse-simple-type'
	(test dbus-introspection-test-parse-composite-type-smoke): new test;
	smoke test for `dbus-proxy-parse-composite-type'
	(test dbus-introspection-test-parse-type-list-smoke): new test;
	smoke test for `dbus-proxy-parse-type-list'

	Support parsing of lists of types in dbus-introspection.el
	* dbus-introspection.el (dbus-proxy-parse-composite-type): do not
	encapsulate the single subtype of an array in a list; call
	`dbus-proxy-parse-type-list' when multiple subtypes are allowed
	(dbus-proxy-parse-type-list): new function; parse a string
	containing a list of type designators

	Improved dbus-proxy-parse-composite-type in dbus-introspection.el
	* dbus-introspection.el (dbus-proxy-parse-composite-type): made
	this work properly in most cases; removed handling of variant type
	which is not really a composite type; added documentation string

	Better error handling for simple types in dbus-introspection.el
	* dbus-introspection.el (malformed-signature): new condition
	symbol; this condition is signaled when parsing of a type
	signature fails
	(dbus-proxy-parse-simple-type): signal `malformed-signature'
	instead of using `error'; added documentation string; cleanup

	Added test dbus-proxy-test-gdm in dbus-proxy.el
	* dbus-proxy.el (test dbus-proxy-test-gdm): new test; added
	because the Gdm object has methods that return object paths

	Changed DBus -> D-Bus in all comments in dbus-proxy.el
	* dbus-proxy.el: changed DBus -> D-Bus in all comments

2010-08-15  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Added unit test for interaction with rhythmbox in dbus-proxy.el
	* dbus-proxy.el (test dbus-proxy-test-rhythmbox): new test; test
	some interaction with the rhythmbox application

	Removed resolved TODO tags in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-interface-class): removed TODO
	tag about defining the condition symbols

	Added unit test for connecting signals in dbus-proxy.el
	* dbus-proxy.el (test dbus-proxy-test-connect): new test; test
	connecting DBus signals to handlers

	Test constructing proxy for system bus object in dbus-proxy.el
	* dbus-proxy.el (test dbus-proxy-test-make-remote-proxy): also
	test constructing a proxy for an object that resides on the system
	bus

	Added conditions no-such-{property, signal} in dbus-proxy.el
	* dbus-proxy.el (condition no-such-property): new condition;
	signaled when a specified property cannot be found
	(condition no-such-signal): new condition; signaled when a
	specified signal cannot be found

	Added TODO which contains a list of unresolved issues
	* TODO: new file; list of unresolved issues

2010-08-13  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Extended unit test in dbus-proxy.el
	* dbus-proxy.el (test dbus-proxy-test-mission-control): improved
	comment
	(test dbus-proxy-test-devicekit): improved tests for slot access;
	test connecting signals

	Signal connect and disconnect methods in dbus-proxy.el
	* dbus-proxy.el (header): bumped version to 0.3 section; extended
	description in commentary section to cover handling of properties
	and signals; added version 0.3 to history section
	(dbus-proxy-remote-object::connect): new method; connect a handler
	to a DBus signal
	(dbus-proxy-remote-object::disconnect): new method; disconnect a
	handler from a DBus signal
	(dbus-proxy-make-method): cosmetic changes in logging
	(dbus-proxy-make-interface-class): generate
	`dbus-proxy-find-signal' method for the interface class; cosmetic
	changes in logging
	(dbus-proxy-make-object-class): cosmetic changes in logging

	Find slot properties using stored interface info in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-remote-object::slot-missing): moved
	into this class from `dbus-proxy-interface-object'; use
	`dbus-proxy-find-property' to find interface name and property
	name
	(dbus-proxy-interface-object::slot-missing): moved to
	`dbus-proxy-remote-object' class
	(dbus-proxy-make-interface-class): generate
	`dbus-proxy-find-property' method for the interface class

2010-08-12  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Store properties and signals in interface slots in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-interface-object::properties): new
	slot; list of properties in the interface
	(dbus-proxy-interface-object::signals): new slot; list of signals
	in the interface
	(dbus-proxy-transform-property-name-function): new variable; the
	value is a function that transforms DBus property names into
	suitable slot names
	(dbus-proxy-transform-signal-name-function): new variable; the
	value is a function that transforms DBus signal names into
	suitable signal names
	(dbus-proxy-make-property-name): new function; transform a DBus
	property name into a slot name
	(dbus-proxy-make-signal-name): new function; transform a DBus
	signal name into a lispy signal name
	(dbus-proxy-make-interface-class): compute and install values for
	properties and signals slots

	Reanimated signal {,dis}connect methods in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-remote-object::connect): renamed
	`dbus-proxy-connect' -> `connect'; reanimated as stub
	(dbus-proxy-remote-object::disconnect): renamed
	`dbus-proxy-disconnect' -> `disconnect'; reanimated as stub

	Increased sophistication of method name transform in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-transform-method-name-function):
	changed value to `dbus-proxy-transform-method-name'
	(dbus-proxy-transform-method-name): new function; transform method
	names using `dbus-proxy-transform-camel-case' and additional rules
	(dbus-proxy-transform-camel-case): improved documentation string
	(test dbus-proxy-test-transform-camel-case-smoke): added more
	cases

	Added property introspection functions in dbus-introspection.el
	* dbus-introspection.el (dbus-property-p): new function; predicate
	for property introspection elements
	(dbus-property-name): new function; name accessor for property
	introspection elements
	(dbus-property-type): new function; type accessor for property
	introspection elements
	(dbus-property-access): new function; access accessor for property
	introspection elements
	(dbus-interface-properties): new function; return property
	elements of an introspection interface

2010-08-11  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Added some comments in dbus-introspection.el
	* dbus-introspection.el: added some comments

	Added new introspection accessors in dbus-introspection.el
	* dbus-introspection.el (dbus-signal-name): new method; return
	name of a signal introspection element
	(dbus-interface-methods): new method; return method elements of an
	interface element
	(dbus-interface-signals): new method; return signal elements of an
	introspection element

	Added docstring to  -simple-type-codes in dbus-introspection.el
	* dbus-introspection.el (dbus-proxy-simple-type-codes): added
	documentation string

	Added variant type to -simple-type-codes in dbus-introspection.el
	* dbus-introspection.el (dbus-proxy-simple-type-codes): added code
	for variant type

2010-08-10  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Generate type specifiers in DBus method calls in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-method): call
	`dbus-proxy-make-call-args' to construct call arguments
	(dbus-proxy-make-call-args): new function; construct typed call
	arguments based on DBus argument specification if possible
	(dbus-proxy-test-make-call-arg-smoke): new unit test; smoke test
	for `dbus-proxy-make-call-args'

	Added function dbus-arg-type in dbus-introspection.el
	* dbus-introspection.el (dbus-proxy-simple-type-codes): added type
	?d
	(dbus-arg-type): new function; return type of DBus argument

2010-08-09  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Add documentation strings to generated classes in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-object-class): generate a simple
	documentation string for the generated class

2010-08-08  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Added messages to code generating functions in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-method): added a message
	(dbus-proxy-make-interface-class): likewise
	(dbus-proxy-make-object-class): likewise

	Make redefinition actual work in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-remote-proxy): added optional
	argument redefine-classes
	(dbus-proxy-make-method-name): use
	`dbus-proxy-symbol-unsuitable-for-method' instead of just
	`fboundp'
	(dbus-proxy-make-object-class): added optional argument
	redefine-interface-classes
	(dbus-proxy-ensure-object-class): pass redefine argument to
	`dbus-proxy-make-object-class'
	(dbus-proxy-symbol-unsuitable-for-method): new function; check
	whether a method can be installed on a symbol

2010-08-07  Jan Moringen  <jmoringe@techfak.uni-bielefeld.de>

	Added and improved docstrings in dbus-proxy.el
	(dbus-proxy-interface-object::slot-missing): added documentation string
	(dbus-proxy-interface-object::object-print): likewise
	(dbus-proxy-transform-method-name-function): likewise
	(test dbus-proxy-test-transform-camel-case-smoke): refer to
	correct function in documentation string

	Added a method name transformation facility in dbus-proxy.el
	* dbus-proxy.el (header): added version field; extended and
	adapted algorithm description in commentary section; bumped
	version to 0.2 in version section
	(dbus-proxy-transform-method-name-function): new variable;
	function used to transform DBus method names into corresponding
	lisp function names
	(dbus-proxy-make-method-name): new function; transforms a DBus
	method name into a lisp method name by applying
	`dbus-proxy-transform-method-name-function' and uniquifying the
	result
	(dbus-proxy-make-method): call `dbus-proxy-make-method-name' to
	obtain the name of the new method
	(dbus-proxy-transform-camel-case): new function; default name
	transformation function
	(test dbus-proxy-test-transform-camel-case-smoke): new unit test;
	test basic behavior of `dbus-proxy-transform-camel-case'
	(test dbus-proxy-test-dbus): expect transformed method names
	(test dbus-proxy-test-mission-control): likewise
	(test dbus-proxy-test-epiphany): likewise
	(test dbus-proxy-test-devicekit): likewise

	Added and improved  unit tests in dbus-proxy.el
	* dbus-proxy.el (test dbus-proxy-test-make-remote-proxy):
	commented out expected error since we cannot implement this right
	now
	(test dbus-proxy-test-dbus): renamed `dbus-proxy-test-bus-proxy'
	-> `dbus-proxy-test-dbus'
	(test dbus-proxy-test-mission-control): new unit test; call some
	methods on the mission control object

	Added new unit tests in dbus-proxy.el
	* dbus-proxy.el (test dbus-proxy-test-bus-proxy): removed; split
	into dbus-proxy-test-bus-proxy, dbus-proxy-test-epiphany and
	dbus-proxy-test-devicekit
	(dbus-proxy-test-make-remote-proxy): new unit test; tests for
	`dbus-proxy-make-remote-proxy'
	(test dbus-proxy-test-bus-proxy): new unit test; call some methods
	on the DBus object
	(test dbus-proxy-test-epiphany): new unit test; call some methods
	on the epiphany browser object
	(test dbus-proxy-test-devicekit): new unit test; call some methods
	on the devicekit object

	Added a comment in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-get-object-class): added a comment

	Improved implementations of code generators in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-method): improved implementation;
	added documentation string
	(dbus-proxy-make-interface-class): improved implementation
	(dbus-proxy-make-object-class): improved implementation

	Use -ensure-object-class in -make-remote-proxy in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-remote-proxy): use
	`dbus-proxy-ensure-object-class' instead of -get and -make

	Reorganized getting, creating, ensuring of things in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-get-interface-class): moved
	(dbus-proxy-ensure-interface-class): new function; find interface
	class, creating it if necessary
	(dbus-proxy-get-object-class): moved
	(dbus-proxy-get-interface-class): new function; find object class,
	creating it if necessary

	Reordering in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-interface-class-symbol): moved to
	interface section
	(dbus-proxy-make-object-class-symbol): moved to object section

	Smarter conditional definition of unit tests in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-interface-class): added some
	comments
	(unit tests): require ert without error to conditionally define
	unit tests

	Added object-print method for interface objects in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-interface-object::object-print): new
	method; return string representation of the interface object
	(call): removed; obsolete
	(connect): renamed `connect' -> `dbus-proxy-connect'; commented
	out for now

	Fixed proxy creation in unit tests in dbus-proxy.el
	* dbus-proxy.el (unit tests): fixed calls to
	`dbus-proxy-make-remote-proxy'

	Renamed object class and added interface class in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-remote-object): renamed `dbus-proxy'
	-> `dbus-proxy-remote-object'
	(dbus-proxy-interface-object): new class; represents DBus
	interfaces
	(dbus-proxy-interface-object::slot-missing): now specialized on
	`dbus-proxy-interface-object'; cleanup; added comments
	(dbus-proxy-make-interface-class): interface class are subclasses
	of `dbus-proxy-interface object'

	Added docstrings to class dbus-proxy in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy::service): added documentation string
	(dbus-proxy::object): likewise
	(dbus-proxy): improved documentation string

	Removed obsolete eval-backup in dbus-proxy.el
	* dbus-proxy.el (dbus-proxy-make-remote-proxy): comment changes
	(eval-backup): removed

	Renamed dbus-proxy-make-remote-proxy in dbus-proxy.el
	* dbus-proxy.el (header): updated copyright; updated commentary
	and history sections
	(dbus-proxy-make-remote-proxy): renamed `dbus-proxy-make-proxy' ->
	`dbus-proxy-make-remote-proxy'

	Minor cosmetic improvements in dbus-introspection.el
	* dbus-introspection.el (header): added one-line description;
	updated copyright; added keywords; added contents to commentary
	and history sections

[-- Attachment #3: dbus-introspection.el --]
[-- Type: text/x-emacs-lisp, Size: 10326 bytes --]

;;; dbus-introspection.el --- Helper functions for D-Bus introspection
;;
;; Copyright (C) 2009, 2010 Jan Moringen
;;
;; Author: Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
;; Keywords: dbus, ipc
;; Version: 0.1
;; X-RCS: $Id:$
;;
;; This Program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This Program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses>.

\f
;;; Commentary:
;;
;; Helper functions for the `dbus-proxy' library.

\f
;;; History:
;;
;; 0.2 - Parsing of complex signatures
;;
;; 0.1 - Initial version

\f
;;; Code:
;;

(require 'dbus)

\f
;;; Error Conditions
;;

;; malformed-signature

(intern "malformed-signature")

(put 'malformed-signature 'error-conditions
     '(error malformed-signature))

(put 'malformed-signature 'error-message
     "Malformed signature signature")

\f
;;;
;;

(defconst dbus-proxy-simple-type-codes
  '((?b . :boolean)
    (?y . :byte)
    (?n . :int16)
    (?q . :uint16)
    (?i . :int32)
    (?u . :uint32)
    (?x . :int64)
    (?t . :uint64)
    (?d . :double)
    (?s . :string)
    (?o . :object-path)
    (?g . :signature)
    (?v . :variant))
  "Mapping type indication characters to type keywords")

(defun dbus-proxy-parse-simple-type (string)
  "Parse (a substring of) STRING as a simple type designator.
The returned value is of the form (TYPE CONSUMED) where the
number of consumed characters is always one. If STRING is not
recognized as starting with a simple type designator, a
`malformed-signature' error condition is signaled.
A list of recognized simple type designators is available in the
variable `dbus-proxy-simple-type-codes."
  (let ((type (cdr (assoc (aref string 0)
			  dbus-proxy-simple-type-codes))))
    (if type
	(list type 1)
      (signal 'malformed-signature
	      (list "Unknown simple type designator" string)))))

(defun dbus-proxy-parse-composite-type (string)
  "Parse (a substring of) STRING as composite type designator.
A composite type is a simple type or one of struct, array or
dict-entry. The returned value is of the form (TYPE CONSUMED)
where the number of consumed characters is at least one but
potentially less than the length of STRING. TYPE is a list
starting with a type keyword:
\(SIMPLE\) for SIMPLE one of the cdrs in `dbus-proxy-simple-type-codes'
\(:struct (SUBSTYPE1 SUBTYPE2 ...)\)
\(:array SUBTYPE\)
\(:dict-entry (KEYTYPE VALUETYPE)\)"
  (destructuring-bind (outer inner consumed)
      (case (aref string 0)
	;; Closing delimiters are consumed without generating
	;; anything.
	((?\) ?})
	 (list nil nil 1))

	;; Struct type
	((?r ?\()
	 (destructuring-bind (types consumed)
	     (dbus-proxy-parse-type-list (substring string 1))
	   (unless (= (aref string consumed) ?\)) ;; TODO check end of string
	     (signal 'malformed-signature
		     (list "struct misses closing parenthesis")))
	   `(:struct ,types ,consumed)))

	;; Array type
	(?a
	 `(:array ,@(dbus-proxy-parse-composite-type
		     (substring string 1))))

	;; Dict entry type.
	;;
	;; From the D-Bus spec: The restrictions are: it occurs only as
	;; an array element type; it has exactly two single complete
	;; types inside the curly braces; the first single complete
	;; type (the "key") must be a basic type rather than a
	;; container type.
	((?e  ?{)
	 (destructuring-bind (types consumed)
	     (dbus-proxy-parse-type-list (substring string 1))
	   (unless (= (length types) 2)
	     (signal 'malformed-signature
		     (list "dict entry has to contain exactly two types")))
	   (unless (= (aref string consumed) ?}) ;; TODO check end of string
	     (signal 'malformed-signature
		     (list "dict entry misses closing curly brace")))
	   `(:dict-entry ,types ,consumed)))

	(t
	 `(nil ,@(dbus-proxy-parse-simple-type string))))

    ;; Return the parsed type as a list of the form (TYPE CONSUMED).
    (cond
     (outer
      (list (list outer inner) (1+ consumed)))
     (inner
      (list inner consumed))
     (t
      (list nil consumed)))))

(defun dbus-proxy-parse-type-list (string)
  "Parse STRING as a list of type designators.
The returned value is of the form (TYPES CONSUMED). Where
consumed is equal to the length of STRING. TYPES is a list of
types
\(TYPE1 TYPE2 ...\)
where each element is of the form produced by
`dbus-proxy-parse-type-list'."
  (let ((remaining string)
	(all-consumed 0)
	(all-types))
    (catch 'early
      (while (> (length remaining) 0)
	(destructuring-bind (type consumed)
	    (dbus-proxy-parse-composite-type remaining)
	  (setq remaining (substring remaining consumed))
	  (incf all-consumed consumed)
	  (if type
	      (push type all-types)
	    (throw 'early nil)))))
    (list (reverse all-types) all-consumed)))

\f
;;; Predicates and accessors for dbus types.
;;

;; Argument introspection elements

(defsubst dbus-arg-p (element)
  (eq (car-safe element) 'arg))

(defsubst dbus-arg-name (arg)
  (cdr (assoc 'name (second arg))))

(defsubst dbus-arg-type (arg)
  (cdr (assoc 'type (second arg))))

(defsubst dbus-arg-in-p (arg)
  (string= (cdr (assoc 'direction (second arg))) "in"))

;; Property introspection elements

(defsubst dbus-property-p (element)
  (eq (car-safe element) 'property))

(defsubst dbus-property-name (property)
  (cdr (assoc 'name (second property))))

(defsubst dbus-property-type (property)
  (cdr (assoc 'type (second property))))

(defsubst dbus-property-access (property)
  (cdr (assoc 'access (second property))))

;; Method introspection elements

(defsubst dbus-method-p (element)
  (eq (car-safe element) 'method))

(defsubst dbus-method-name (method)
  (cdr (assoc 'name (second method))))

;; Signal introspection elements

(defsubst dbus-signal-p (element)
  (eq (car-safe element) 'signal))

(defsubst dbus-signal-name (signal)
  (cdr (assoc 'name (second signal))))

;; Interface introspection elements

(defsubst dbus-interface-name (interface)
  (cdr (assoc 'name (second interface))))

(defsubst dbus-interface-elements (interface)
  (cddr interface))

(defsubst dbus-interface-properties (interface)
  (remove-if-not #'dbus-property-p (cddr interface)))

(defsubst dbus-interface-methods (interface)
  (remove-if-not #'dbus-method-p (cddr interface)))

(defsubst dbus-interface-signals (interface)
  (remove-if-not #'dbus-signal-p (cddr interface)))

(provide 'dbus-introspection)

\f
;;; Unit Tests:
;;

(eval-when-compile
  (when (require 'ert nil t)

    (ert-deftest dbus-introspection-test-parse-simple-type-smoke ()
      "Smoke test for the `dbus-proxy-parse-simple-type' function."
      (dolist (case '(("u"  (:uint32 1))
		      ("ab" error)
		      ("!"  error)))
	(destructuring-bind (input expected) case
	  (if (eq expected 'error)
	      (should-error
	       (dbus-proxy-parse-simple-type input)
	       :type 'error)
	    (should (equal
		     (dbus-proxy-parse-simple-type input)
		     expected)))))
      )

    (ert-deftest dbus-introspection-test-parse-composite-type-smoke ()
      "Smoke test for the `dbus-proxy-parse-composite-type' function."
      (dolist (case '(;; Simple
		      ("i"       (:int32 1))
		      ("ii"      (:int32 1))
		      ("u"       (:uint32 1))
		      ("uu"      (:uint32 1))

		      ;; Struct
		      ("(u)"     ((:struct (:uint32)) 3))
		      ("(v)"     ((:struct (:variant)) 3))
		      ("(ii)"    ((:struct (:int32 :int32)) 4))
		      ("(i(ii))" ((:struct (:int32 (:struct (:int32 :int32)))) 7))
		      ("(ius)"   ((:struct (:int32 :uint32 :string)) 5))
		      ("(ii"     error)
		      ("ii)"     (:int32 1))

		      ;; Array
		      ("au"      ((:array :uint32) 2))
		      ("ai"      ((:array :int32) 2))
		      ("av"      ((:array :variant) 2))
		      ("a(ii)"   ((:array (:struct (:int32 :int32))) 5))
		      ("aai"     ((:array (:array :int32)) 3))
		      ("aa"      error)

		      ;; Dict entry
		      ("a{su}"   ((:array (:dict-entry (:string :uint32))) 5))
		      ("a{su}s"  ((:array (:dict-entry (:string :uint32))) 5))
		      ("a{suu}"  error)
		      ("a{su"    error)
		      ("a{s"     error)

		      ;; Random stuff
		      ("!"       error)))
	(destructuring-bind (input expected) case
	  (if (eq expected 'error)
	      (should-error
	       (dbus-proxy-parse-composite-type input)
	       :type 'error)
	    (should (equal
		     (dbus-proxy-parse-composite-type input)
		     expected)))))
      )

    (ert-deftest dbus-introspection-test-parse-type-list-smoke ()
      "Smoke test for the `dbus-proxy-parse-type-list' function."
      (dolist (case '(;; Simple
		      ("i"       ((:int32) 1))
		      ("ii"      ((:int32 :int32) 2))

		      ("u"       ((:uint32) 1))
		      ("uu"      ((:uint32 :uint32) 2))

		      ;; Struct
		      ("(ii)"    (((:struct (:int32 :int32))) 4))
		      ("(i(ii))" (((:struct (:int32 (:struct (:int32 :int32))))) 7))
		      ;("(ii"     error)
		      ;("ii)"     error)

		      ("(u)"     (((:struct (:uint32))) 3))
		      ("(v)"     (((:struct (:variant))) 3))
		      ("(ius)"   (((:struct (:int32 :uint32 :string))) 5))

		      ;; Array
		      ("ai"      (((:array :int32)) 2))
		      ("a(ii)"   (((:array (:struct (:int32 :int32)))) 5))
		      ("aai"     (((:array (:array :int32))) 3))
		      ("aa"      error)

		      ("au"      (((:array :uint32)) 2))
		      ("av"      (((:array :variant)) 2))

		      ;; Dict entry
		      ("a{su}"   (((:array (:dict-entry (:string :uint32)))) 5))
		      ("a{su}s"  (((:array (:dict-entry (:string :uint32))) :string) 6))
		      ("a{suu}"  error)
		      ("a{su"    error)
		      ("a{s"     error)

		      ;; Random stuff
		      ("!"       error)))
	(destructuring-bind (input expected) case
	  (if (eq expected 'error)
	      (should-error
	       (dbus-proxy-parse-composite-type input)
	       :type 'error)
	    (should (equal
		     (dbus-proxy-parse-type-list input)
		     expected)))))

      )

  ))

;;; dbus-introspection.el ends here

[-- Attachment #4: dbus-proxy.el --]
[-- Type: text/x-emacs-lisp, Size: 25856 bytes --]

;;; dbus-proxy.el --- Automatic proxies for remote D-Bus objects
;;
;; Copyright (C) 2009, 2010 Jan Moringen
;;
;; Author: Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
;; Keywords: DBus, proxy
;; Version: 0.3
;; X-RCS: $Id:$
;;
;; This Program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This Program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses>.

\f
;;; Commentary:
;;
;; Here is a basic example of the intended use:
;;
;; (let ((epiphany (dbus-proxy-make-remote-proxy
;;		    :session
;;		    "org.gnome.Epiphany"
;;		    "/org/gnome/Epiphany")))
;;   (open-bookmarks-editor epiphany 0))
;;
;; Creating a proxy object works as follows:
;;
;; Input: bus, client, object path
;;
;; 1. Collect all interfaces of the object at the specified path
;; 2. Form class name (1) (which is a symbol) using the sorted
;;    interface names
;; 3. Check whether the symbol has a class value
;; 4.a If there is one, continue with 5.
;; 4.b Create a new class for the object
;;   1. For each interface:
;;     1. Form class name (2) (which is a symbol) using the interface
;;        name
;;     2. Check whether a class exists for the interface
;;     3.a If there is one, continue with next interface
;;     3.b Create a new class
;;       1. Define the new class using the symbol (2)
;;          Parents: `dbus-proxy-interface-object'
;;       2. For each method specified by the interface
;;          1. Transform the method name into a lispy name
;;          2. Create a method definition for the name that calls the
;;             D-Bus method
;;       3. For each property specified by the interface
;;          1. Transform the property name into a lispy name
;;          2. Store a mapping from transformed name to interface name
;;             and property name
;;       4. For each signal specified by the interface
;;          1. Transform the signal name into a lispy name
;;          2. Store a mapping from transformed name to interface name
;;             and signal name
;;   2. Define a (3) class using the symbol (1)
;;      Parents: `dbus-proxy-remote-object' and the classes created in
;;               4.b.1
;; 5. Create an instance of the class from 4.a or (3) for the object
;;    at the specified object path
;;
;; This algorithm is implemented by:
;;
;; + `dbus-proxy-make-remote-proxy'    (1. 2. 5.)
;; + `dbus-proxy-make-interface-class' (4.b.1.)
;; + `dbus-proxy-make-object-class'    (4.b.2.)
;; + `dbus-proxy-make-method-name'     (4.b.1.3.b.2.1)
;; + `dbus-proxy-make-method'          (4.b.1.3.b.2.2)
;; + `dbus-proxy-make-property-name'   (4.b.1.3.b.3.1)
;; + `dbus-proxy-make-signal-name'     (4.b.1.3.b.4.1)
;;
;; TODO mention properties

\f
;;; History:
;;
;; 0.3 - Properties
;;     - Signals
;;
;; 0.2 - Method name transformations
;;
;; 0.1 - Initial version

\f
;;; Code:
;;

(eval-when-compile
  (require 'cl))

(require 'eieio)

(require 'dbus)
(require 'dbus-introspection)

\f
;;; Conditions
;;

;; no-such-property

(intern "no-such-property")

(put 'no-such-property 'error-conditions
     '(error no-such-property))

(put 'no-such-property 'error-message
     "The requested property does not exist.")

;; no-such-signal

(intern "no-such-signal")

(put 'no-such-signal 'error-conditions
     '(error no-such-signal))

(put 'no-such-signal 'error-message
     "The requested signal does not exist.")

\f
;;; Proxy creation
;;

(defun dbus-proxy-make-remote-proxy (bus service object
				     &optional redefine-classes)
  "Create and return proxy object for OBJECT of SERVICE on BUS.

If necessary, proxy classes are created for OBJECT and the
interfaces it implements.

The first time an object of a specific class (read: a set of
interfaces) is created, the process can take some time since D-Bus
introspection and the definition of several classes are
required. However, subsequent proxy objects of the same class can
be created without the overhead."

  ;; TODO handle case in which object is not found

  ;; Retrieve names of all interfaces implemented by the object and
  ;; get the corresponding proxy class, creating it if required.
  (let* ((interfaces (dbus-introspect-get-interface-names
		      bus service object))
	 (class      (dbus-proxy-ensure-object-class
		      interfaces bus service object
		      redefine-classes)))

    ;; The proxy object is an instance of that class. Create the
    ;; instance, passing it the object address.
    (make-instance class
		   (concat (symbol-name bus) ":" service ":" object)
		   :bus     bus
		   :service service
		   :object  object))
  )

\f
;;; Class `dbus-proxy-remote-object'
;;

(defclass dbus-proxy-remote-object ()
  ((bus     :initarg :bus
	    :type    keyword
	    :documentation
	    "The bus this D-Bus object lives on.")
   (service :initarg :service
	    :type    string
	    :documentation
	    "The well known service name held by this object.")
   (object  :initarg :object
	    :type    string
	    :documentation
	    "The path of this object."))
  "Objects of this class represent D-Bus objects.

Methods of the associated D-Bus object can be called like ordinary
methods.

Properties of the associated D-Bus object are made available using
virtual slots that behave like regular slots."
  :abstract t)

(defmethod slot-missing ((this dbus-proxy-remote-object)
			 slot-name operation &optional new-value)
  "Implement access to slot SLOT-NAME of THIS by calling D-Bus methods."
  (cond
   ;; If SLOT-NAME is a keyword, strip the leading colon and try
   ;; again.
   ((keywordp slot-name)
    (slot-missing
     this
     (intern (substring (symbol-name slot-name) 1))
     operation new-value))

   ;; If SLOT-NAME is a symbol, we have to look up the interface and
   ;; property name to which SLOT-NAME maps. Ask our parents (which
   ;; are all interface classes) to do this.
   ((symbolp slot-name)
    (slot-missing this (dbus-proxy-find-property this slot-name)
		  operation new-value))

   ;; If SLOT-NAME is a cons cell, it contains the interface and
   ;; property name.
   ((listp slot-name)
    (with-slots (bus service object) this
      (destructuring-bind (interface property) slot-name
	(cond

	 ;; Read access
	 ((eq operation 'oref)
	  (dbus-get-property bus service object interface property))

	 ;; Write access
	 ((eq operation 'oset)
	  (dbus-set-property bus service object interface property
			     new-value))

	 ;; Invalid operation
	 (call-next-method))))))
  )

(defmethod connect ((this dbus-proxy-remote-object) signal handler)
  "Connect HANDLER to SIGNAL of THIS.
SIGNAL can either be a symbol or a list of the
form (INTERFACE-NAME SIGNAL-NAME). When the first form is used,
the interface providing the signal will be determined
automatically. An error is signaled if SIGNAL is not contained in
any of the interfaces provided by THIS."
  (if (listp signal)
      ;; If SIGNAL is a cons cell, it contains the interface and
      ;; signal name.
      (with-slots (bus service object) this
	(dbus-register-signal
	 bus service object (first signal) (second signal) handler))
    ;; Otherwise SIGNAL is just a symbol and we have to look up the
    ;; interface and signal name to which SIGNAL maps. Ask our parents
    ;; (which are all interface classes) to do this.
    (connect this (dbus-proxy-find-signal this signal) handler)))

(defmethod disconnect ((this dbus-proxy-remote-object) registration)
  "Disconnect HANDLER from SIGNAL of THIS."
  (dbus-unregister-object registration))

(defclass dbus-proxy-interface-object ()
  ((interface  :allocation :class
	       :type       string
	       :documentation
	       "The name of the interface to which a particular
subclass corresponds.")
   (properties :allocation :class
	       :type       list
	       :documentation
	       "TODO")
   (signals    :allocation :class
	       :type       list
	       :documentation
	       "TODO"))
  "Subclasses of this correspond to D-Bus interfaces.")

(defmethod object-print ((this dbus-proxy-interface-object)
			 &rest strings)
  "Return a textual representation of THIS."
  (with-slots (bus service object) this
    (let ((simple-bus     (eq bus :session))
	  (simple-service (string=
			   (concat "/"
				   (replace-regexp-in-string
				    "\\." "/" service))
			   object)))
      (if (and simple-bus simple-service)
	  (concat "#<dbus-proxy " object ">")
	(concat "#<dbus-proxy"
		(unless simple-bus
		  (concat " bus: " (symbol-name bus)))
		(unless simple-service
		  (concat " service: " service))
		" object: "
		object
		strings
		">"))))
  )

\f
;;; Variables
;;

(defvar dbus-proxy-transform-property-name-function
  #'dbus-proxy-transform-camel-case
  "The value of this is called to transform property names.")

(defvar dbus-proxy-transform-signal-name-function
  #'dbus-proxy-transform-camel-case
  "The value of this is called to transform signal names.")

(defvar dbus-proxy-transform-method-name-function
  #'dbus-proxy-transform-method-name
  "The value of this is called to transform method names.")

\f
;;; Code generation
;;

(defun dbus-proxy-make-property-name (name)
  "Transform NAME to make it usable as a slot name."
  (intern
   (funcall dbus-proxy-transform-property-name-function name)))

(defun dbus-proxy-make-signal-name (name)
  "Transform NAME into a nice signal name."
  (intern
   (funcall dbus-proxy-transform-signal-name-function name)))

(defun dbus-proxy-make-method-name (name)
  "Transform NAME to make it usable as a LISP function name."
  (let ((preferred-name
	 (funcall dbus-proxy-transform-method-name-function name)))
    (if (dbus-proxy-symbol-unsuitable-for-method
	 (intern preferred-name))
	(let ((fallback (concat preferred-name "1")))
	  (warn "Preferred method name `%s' in use; falling back to `%s'"
		preferred-name fallback)
	  (dbus-proxy-make-method-name fallback))
      preferred-name)))

(defun dbus-proxy-make-method (interface method)
  "Construct a stub method for METHOD on INTERFACE."
  (let* ((interface-name      (dbus-interface-name interface))
	 (interface-symbol    (dbus-proxy-make-interface-class-symbol
			       interface-name))
	 (dbus-method-name    (dbus-method-name method))
	 (emacs-method-name   (dbus-proxy-make-method-name
			       dbus-method-name))
	 (method-symbol       (intern emacs-method-name))
	 (dbus-method-args    (remove-if-not #'dbus-arg-p (cddr method)))
	 (dbus-method-in-args (remove-if-not
			       #'dbus-arg-in-p dbus-method-args))
	 (formal-args         (let ((count 0))
				(mapcar
				 (lambda (arg)
				   (incf count)
				   (make-symbol
				    (or (dbus-arg-name arg)
					(format "arg-%d" count))))
				 dbus-method-in-args)))
	 (call-args           (dbus-proxy-make-call-args
			       dbus-method-in-args formal-args))
	 (doc                 ""))

    (message "Defining interface method   %s:%s with signature `%s' as %s"
	     interface-name dbus-method-name
	     (mapconcat #'dbus-arg-type dbus-method-args "")
	     emacs-method-name)

    ;; Define method.
    (eval
     `(defmethod ,method-symbol ((this ,interface-symbol) ,@formal-args)
	,doc
	(with-slots (bus service object) this
	  (dbus-call-method
	   bus service object ,interface-name ,dbus-method-name
	   ,@call-args))))

    ;; Return the symbol
    method-symbol)
  )

\f
;;; Interface Class
;;

(defun dbus-proxy-make-interface-class-symbol (interface)
  ""
  (intern interface))

(defun dbus-proxy-get-interface-class (interface)
  "Return the proxy class for INTERFACE or nil."
  (let ((class (intern-soft
		(symbol-name
		 (dbus-proxy-make-interface-class-symbol interface)))))
    (when (class-p class)
      class))
  )

(defun dbus-proxy-make-interface-class (interface) ;; interface name or interface object?
  ""
  (let* ((class-name     (dbus-interface-name interface))
	 ;; EIEIO globally binds the name `class-symbol'
	 (class-symbol-1 (dbus-proxy-make-interface-class-symbol
			  class-name))
	 (properties     (mapcar
			  (lambda (property)
			    (let ((name (dbus-property-name property)))
			      (cons (dbus-proxy-make-property-name name)
				    name)))
			  (dbus-interface-properties interface)))
	 (signals        (mapcar
			  (lambda (signal)
			    (let ((name (dbus-signal-name signal)))
			      (cons (dbus-proxy-make-signal-name name)
				    name)))
			  (dbus-interface-signals interface)))
	 (doc            (format
			  "Abstract class implementing the interface %s"
			  class-name)))

    (message "Defining interface class  %s" class-name)

    ;; Define the class representing the interface.
    (eval
     `(defclass ,class-symbol-1 (dbus-proxy-interface-object)
	((interface  :initform ,class-name)
	 (properties :initform ,properties)
	 (signals    :initform ,signals))
	,doc
	:abstract t))

    ;; Define the property finder method for this interface.
    (dolist (property properties)
      (message "Defining interface property %s:%s as %s"
	       class-name (cdr property) (car property)))

    (eval
     `(defmethod dbus-proxy-find-property ((this ,class-symbol-1) property)
	"TODO"
	(let ((property-info (assoc property (oref ,class-symbol-1 properties))))
	  (cond
	   ;; Found property info return this interface and the property
	   ;; name.
	   (property-info
	    (list (oref ,class-symbol-1 interface)
		  (cdr property-info)))

	   ;; Not found, but there is another interface class. Try it,
	   ((next-method-p)
	    (call-next-method))

	   ;; The property could not be found in any interface
	   ;; class. Property an error.
	   (t
	    (property 'no-such-property (list property)))))))

    ;; Define the signal finder method for this interface.
    (dolist (signal signals)
      (message "Defining interface signal   %s:%s as %s"
	       class-name (cdr signal) (car signal)))

    (eval
     `(defmethod dbus-proxy-find-signal ((this ,class-symbol-1) signal)
	"TODO"
	(let ((signal-info (assoc signal (oref ,class-symbol-1 signals))))
	  (cond
	   ;; Found signal info return this interface and the signal
	   ;; name.
	   (signal-info
	    (list (oref ,class-symbol-1 interface)
		  (cdr signal-info)))

	   ;; Not found, but there is another interface class. Try it,
	   ((next-method-p)
	    (call-next-method))

	   ;; The signal could not be found in any interface
	   ;; class. Signal an error.
	   (t
	    (signal 'no-such-signal (list signal)))))))

    ;; Define methods for the method elements of the interface.
    (dolist (method (dbus-interface-methods interface))
      (dbus-proxy-make-method interface method))

    ;; Return the symbol.
    class-symbol-1)
  )

(defun dbus-proxy-ensure-interface-class (interface &optional redefine)
  ""
  (or (and (not redefine)
	   (dbus-proxy-get-interface-class
	    (dbus-interface-name interface)))
      (dbus-proxy-make-interface-class interface)))

\f
;;; Object Class
;;

(defun dbus-proxy-make-object-class-symbol (interfaces)
  ""
  (intern
   (mapconcat #'identity
	      (sort (copy-list interfaces) #'string<)
	      "-"))
  )

(defun dbus-proxy-get-object-class (interfaces)
  "Return the proxy class for INTERFACES or nil."
  ;; Construct the symbol under which the class should be
  ;; stored. Check whether it is there.
  (let ((class (intern-soft
		(symbol-name
		 (dbus-proxy-make-object-class-symbol interfaces)))))
    (when (class-p class)
      class))
  )

;; TODO &optional interfaces
(defun dbus-proxy-make-object-class (interfaces
				     &optional redefine-interface-classes)
  ""
  ;; Make sure there are proxy classes for all the interfaces and
  ;; define the class.
  (let (;; EIEIO globally binds the name `class-symbol'
	(class-symbol-1 (dbus-proxy-make-object-class-symbol
			 (mapcar #'dbus-interface-name interfaces)))
	(parents        (cons dbus-proxy-remote-object
			      (mapcar
			       (lambda (interface)
				 (dbus-proxy-ensure-interface-class
				  interface redefine-interface-classes))
			       interfaces)))
	(doc            (format
			 "A class implementing the following D-Bus interfaces:\n\n+ %s."
			 (mapconcat #'dbus-interface-name interfaces "\n+ "))))

    (message "Defining object    class  %s"
	     (mapconcat #'dbus-interface-name interfaces "-"))

    (eval
     `(defclass ,class-symbol-1 (,@parents)
	()
	,doc
	:method-invocation-order :c3))

    ;; Return the symbol.
    class-symbol-1)
  )

(defun dbus-proxy-ensure-object-class (interfaces bus service object
				       &optional redefine)
  ""
  (or (and (not redefine)
	   (dbus-proxy-get-object-class interfaces))
      ;; If necessary, retrieve full interface information and define
      ;; the object class.
      (dbus-proxy-make-object-class
       (mapcar
	(lambda (interface)
	  (dbus-introspect-get-interface bus service object interface))
	interfaces)
       redefine))
  )

\f
;;; Utility Functions
;;

(defun dbus-proxy-make-call-args (dbus-args formal-args)
  "Generate typed call arguments based on DBUS-ARGS and FORMAL-ARGS."
  (let ((dbus-args-rest   dbus-args)
	(formal-args-rest formal-args)
	(result))
    (while (and dbus-args-rest formal-args-rest)
      (let* ((formal-arg (car formal-args-rest))
	     (dbus-arg   (car dbus-args-rest))
	     (type       (dbus-arg-type dbus-arg)))
	;; For simple types, look up the D-Bus type designator and push
	;; it onto the call argument list.
	(when (= (length type) 1)
	  (let ((designator (cdr (assoc (aref type 0)
					dbus-proxy-simple-type-codes))))
	    (if designator
		(push designator result)
	      (warn "Could not find type designator for type `%s'; \
emitting `%s' as untyped argument."
		    type (dbus-arg-name dbus-arg)))))

	;; Push the argument variable unconditionally.
	(push formal-arg result)

	;; Advance to next pair of arguments.
	(setq dbus-args-rest   (cdr dbus-args-rest)
	      formal-args-rest (cdr formal-args-rest))))
    (nreverse result)))

(defun dbus-proxy-symbol-unsuitable-for-method (symbol)
  "Check whether a D-Bus method can be defined on SYMBOL."
  (and (fboundp symbol)
       (not (generic-p symbol))
       (or (byte-code-function-p (symbol-function symbol))
	   (not (eq 'autoload (car-safe (symbol-function symbol)))))))

(defun dbus-proxy-transform-method-name (name)
  "Transform NAME into suitable LISP function name.
The following literal translations are applied an addition to the
transformation performed by `dbus-proxy-transform-camel-case':

+ Get -> prop-get
+ Set -> prop-set"
  (cond
   ((string= name "Get")
    "prop-get")

   ((string= name "Set")
    "prop-set")

   (t
    (dbus-proxy-transform-camel-case name))))

(defun dbus-proxy-transform-camel-case (name)
  "Transform NAME from CamelCase to dash-separated lower case.
Examples:

+ CamelCase        -> camel-case
+ UPPERCASE        -> uppercase
+ PARTIALUppercase -> partial-uppercase"
  (let ((case-fold-search nil))
    (downcase
     (replace-regexp-in-string
      (rx (seq (group (char lower)) (group (char upper))))
      "\\1-\\2"
      (replace-regexp-in-string
       (rx (seq (group (1+ (char upper))) (char lower)))
       (lambda (match)
	 (concat (capitalize (substring match 0 -2))
		 (substring match -2)))
       name t) t))))

(provide 'dbus-proxy)

\f
;;; Unit Tests
;;

(eval-when-compile
  (when (require 'ert nil t)

    (ert-deftest dbus-proxy-test-make-call-arg-smoke ()
      "Test smoke of `make-call-arg'DOC."
      (should
       (equal (dbus-proxy-make-call-args
	       '((arg ((type . "d")))
		 (arg ((type . "s")))
		 (arg ((type . "s"))))
	       '(arg1 arg2 arg3))
	      '(:double arg1 :string arg2 :string arg3)))
      )

    (ert-deftest dbus-proxy-test-transform-camel-case-smoke ()
      "Smoke test for `dbus-proxy-transform-camel-case'."
      (dolist (case '(("CamelCase"        "camel-case")
		      ("UPPERCASE"        "uppercase")
		      ("PARTIALUppercase" "partial-uppercase")
		      ("CrazyCamelCase"   "crazy-camel-case")
		      ("loadURIList"      "load-uri-list")))
	(destructuring-bind (input expected) case
	  (should (string= (dbus-proxy-transform-camel-case input)
			   expected))))
      )

    (ert-deftest dbus-proxy-test-make-remote-proxy ()
      "Test `dbus-proxy-make-remote-proxy'."

      ;; Invalid object path - it seems we cannot check this
      ;; (should-error (dbus-proxy-make-remote-proxy
      ;;		     :session
      ;;		     "org.freedesktop.DBus"
      ;;		     "/org/freedesktop/DBus-invalid"))
      ;; Invalid service - it seems we cannot check this
      ;; (should-error (dbus-proxy-make-remote-proxy
      ;;		  :session
      ;;		  "org.freedesktop.DBus-invalid"
      ;;		  "/org/freedesktop/DBus"))
      ;; Invalid bus name - it seems we cannot check this
      ;; (should-error (dbus-proxy-make-remote-proxy
      ;;		     :session-invalid
      ;;		     "org.freedesktop.DBus"
      ;;		     "/org/freedesktop/DBus"))

      ;; Existing object on session bus.
      (let ((session-bus (dbus-proxy-make-remote-proxy
			  :session
			  "org.freedesktop.DBus"
			  "/org/freedesktop/DBus")))
	(with-slots (bus service object) session-bus
	  (should (eq      bus     :session))
	  (should (string= service "org.freedesktop.DBus"))
	  (should (string= object  "/org/freedesktop/DBus"))))

      ;; Existing object on system bus.
      (let ((session-bus (dbus-proxy-make-remote-proxy
			  :system
			  "org.freedesktop.DBus"
			  "/org/freedesktop/DBus")))
	(with-slots (bus service object) session-bus
	  (should (eq      bus     :system))
	  (should (string= service "org.freedesktop.DBus"))
	  (should (string= object  "/org/freedesktop/DBus"))))
      )

    (ert-deftest dbus-proxy-test-connect ()
      "Test the `connect' mechanism."
      (let ((hal (dbus-proxy-make-remote-proxy
		  :system
		  "org.freedesktop.Hal"
		  "/org/freedesktop/Hal/Manager")))

	;; Connecting to non-existing signal should signal an error.
	(should-error
	 (connect hal 'does-not-exist #'ignore)
	 :type 'no-such-signal)

	;; Do a successful connection and then disconnect again.
	(let ((connection (connect hal 'device-added #'ignore)))
	  (should connection)
	  (disconnect hal connection))

	;; Connect by manually specifying the precise D-Bus interface
	;; and signal names and then disconnect again.
	(let ((connection (connect hal (list "org.freedesktop.Hal.Manager"
					     "DeviceAdded")
				   #'ignore)))
	  (should connection)
	  (disconnect hal connection))

	;; TODO test this properly; the problem is that we need a D-Bus
	;; service that emits signals regularly and automatically
	)
      )

    (ert-deftest dbus-proxy-test-dbus ()
      "Some tests with the D-Bus object."
      (let ((dbus (dbus-proxy-make-remote-proxy
		   :session
		   "org.freedesktop.DBus"
		   "/org/freedesktop/DBus")))
	;; Call Introspect method of the bus object.
	(should (stringp (introspect dbus)))

	;; Call the Hello method. This fails since the bus does not
	;; want us to call the method multiple times.
	(should-error
	 (hello dbus)
	 :type 'dbus-error)
	)
      )

    (ert-deftest dbus-proxy-test-mission-control ()
      "Some tests with the Mission Control D-Bus object."
      (let ((mission-control (dbus-proxy-make-remote-proxy
			      :session
			      "org.freedesktop.Telepathy.MissionControl5"
			      "/org/freedesktop/Telepathy/AccountManager")))

	;; Call the find-accounts method of the mission control
	;; object.
	(should (listp
		 (find-accounts mission-control
				'(:array :signature "{sv}")))))
      )

    (ert-deftest dbus-proxy-test-epiphany ()
      "Some tests with the Epiphany D-Bus object."
      (let ((epiphany (dbus-proxy-make-remote-proxy
		       :session
		       "org.gnome.Epiphany"
		       "/org/gnome/Epiphany")))

	;; Open the bookmarks editor
	(open-bookmarks-editor epiphany 0)

	;; Load a URL
	(load-uri-list epiphany '("www.heise.de") "" 1))
      )

    (ert-deftest dbus-proxy-test-devicekit ()
      "Some tests with the Devicekit D-Bus object."
      (let ((device-kit (dbus-proxy-make-remote-proxy
			 :system
			 "org.freedesktop.DeviceKit"
			 "/org/freedesktop/DeviceKit")))

	;; Retrieve the daemon-version property.
	(should (stringp (slot-value device-kit :daemon-version)))
	(should (stringp (oref device-kit :daemon-version)))

	;; Connect to the device-event signal.
	(connect device-kit 'device-event
		 (lambda (&rest args)
		   (message "Device event %s" args)))

	;; Enumerate subsystems.
	(should (listp (enumerate-by-subsystem device-kit '("sound")))))
      )

    (ert-deftest dbus-proxy-test-rhythmbox ()
      "Some tests with the Rhythmbox D-Bus object."
      (let ((rhythmbox (dbus-proxy-make-remote-proxy
			:session
			"org.gnome.Rhythmbox"
			"/org/gnome/Rhythmbox/Player")))

	;; Connect to the playing-uri-changed signal
	(connect rhythmbox 'playing-uri-changed
		 (lambda (&rest args)
		   (message "Playing URI changed event %s" args)))

	;; Why can't we have `compose' :(
	(dolist (v (mapcar
		    (apply-partially #'* 0.3)
		    (mapcar
		     #'abs
		     (mapcar
		      #'sin
		      (mapcar
		       (apply-partially #'* 0.1)
		       (number-sequence 1 100))))))
	  (set-volume rhythmbox v)
	  (sit-for 0.02)))
      )

    (ert-deftest dbus-proxy-test-gdm ()
      "Some tests with the GDM D-Bus object."
      (let ((gdm (dbus-proxy-make-remote-proxy
		  :system
		  "org.gnome.DisplayManager"
		  "/org/gnome/DisplayManager/Manager" t)))

	;; List displays. This is interesting because the return value
	;; of the call is a list of object paths.
	(get-displays gdm))
      )

  ))

;;; dbus-proxy.el ends here

[-- Attachment #5: TODO --]
[-- Type: text/plain, Size: 618 bytes --]

* TODO Methods which are present in multiple interfaces
  A fine-grained selection of the method to call is not possible at
  the moment
* TODO Slots do not appear in class descriptions
* TODO Complex argument types are not handled properly
* TODO Automatic conversion of return values
  Return values which are object paths are not converted to proxies
* TODO Signal an error for invalid object info
  An error should be signaled if one of the following parameters is
  invalid:
  + Bus name
  + Service
  + Object path
* DONE Support for signals

* settings							   :noexport:
# Local Variables:
# mode: org
# End:

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-21 22:03 Inclusion of dbus-proxy Jan Moringen
@ 2010-08-22 14:47 ` joakim
  2010-08-22 16:20 ` Michael Albinus
  1 sibling, 0 replies; 12+ messages in thread
From: joakim @ 2010-08-22 14:47 UTC (permalink / raw)
  To: Jan Moringen; +Cc: emacs-devel

Jan Moringen <jan.moringen@uni-bielefeld.de> writes:

> Hi,
>
> I recently developed dbus-proxy, a framework that makes accessing remote
> D-Bus objects from Emacs Lisp easier and more transparent than the
> current `dbus-call-method' mechanism. Since I was encouraged to propose
> it for inclusion in Emacs, I'm hereby doing so. Besides being attached,
> the code is also available here:
> http://bazaar.launchpad.net/~scymtym/+junk/dbus-proxy/files
>
> Let me start with a simple example of how a typical usage of dbus-proxy
> looks:
>
> (let ((device-kit (dbus-proxy-make-remote-proxy
>                    :system
>                    "org.freedesktop.DeviceKit"
>                    "/org/freedesktop/DeviceKit")))
>
>   ;; Retrieve the daemon-version property.
>   (slot-value device-kit :daemon-version)
>   (oref device-kit :daemon-version)
>
>   ;; Connect to the device-event signal.
>   (connect device-kit 'device-event
>     (lambda (&rest args)
>       (message "Device event %s" args)))
>
>   ;; Enumerate subsystems.
>   (enumerate-by-subsystem device-kit '("sound")))
>
> This example creates a proxy object for the device kit D-Bus object and
> uses its methods, properties and signals.
>
> dbus-proxy uses D-Bus introspection information to dynamically create
> classes and methods that reflect the structure of the remote interface
> in Emacs Lisp as naturally as possible. For example, `describe-class'
> can used to inspect the methods supported by a particular D-Bus object.
>
> dbus-proxy consists of the following components:
>
> + dbus-proxy.el
>   + public programming interface
>   + interface analysis
>   + proxy superclasses
>   + code generation
>   + unit tests
> + dbus-introspection.el
>   + accessors for D-Bus introspection data
>     (similar to those in dbus.el)
>   + parsing of signature strings
>   + unit tests
>
> I am aware of the following problems with respect to the inclusion in
> Emacs:
> + Names:
>   + Generated class names tend to be long and ugly and do not follow 
>     usual Lisp conventions
>   + `connect' and `disconnect' may need a `dbus-proxy-' prefix?
> + The generated class hierarchies only work with the :c3 method 
>   resolution order which was added to EIEIO upstream a few months ago 
>   but does not seem to have been merged yet
> + The unit tests use ert which is also not currently included in Emacs
> + The use of the cl library may or may not be acceptable
>
> I have signed the copyright assignment for Emacs.
>
> What do you think?

I have used Jan:s dbus-proxy while working on Inkscape-Emacs
integration. Dbus-proxy has been very useful, so I would very much like
for dbus-proxy to be included. Maybe in the new official GNU ELPA repos?

> Kind regards,
> Jan
>
>
>
>

-- 
Joakim Verona



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-21 22:03 Inclusion of dbus-proxy Jan Moringen
  2010-08-22 14:47 ` joakim
@ 2010-08-22 16:20 ` Michael Albinus
  2010-08-22 21:04   ` Jan Moringen
  1 sibling, 1 reply; 12+ messages in thread
From: Michael Albinus @ 2010-08-22 16:20 UTC (permalink / raw)
  To: Jan Moringen; +Cc: emacs-devel

Jan Moringen <jan.moringen@uni-bielefeld.de> writes:

> Hi,

Hi Jan,

> I recently developed dbus-proxy, a framework that makes accessing remote
> D-Bus objects from Emacs Lisp easier and more transparent than the

I would call it "proxy D-Bus objects". The objects you are using reside
on your local host. Accessing D-Bus objects on a remote host is a
different thing.

> current `dbus-call-method' mechanism. Since I was encouraged to propose
> it for inclusion in Emacs, I'm hereby doing so.

It's a nice idea I really appreciate.

> Let me start with a simple example of how a typical usage of dbus-proxy
> looks:
>
> (let ((device-kit (dbus-proxy-make-remote-proxy
>                    :system
>                    "org.freedesktop.DeviceKit"
>                    "/org/freedesktop/DeviceKit")))
>
>   ;; Retrieve the daemon-version property.
>   (slot-value device-kit :daemon-version)
>   (oref device-kit :daemon-version)

For people not familiar with eieio (like me) it would be helpful to
explain what's happening here.

> + dbus-introspection.el

If such a module exists, dbus-introspection-* functions from dbus.el
could be moved here.

> I am aware of the following problems with respect to the inclusion in
> Emacs:
> + Names:
>   + Generated class names tend to be long and ugly and do not follow 
>     usual Lisp conventions
>   + `connect' and `disconnect' may need a `dbus-proxy-' prefix?

Yes. In general, every Lisp package should use its own namespace.

> + The generated class hierarchies only work with the :c3 method 
>   resolution order which was added to EIEIO upstream a few months ago 
>   but does not seem to have been merged yet

This shall be solved before adding your package.

> + The unit tests use ert which is also not currently included in Emacs

Maybe you cam move the unit tests to another package?

> + The use of the cl library may or may not be acceptable

You load it only during byte-compilation. That's OK I believe.

> ;;; dbus-introspection.el --- Helper functions for D-Bus introspection
>
> ;; malformed-signature
>
> (intern "malformed-signature")

What is this good for? The first use of an uninterned symbol adds it to
the obarray.

> (defconst dbus-proxy-simple-type-codes
> (defun dbus-proxy-parse-simple-type (string)
> (defun dbus-proxy-parse-composite-type (string)
> (defun dbus-proxy-parse-type-list (string)

There are already signature parsing functions in dbusbind.c. Maybe we
could make them available on Lisp level; this could simplify your code.

Furthermore, since we are in dbus-introspection.el, the functions shall
not be prefixed dbus-proxy-*

> ;;; dbus-proxy.el --- Automatic proxies for remote D-Bus objects

> ;;; Commentary:
> ;;
> ;; Here is a basic example of the intended use:
> ;;
> ;; (let ((epiphany (dbus-proxy-make-remote-proxy
> ;;		    :session
> ;;		    "org.gnome.Epiphany"
> ;;		    "/org/gnome/Epiphany")))
> ;;   (open-bookmarks-editor epiphany 0))

You could be a little bit more verbose with the example. I suspect, that
the `dbus-proxy-make-remote-proxy' call declares the function
`open-bookmarks-editor', which has been retrieved as method from an
existing interface at "/org/gnome/Epiphany". The following description
of the algorithm tells it somehow, but it is not obvious.

Maybe you could also contribute a section to dus.texi.

Best regards, Michael.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-22 16:20 ` Michael Albinus
@ 2010-08-22 21:04   ` Jan Moringen
  2010-08-23 13:23     ` Kevin Rodgers
  2010-08-23 15:44     ` Michael Albinus
  0 siblings, 2 replies; 12+ messages in thread
From: Jan Moringen @ 2010-08-22 21:04 UTC (permalink / raw)
  To: Michael Albinus; +Cc: emacs-devel

Hi Michael,

thanks for the feedback.

> > I recently developed dbus-proxy, a framework that makes accessing remote
> > D-Bus objects from Emacs Lisp easier and more transparent than the
> 
> I would call it "proxy D-Bus objects". The objects you are using reside
> on your local host. Accessing D-Bus objects on a remote host is a
> different thing.

The rationale for calling proxies "remote" is that one could also
imagine proxy objects that serve the purpose of exposing (local) Emacs
Lisp objects to the D-Bus.

But you are definitely right in that the current naming scheme is be
misleading. Especially with D-Bus lately gaining some remote
capabilities in the other sense.

> > current `dbus-call-method' mechanism. Since I was encouraged to propose
> > it for inclusion in Emacs, I'm hereby doing so.
> 
> It's a nice idea I really appreciate.

Thanks.

> > Let me start with a simple example of how a typical usage of dbus-proxy
> > looks:
> >
> > (let ((device-kit (dbus-proxy-make-remote-proxy
> >                    :system
> >                    "org.freedesktop.DeviceKit"
> >                    "/org/freedesktop/DeviceKit")))
> >
> >   ;; Retrieve the daemon-version property.
> >   (slot-value device-kit :daemon-version)
> >   (oref device-kit :daemon-version)
> 
> For people not familiar with eieio (like me) it would be helpful to
> explain what's happening here.

When called for the first time on a particular D-Bus object,
`dbus-proxy-make-remote-proxy' looks up the set of interfaces
implemented by the object and dynamically creates EIEIO classes for the
D-Bus interfaces. After that, information about D-Bus properties and
signals are stored in the interface classes and EIEIO methods are
created for D-Bus interfaces methods. The generated methods contain
calls to `dbus-call-method' with some argument processing (based on
method signatures, the argument list is augmented with D-Bus type
designators). When the different proxy components are created, names are
transform from D-Bus-typical CamelCamel to something-more-lispy.

In the example, the following slot value access and function calls just
use the facilities of the proxy object described above to transparently
access D-Bus properties and signals and call D-Bus methods. Note that
the function call syntax for EIEIO methods does not differ from ordinary
function calls.

> > + dbus-introspection.el
> 
> If such a module exists, dbus-introspection-* functions from dbus.el
> could be moved here.

Currently this file contains some accessor functions for D-Bus
introspection data and some signature parsing code. I'm not sure how
this would fit together with dbus.el but I do think these should be
merged. In particular dbus-introspection.el does not depend on
dbus-proxy.el.

> > I am aware of the following problems with respect to the inclusion in
> > Emacs:
> > + Names:
> >   + Generated class names tend to be long and ugly and do not follow 
> >     usual Lisp conventions
> >   + `connect' and `disconnect' may need a `dbus-proxy-' prefix?
> 
> Yes. In general, every Lisp package should use its own namespace.

I will fix this.

> > + The generated class hierarchies only work with the :c3 method 
> >   resolution order which was added to EIEIO upstream a few months ago 
> >   but does not seem to have been merged yet
> 
> This shall be solved before adding your package.

A recent CEDET, or at least EIEIO, version would have to be merged. I
don't know how quickly this can happen, but I can still address the
other issues with dbus-proxy in the meantime.

> > + The unit tests use ert which is also not currently included in Emacs
> 
> Maybe you cam move the unit tests to another package?

Are you suggesting to move the unit test code into a different file or
should the tests be migrated to a different unit test framework?

> > + The use of the cl library may or may not be acceptable
> 
> You load it only during byte-compilation. That's OK I believe.

I did not actually try to byte-compile yet, though ;)

> > ;;; dbus-introspection.el --- Helper functions for D-Bus introspection
> >
> > ;; malformed-signature
> >
> > (intern "malformed-signature")
> 
> What is this good for? The first use of an uninterned symbol adds it to
> the obarray.

It serves the purpose of letting a person reading the code know that he
or she is looking at the "definition" of the condition. I though, if I
just put properties on a symbol one might wonder whether the symbol also
serves some other purpose. Technically it is totally unnecessary, I
guess. Should the call to `intern' be removed then?

> > (defconst dbus-proxy-simple-type-codes
> > (defun dbus-proxy-parse-simple-type (string)
> > (defun dbus-proxy-parse-composite-type (string)
> > (defun dbus-proxy-parse-type-list (string)
> 
> There are already signature parsing functions in dbusbind.c. Maybe we
> could make them available on Lisp level; this could simplify your code.

I wasn't aware of that. I will have a look. Thanks.

> Furthermore, since we are in dbus-introspection.el, the functions shall
> not be prefixed dbus-proxy-*

OK. I wasn't sure, where these functions should be placed. Maybe they
could be in dbus.el and then be called dbus-parse-*?

> > ;;; dbus-proxy.el --- Automatic proxies for remote D-Bus objects
> 
> > ;;; Commentary:
> > ;;
> > ;; Here is a basic example of the intended use:
> > ;;
> > ;; (let ((epiphany (dbus-proxy-make-remote-proxy
> > ;;		    :session
> > ;;		    "org.gnome.Epiphany"
> > ;;		    "/org/gnome/Epiphany")))
> > ;;   (open-bookmarks-editor epiphany 0))
> 
> You could be a little bit more verbose with the example. I suspect, that
> the `dbus-proxy-make-remote-proxy' call declares the function
> `open-bookmarks-editor', which has been retrieved as method from an
> existing interface at "/org/gnome/Epiphany". The following description
> of the algorithm tells it somehow, but it is not obvious.

Basically, yes. `open-bookmarks-editor' is a method on the interface
class that has been generated for "/org/gnome/Epiphany". Calling the
method is like a regular function call. The part about
`dbus-proxy-make-remote-proxy' creating the method is only true when the
interface has not yet been used in any proxy. If it has, classes and
methods do not have to be created a second time.

> Maybe you could also contribute a section to dus.texi.

If that's OK, I would rather wait a little longer so it can turn out how
exactly dbus-proxy may be integrated into the existing D-Bus subsystem
before writing this.

Thanks for the feedback again.

Kind regards,
Jan




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-22 21:04   ` Jan Moringen
@ 2010-08-23 13:23     ` Kevin Rodgers
  2010-08-23 15:44     ` Michael Albinus
  1 sibling, 0 replies; 12+ messages in thread
From: Kevin Rodgers @ 2010-08-23 13:23 UTC (permalink / raw)
  To: emacs-devel

Jan Moringen wrote:

> I did not actually try to byte-compile yet, though ;)

Byte compiling in a fresh emacs session (-Q) in which the files
have not been loaded is a good way to find problems such as implicit
dependencies and function calls with the wrong number of arguments.

-- 
Kevin Rodgers
Denver, Colorado, USA




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-22 21:04   ` Jan Moringen
  2010-08-23 13:23     ` Kevin Rodgers
@ 2010-08-23 15:44     ` Michael Albinus
  2010-08-24  2:03       ` Jan Moringen
  1 sibling, 1 reply; 12+ messages in thread
From: Michael Albinus @ 2010-08-23 15:44 UTC (permalink / raw)
  To: Jan Moringen; +Cc: emacs-devel@gnu.org

Jan Moringen <jan.moringen@uni-bielefeld.de> writes:

> Hi Michael,

Hi Jan,

>> For people not familiar with eieio (like me) it would be helpful to
>> explain what's happening here.
>
> When called for the first time on a particular D-Bus object,
> `dbus-proxy-make-remote-proxy' looks up the set of interfaces
> implemented by the object and dynamically creates EIEIO classes for the
> D-Bus interfaces. After that, information about D-Bus properties and
> signals are stored in the interface classes and EIEIO methods are
> created for D-Bus interfaces methods. The generated methods contain
> calls to `dbus-call-method' with some argument processing (based on
> method signatures, the argument list is augmented with D-Bus type
> designators). When the different proxy components are created, names are
> transform from D-Bus-typical CamelCamel to something-more-lispy.
>
> In the example, the following slot value access and function calls just
> use the facilities of the proxy object described above to transparently
> access D-Bus properties and signals and call D-Bus methods. Note that
> the function call syntax for EIEIO methods does not differ from ordinary
> function calls.

That's more or less how I did understand it. What I recommend is a
description for the user, which does not use eieio terminology. It could
be a barrier for people not familiar with eieio.

>> > + dbus-introspection.el
>> 
>> If such a module exists, dbus-introspection-* functions from dbus.el
>> could be moved here.
>
> Currently this file contains some accessor functions for D-Bus
> introspection data and some signature parsing code. I'm not sure how
> this would fit together with dbus.el but I do think these should be
> merged. In particular dbus-introspection.el does not depend on
> dbus-proxy.el.

Maybe there is a misunderstanding. If dbus-introspection.el would be a
part of core Emacs, I recommend to move existing dbus-introspection-*
functions *from* dbus.el *to* dbus-introspection.el.

If dbus-introspection.el will be offered via ELPA, this is not useful.

> It serves the purpose of letting a person reading the code know that he
> or she is looking at the "definition" of the condition. I though, if I
> just put properties on a symbol one might wonder whether the symbol also
> serves some other purpose. Technically it is totally unnecessary, I
> guess. Should the call to `intern' be removed then?

I would say yes.

>> Furthermore, since we are in dbus-introspection.el, the functions shall
>> not be prefixed dbus-proxy-*
>
> OK. I wasn't sure, where these functions should be placed. Maybe they
> could be in dbus.el and then be called dbus-parse-*?

Maybe. Or they are kept in dbus-introspection.el, and are called
dbus-introspection-*.

> Kind regards,
> Jan

Best regards, Michael.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-23 15:44     ` Michael Albinus
@ 2010-08-24  2:03       ` Jan Moringen
  2010-08-24 19:50         ` Michael Albinus
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Moringen @ 2010-08-24  2:03 UTC (permalink / raw)
  To: Michael Albinus; +Cc: emacs-devel@gnu.org

Hi Michael,

thanks for the extensive feedback.

> >> For people not familiar with eieio (like me) it would be helpful to
> >> explain what's happening here.
> >
> > When called for the first time on a particular D-Bus object,
> > [...]
> > the function call syntax for EIEIO methods does not differ from ordinary
> > function calls.
> 
> That's more or less how I did understand it. What I recommend is a
> description for the user, which does not use eieio terminology. It could
> be a barrier for people not familiar with eieio.

I committed the following updated explanation which makes fewer
references to EIEIO:

;; When called for the first time on a particular D-Bus object,
;; `dbus-proxy-make-remote-proxy' looks up the set of interfaces
;; implemented by the object and dynamically creates Emacs Lisp code
;; that mimics the D-Bus interfaces. This code mainly consists of
;; functions which correspond to the D-Bus interface methods, but
;; information about D-Bus properties and signals is also stored.
;; When called, the generated functions augment their arguments with
;; D-Bus type information and call their corresponding D-Bus methods.
;; When these proxy components are created, names are transformed from
;; D-Bus-typical CamelCamel to something-more-lispy. The generated
;; functions and object slots can be used like ordinary Emacs Lisp
;; functions and object slots.

> >> > + dbus-introspection.el
> >> 
> >> If such a module exists, dbus-introspection-* functions from dbus.el
> >> could be moved here.
> >
> > Currently this file contains some accessor functions for D-Bus
> > introspection data and some signature parsing code. I'm not sure how
> > this would fit together with dbus.el but I do think these should be
> > merged. In particular dbus-introspection.el does not depend on
> > dbus-proxy.el.
> 
> Maybe there is a misunderstanding. If dbus-introspection.el would be a
> part of core Emacs, I recommend to move existing dbus-introspection-*
> functions *from* dbus.el *to* dbus-introspection.el.

Ah, OK. Well, this has to be done later in case dbus-proxy is included
in Emacs, I guess

> If dbus-introspection.el will be offered via ELPA, this is not useful.

Yes.

> > It serves the purpose of letting a person reading the code know that he
> > or she is looking at the "definition" of the condition. I though, if I
> > just put properties on a symbol one might wonder whether the symbol also
> > serves some other purpose. Technically it is totally unnecessary, I
> > guess. Should the call to `intern' be removed then?
> 
> I would say yes.

Done.

> >> Furthermore, since we are in dbus-introspection.el, the functions shall
> >> not be prefixed dbus-proxy-*
> >
> > OK. I wasn't sure, where these functions should be placed. Maybe they
> > could be in dbus.el and then be called dbus-parse-*?
> 
> Maybe. Or they are kept in dbus-introspection.el, and are called
> dbus-introspection-*.

Done.

I think, I addressed all suggestions which do not depend on changes in
Emacs itself. The code is available at
https://code.launchpad.net/~scymtym/+junk/dbus-proxy

Kind regards,
Jan




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-24  2:03       ` Jan Moringen
@ 2010-08-24 19:50         ` Michael Albinus
  2010-08-24 21:32           ` Chong Yidong
  2010-08-25  3:24           ` Jan Moringen
  0 siblings, 2 replies; 12+ messages in thread
From: Michael Albinus @ 2010-08-24 19:50 UTC (permalink / raw)
  To: Jan Moringen; +Cc: emacs-devel@gnu.org

Jan Moringen <jan.moringen@uni-bielefeld.de> writes:

> Hi Michael,

Hi Jan,

>> That's more or less how I did understand it. What I recommend is a
>> description for the user, which does not use eieio terminology. It could
>> be a barrier for people not familiar with eieio.
>
> I committed the following updated explanation which makes fewer
> references to EIEIO:
>
> ;; When called for the first time on a particular D-Bus object,
> ;; `dbus-proxy-make-remote-proxy' looks up the set of interfaces
> ;; implemented by the object and dynamically creates Emacs Lisp code
> ;; that mimics the D-Bus interfaces. This code mainly consists of
> ;; functions which correspond to the D-Bus interface methods, but
> ;; information about D-Bus properties and signals is also stored.
> ;; When called, the generated functions augment their arguments with
> ;; D-Bus type information and call their corresponding D-Bus methods.
> ;; When these proxy components are created, names are transformed from
> ;; D-Bus-typical CamelCamel to something-more-lispy. The generated
> ;; functions and object slots can be used like ordinary Emacs Lisp
> ;; functions and object slots.

Sounds OK to me. Together with an instructive example (showing
introspection data and derived Lisp functions), it shall be sufficient.

> I think, I addressed all suggestions which do not depend on changes in
> Emacs itself. The code is available at
> https://code.launchpad.net/~scymtym/+junk/dbus-proxy

I try to play with this next days. Unfortunately, I'm short in time (as
usual), and I'm on the road next week.

However, a short note from the maintainers (Stefan? Chong?), whether the
code is worth to be added, would be great.

> Kind regards,
> Jan

Best regards, Michael.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-24 19:50         ` Michael Albinus
@ 2010-08-24 21:32           ` Chong Yidong
  2010-08-24 23:59             ` Tom Tromey
  2010-08-26 10:09             ` Michael Albinus
  2010-08-25  3:24           ` Jan Moringen
  1 sibling, 2 replies; 12+ messages in thread
From: Chong Yidong @ 2010-08-24 21:32 UTC (permalink / raw)
  To: Michael Albinus; +Cc: emacs-devel@gnu.org, Jan Moringen

Michael Albinus <michael.albinus@gmx.de> writes:

> However, a short note from the maintainers (Stefan? Chong?), whether the
> code is worth to be added, would be great.

It looks interesting, and I'd like to add it; the question is whether to
add it to the main Emacs tarball, or to elpa.gnu.org.  If it is very
likely that many other parts of Emacs will make use of this package as a
piece of basic "infrastructure", it should go into the tarball.

If in doubt, we can add it to elpa.gnu.org for now.

What do you think?



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-24 21:32           ` Chong Yidong
@ 2010-08-24 23:59             ` Tom Tromey
  2010-08-26 10:09             ` Michael Albinus
  1 sibling, 0 replies; 12+ messages in thread
From: Tom Tromey @ 2010-08-24 23:59 UTC (permalink / raw)
  To: Chong Yidong; +Cc: Jan Moringen, Michael Albinus, emacs-devel@gnu.org

>>>>> "Chong" == Chong Yidong <cyd@stupidchicken.com> writes:

>> However, a short note from the maintainers (Stefan? Chong?), whether the
>> code is worth to be added, would be great.

Chong> It looks interesting, and I'd like to add it; the question is whether to
Chong> add it to the main Emacs tarball, or to elpa.gnu.org.  If it is very
Chong> likely that many other parts of Emacs will make use of this package as a
Chong> piece of basic "infrastructure", it should go into the tarball.

Chong> If in doubt, we can add it to elpa.gnu.org for now.

Chong> What do you think?

It doesn't have to be either-or.

package.el was designed to support having a package be distributed both
in Emacs and outside.  This can't work perfectly (autoloads can change),
and for best results it requires a bit of hacking on the Emacs side.

Tom



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-24 19:50         ` Michael Albinus
  2010-08-24 21:32           ` Chong Yidong
@ 2010-08-25  3:24           ` Jan Moringen
  1 sibling, 0 replies; 12+ messages in thread
From: Jan Moringen @ 2010-08-25  3:24 UTC (permalink / raw)
  To: Michael Albinus; +Cc: emacs-devel@gnu.org

Hi Michael.

> >> That's more or less how I did understand it. What I recommend is a
> >> description for the user, which does not use eieio terminology. It could
> >> be a barrier for people not familiar with eieio.
> >
> > I committed the following updated explanation which makes fewer
> > references to EIEIO:
> >
> > ;; When called for the first time on a particular D-Bus object,
> > ;; `dbus-proxy-make-remote-proxy' looks up the set of interfaces
> > ;; implemented by the object and dynamically creates Emacs Lisp code
> > ;; that mimics the D-Bus interfaces. This code mainly consists of
> > ;; functions which correspond to the D-Bus interface methods, but
> > ;; information about D-Bus properties and signals is also stored.
> > ;; When called, the generated functions augment their arguments with
> > ;; D-Bus type information and call their corresponding D-Bus methods.
> > ;; When these proxy components are created, names are transformed from
> > ;; D-Bus-typical CamelCamel to something-more-lispy. The generated
> > ;; functions and object slots can be used like ordinary Emacs Lisp
> > ;; functions and object slots.
> 
> Sounds OK to me. Together with an instructive example (showing
> introspection data and derived Lisp functions), it shall be sufficient.

I expanded the commentary section to that extent.

> > I think, I addressed all suggestions which do not depend on changes in
> > Emacs itself. The code is available at
> > https://code.launchpad.net/~scymtym/+junk/dbus-proxy
> 
> I try to play with this next days. Unfortunately, I'm short in time (as
> usual), and I'm on the road next week.

Thanks.

Jan




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Inclusion of dbus-proxy
  2010-08-24 21:32           ` Chong Yidong
  2010-08-24 23:59             ` Tom Tromey
@ 2010-08-26 10:09             ` Michael Albinus
  1 sibling, 0 replies; 12+ messages in thread
From: Michael Albinus @ 2010-08-26 10:09 UTC (permalink / raw)
  To: Chong Yidong; +Cc: emacs-devel@gnu.org, Jan Moringen

Chong Yidong <cyd@stupidchicken.com> writes:

> It looks interesting, and I'd like to add it; the question is whether to
> add it to the main Emacs tarball, or to elpa.gnu.org.  If it is very
> likely that many other parts of Emacs will make use of this package as a
> piece of basic "infrastructure", it should go into the tarball.

Honestly, I don't know. It adds a convenience layer for access to
applications via D-Bus; such a layer is offered by other D-Bus language
bindings as well.

OTOH, it cannot be regarded as the default library for communicationg
with other applications via D-Bus, because there are applications which
do not offer introspection data. In this case, the low level functions
must still be used.

> If in doubt, we can add it to elpa.gnu.org for now.
>
> What do you think?

Maybe adding to elpa.gnu.org is OK for now. If we see the majority of
packages, which need D-Bus, are using dbus-proxy.el, we shall move it to
Emacs core.

Best regards, Michael.



^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2010-08-26 10:09 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-08-21 22:03 Inclusion of dbus-proxy Jan Moringen
2010-08-22 14:47 ` joakim
2010-08-22 16:20 ` Michael Albinus
2010-08-22 21:04   ` Jan Moringen
2010-08-23 13:23     ` Kevin Rodgers
2010-08-23 15:44     ` Michael Albinus
2010-08-24  2:03       ` Jan Moringen
2010-08-24 19:50         ` Michael Albinus
2010-08-24 21:32           ` Chong Yidong
2010-08-24 23:59             ` Tom Tromey
2010-08-26 10:09             ` Michael Albinus
2010-08-25  3:24           ` Jan Moringen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).