source: project/release/4/dbus/trunk/dbus.scm @ 25785

Last change on this file since 25785 was 25785, checked in by ecloud, 9 years ago

Tagging dbus.setup fix for 0.88 release

File size: 27.6 KB
Line 
1;;;; dbus.scm
2
3(module dbus (dbus:make-context
4              dbus:send
5              dbus:call
6              dbus:make-method-proxy
7              dbus:register-signal-handler
8              dbus:register-method
9              dbus:enable-polling-thread!
10              dbus:poll-for-message
11              dbus:discover-services
12              dbus:discover-api-xml
13              dbus:dbus-service
14              dbus:type-uint32
15              dbus:session-bus
16              dbus:system-bus
17              dbus:starter-bus
18              dbus:known-bus-count
19              dbus:register-path)
20        (import scheme chicken
21                (except foreign foreign-declare)
22                foreigners
23                easyffi
24                miscmacros)
25        (use srfi-18)
26
27
28#>
29        #include <dbus/dbus.h>
30        #include <stdbool.h>
31        #include <stdint.h>
32        #include <fcntl.h>
33        #include <unistd.h>
34
35        static DBusError err;
36<#
37
38; Would want to do this:
39; (define-foreign-enum (dbus:bus (enum "DBusBusType"))
40        ; (dbus:session-bus DBUS_BUS_SESSION)
41        ; (dbus:system-bus DBUS_BUS_SYSTEM)
42        ; (dbus:starter-bus DBUS_BUS_STARTER) )
43; but because that enum is typedef'd, chicken has a problem with it.
44
45(define-foreign-type dbus:bus int) ; really "DBusBusType"
46(define dbus:session-bus (foreign-value DBUS_BUS_SESSION int))
47(define dbus:system-bus (foreign-value DBUS_BUS_SYSTEM int))
48(define dbus:starter-bus (foreign-value DBUS_BUS_STARTER int))
49(define dbus:dbus-service (foreign-value DBUS_SERVICE_DBUS c-string))
50(define dbus:known-bus-count (+ 1 (max dbus:session-bus dbus:system-bus dbus:starter-bus)))
51
52(define-foreign-type dbus:handler-result int) ; really "DBusHandlerResult"
53(define dbus:result-handled (foreign-value DBUS_HANDLER_RESULT_HANDLED int))
54(define dbus:result-not-yet-handled (foreign-value DBUS_HANDLER_RESULT_NOT_YET_HANDLED int))
55(define dbus:result-need-memory (foreign-value DBUS_HANDLER_RESULT_NEED_MEMORY int))
56
57(define dbus:name-flag-allow-replacement (foreign-value DBUS_NAME_FLAG_ALLOW_REPLACEMENT int))
58(define dbus:name-flag-replace-existing (foreign-value DBUS_NAME_FLAG_REPLACE_EXISTING int))
59(define dbus:name-flag-do-not-queue (foreign-value DBUS_NAME_FLAG_DO_NOT_QUEUE int))
60
61;; DBus simple data types
62(define dbus:type-invalid (foreign-value DBUS_TYPE_INVALID int))
63(define dbus:type-invalid-string (foreign-value DBUS_TYPE_INVALID_AS_STRING int))
64(define dbus:type-byte  (foreign-value DBUS_TYPE_BYTE int))
65(define dbus:type-byte-string  (foreign-value DBUS_TYPE_BYTE_AS_STRING int))
66(define dbus:type-boolean  (foreign-value DBUS_TYPE_BOOLEAN int))
67(define dbus:type-boolean-string  (foreign-value DBUS_TYPE_BOOLEAN_AS_STRING int))
68(define dbus:type-int16  (foreign-value DBUS_TYPE_INT16 int))
69(define dbus:type-int16-string  (foreign-value DBUS_TYPE_INT16_AS_STRING int))
70(define dbus:type-uint16  (foreign-value DBUS_TYPE_UINT16 int))
71(define dbus:type-uint16-string  (foreign-value DBUS_TYPE_UINT16_AS_STRING int))
72(define dbus:type-fixnum (foreign-value DBUS_TYPE_INT32 int))
73(define dbus:type-int32  (foreign-value DBUS_TYPE_INT32 int))
74(define dbus:type-int32-string  (foreign-value DBUS_TYPE_INT32_AS_STRING int))
75(define dbus:type-uint32  (foreign-value DBUS_TYPE_UINT32 int))
76(define dbus:type-uint32-string  (foreign-value DBUS_TYPE_UINT32_AS_STRING int))
77(define dbus:type-int64  (foreign-value DBUS_TYPE_INT64 int))
78(define dbus:type-int64-string  (foreign-value DBUS_TYPE_INT64_AS_STRING int))
79(define dbus:type-uint64  (foreign-value DBUS_TYPE_UINT64 int))
80(define dbus:type-uint64-string  (foreign-value DBUS_TYPE_UINT64_AS_STRING int))
81(define dbus:type-double  (foreign-value DBUS_TYPE_DOUBLE int))
82(define dbus:type-flonum  (foreign-value DBUS_TYPE_DOUBLE int))
83(define dbus:type-double-string  (foreign-value DBUS_TYPE_DOUBLE_AS_STRING int))
84(define dbus:type-string  (foreign-value DBUS_TYPE_STRING int))
85(define dbus:type-string-string  (foreign-value DBUS_TYPE_STRING_AS_STRING int))
86(define dbus:type-object-path  (foreign-value DBUS_TYPE_OBJECT_PATH int))
87(define dbus:type-object-path-string  (foreign-value DBUS_TYPE_OBJECT_PATH_AS_STRING int))
88(define dbus:type-signature  (foreign-value DBUS_TYPE_SIGNATURE int))
89(define dbus:type-signature-string  (foreign-value DBUS_TYPE_SIGNATURE_AS_STRING int))
90(define dbus:type-array (foreign-value DBUS_TYPE_ARRAY int))
91(define dbus:type-array-string  (foreign-value DBUS_TYPE_ARRAY_AS_STRING int))
92
93(define dbus:make-context)
94(define dbus:send)
95(define dbus:make-method-proxy)
96(define dbus:call)
97(define dbus:flush)
98(define dbus:poll-for-message)
99(define dbus:register-signal-handler)
100(define dbus:register-method)
101(define dbus:register-path)
102(define dbus:enable-polling-thread!)
103
104(define dbus:add-match)
105(define dbus:request-name)
106
107
108(define find-callback)
109
110(define-foreign-type error-ptr c-pointer) ;; DBusError*
111(define-foreign-type connection-ptr c-pointer)  ;; DBusConnection*
112(define-foreign-type message-ptr c-pointer)     ;; DBusMessage*
113(define-foreign-type uint-ptr c-pointer)        ;; dbus_uint32_t*
114(define-foreign-type message-iter-ptr c-pointer)        ;; DBusMessageIter*
115(define-foreign-type vtable-ptr c-pointer)      ;; DBusObjectPathVTable*
116
117(define (dbus:discover-services #!key (bus dbus:session-bus))
118        (let* ([ctxt (dbus:make-context
119                                        bus: bus
120                                        service: 'org.freedesktop.DBus
121                                        interface: 'org.freedesktop.DBus
122                                        path: '/org/freedesktop/DBus)]
123                        [services (dbus:call ctxt "ListNames")])
124                (and (pair? services) (vector? (car services)) (vector->list (car services)))))
125
126(define dbus:discover-api-xml)
127
128(define-external (C_msg_cb (dbus:bus bus) (message-ptr msg)) bool
129        (let* ([cb (find-callback bus msg)][found (procedure? cb)])
130                ; (printf "got a message: ~s on bus ~a and found callback ~s~%" msg bus cb)
131                (when found
132                        (cb msg))
133                found
134        ))
135
136(let (  [connections '()]       ;; an alist mapping dbus:bus to DBusConnection ptrs
137                [error (foreign-value "&err" c-pointer)]
138                ;; indices in a "context" vector
139                [context-idx-ID 0]
140                [context-idx-bus 1]
141                [context-idx-service 2]
142                [context-idx-path 3]
143                [context-idx-interface 4]
144                [context-count 0]
145                [default-polling-interval 0.01]
146                [polling-interval (make-vector dbus:known-bus-count 0.01)]
147                [polling-enabled (make-vector dbus:known-bus-count #t)]
148                [polling-threads (make-vector dbus:known-bus-count #f)]
149                ;; will become an assoc tree:
150                ;; bus
151                ;;   path
152                ;;     service (unless it's a signal callback)
153                ;;       interface
154                ;;         method
155                ;;           callback-fn
156                [callbacks-table `((,dbus:system-bus . #f) (,dbus:session-bus . #f))]
157                [iterm (gensym 'terminiter)] )
158
159    (define (any->string arg)
160      (if (string? arg)
161        arg
162        (if (eq? (void) arg)
163          ""
164          (format "~a" arg)
165        )))
166
167        (define (symbol?->string arg)
168                (if (symbol? arg)
169                        (symbol->string arg)
170                        arg))
171
172        (define (string?->symbol arg)
173                (if (string? arg)
174                        (string->symbol arg)
175                        arg))
176
177        ;; If the assq-list has the given key, replace its value.
178        ;; Else add the key-value pair.
179        (define (asset! alist key val)
180                (let ([pr (assq key alist)])
181                        (if pr
182                                (set-cdr! pr val)
183                                (if (null? (cdar alist))
184                                        (set-car! alist (cons key val))
185                                        (begin
186                                                (set-cdr! alist (cons (car alist) (cdr alist)))
187                                                (set-car! alist (cons key val))) ))))
188
189        ;; The "tree" version of assq: drills down into an assq-tree
190        ;; as directed by the sequence of keys, and returns the value found.
191        ;; #f means not found (so it is not useful to store #f in such a tree).
192        (define (tassq tree . keys)
193                (let ([key-list (if (pair? (car keys)) (car keys) keys)])
194                        (let loop ([rem-keys key-list][subtree tree])
195                                (if (null? rem-keys)
196                                        subtree
197                                        (loop (cdr rem-keys)
198                                                (let ([pr (assq (car rem-keys) subtree)])
199                                                        (and (pair? pr) (cdr pr))))))))
200
201        ;; The "tree" version of asset!: drills down into an assq-tree
202        ;; as directed by the sequence of keys, making new branches as necessary,
203        ;; and sets the given value as a leaf at that point in the tree.
204        ;; return value is undefined
205        (define (tasset! tree val . keys)
206                (let ([key-list (if (pair? (car keys)) (car keys) keys)])
207                        (let loop (     [rem-keys (cdr key-list)]
208                                                [subtree (tassq tree (car key-list))]
209                                                [prev-key (car key-list)]
210                                                [prev-subtree tree])
211; (printf "rem-keys ~s subtree ~s prev-key ~s prev-subtree ~s~%" rem-keys subtree prev-key prev-subtree)
212                                (when (and (not subtree) (pair? rem-keys))
213                                        (set! subtree (list (cons (car rem-keys) #f)))
214; (printf "   creating subtree ~s within ~s~%" subtree prev-subtree)
215                                        (asset! prev-subtree prev-key subtree)
216; (pretty-print prev-subtree)
217                                )
218                                (if (null? rem-keys)
219                                        (asset! prev-subtree prev-key val)
220                                        (loop
221                                                (cdr rem-keys)
222                                                (let ([pr (assq (car rem-keys) subtree)])
223                                                        (unless (pair? pr)
224                                                                (set! pr (cons (car rem-keys) #f))
225                                                                (set-cdr! subtree (cons (car subtree) (cdr subtree)))
226                                                                (set-car! subtree pr) )
227                                                        (cdr pr))
228                                                (car rem-keys)
229                                                subtree )))))
230
231        (define (next-context-ID) (set! context-count (+ 1 context-count)) context-count)
232
233        (define (get-conn bus-type)
234                (let ([conn (assq bus-type connections)])
235                        (if (pair? conn)
236                                (set! conn (cdr conn))
237                                (begin
238                                        (set! conn ((foreign-lambda connection-ptr "dbus_bus_get" dbus:bus error-ptr)
239                                                bus-type error) )
240                                        (when conn
241                                                (set! connections (cons (cons bus-type conn) connections)))))
242                        conn))
243
244        (define (conn-or-abort bus-type)
245                (or (get-conn bus-type)
246                        (abort (format "unable to connect to bus ~s~%" bus-type))))
247
248        (define (exists-or-abort datum err-str)
249                (or datum
250                        (abort err-str)))
251
252        ;; params: path interface name
253        ;; todo: garbage-collect this
254        (define make-signal (foreign-lambda message-ptr "dbus_message_new_signal"
255                c-string c-string c-string))
256
257        ;; params: service path interface method-name
258        ;; todo: garbage-collect this
259        (define make-message (foreign-lambda message-ptr "dbus_message_new_method_call"
260                c-string c-string c-string c-string))
261
262        ;; todo: garbage-collect this
263        (define make-iter-append
264                (foreign-lambda* message-iter-ptr ((message-ptr msg))
265                        "DBusMessageIter* iter = malloc(sizeof(DBusMessageIter));
266                        dbus_message_iter_init_append (msg, iter);
267                        C_return (iter);"))
268
269        (define iter-append-basic-string
270                (foreign-lambda* bool ((message-iter-ptr iter) (c-string v))
271                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &v));"))
272
273        (define iter-append-basic-bool
274                (foreign-lambda* bool ((message-iter-ptr iter) (bool v))
275                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &v));"))
276
277        (define iter-append-basic-int
278                (foreign-lambda* bool ((message-iter-ptr iter) (int v))
279                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &v));"))
280
281        (define iter-append-basic-double
282                (foreign-lambda* bool ((message-iter-ptr iter) (double v))
283                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &v));"))
284
285        (define iter-append-basic-byte
286                (foreign-lambda* bool ((message-iter-ptr iter) (int v))
287                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &v));"))
288
289        (define iter-append-basic-int16
290                (foreign-lambda* bool ((message-iter-ptr iter) (int v))
291                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &v));"))
292
293        (define iter-append-basic-uint32
294                (foreign-lambda* bool ((message-iter-ptr iter) (unsigned-integer32 v))
295                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &v));"))
296
297        (define iter-append-basic-uint16
298                (foreign-lambda* bool ((message-iter-ptr iter) (unsigned-short v))
299                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &v));"))
300
301        (define iter-append-basic-int64
302                (foreign-lambda* bool ((message-iter-ptr iter) (integer64 v))
303                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &v));"))
304
305        (define iter-append-basic-uint64
306                (foreign-lambda* bool ((message-iter-ptr iter) (integer64 v))
307                        "C_return (dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &v));"))
308
309        ;; TODO: iter-append-basic-T for each possible type:
310        ;; especially array, variant and struct might be possible
311
312        ;; val would usually be a single value, but
313        ;; could be a pair of the form (dbus:type-x . value)
314        ;; in which case we will attempt to convert the value to that type for sending.
315        (define (iter-append-basic iter val)
316                (cond
317                        [(fixnum? val) (iter-append-basic-int iter val)]
318                        [(flonum? val) (iter-append-basic-double iter val)]
319                        [(boolean? val) (iter-append-basic-bool iter val)]
320                        [(pair? val)
321                                (let ([type (car val)])
322                                        (cond
323                                                ;; TODO: this doesn't compile
324                                                ;; Error: Arguments to inlined call of `iter-append-basic-byte'
325                                                ;; do not match parameter-list (a207 a206)
326                                                ;; so I guess it has to _be_ a byte before the call
327                                                ; [(eq? type dbus:type-byte)
328                                                        ; (iter-append-basic-byte (cdr val))]
329                                                ; [(eq? type dbus:type-int16)
330                                                        ; (iter-append-basic-int16 (cdr val))]
331                                                ; [(eq? type dbus:type-uint32)
332                                                        ; (iter-append-basic-uint32 (cdr val))]
333                                                ; [(eq? type dbus:type-uint16)
334                                                        ; (iter-append-basic-uint16 (cdr val))]
335                                                ; [(eq? type dbus:type-int64)
336                                                        ; (iter-append-basic-int64 (cdr val))]
337                                                ; [(eq? type dbus:type-uint64)
338                                                        ; (iter-append-basic-uint64 (cdr val))]
339                                                ;; other custom requests will be handled as usual, above
340                                                [else (iter-append-basic iter (cdr val))] ))]
341                        [else (iter-append-basic-string iter (any->string val))] ))
342
343        (define free-iter (foreign-lambda* void ((message-iter-ptr i)) "free(i);"))
344
345        (define (iter-cond iter)
346                (let (  [type ((foreign-lambda int "dbus_message_iter_get_arg_type"
347                                                message-iter-ptr) iter)] )
348                        (cond
349                                [(memq type `(,dbus:type-string ,dbus:type-invalid-string
350                                                                ,dbus:type-string-string ,dbus:type-object-path-string
351                                                                ,dbus:type-signature-string
352                                                                ;; TODO maybe the following types ought to be converted?
353                                                                ,dbus:type-byte-string ,dbus:type-boolean-string
354                                                                ,dbus:type-int16-string ,dbus:type-uint16-string
355                                                                ,dbus:type-int32-string ,dbus:type-uint32-string
356                                                                ,dbus:type-int64-string ,dbus:type-uint64-string
357                                                                ,dbus:type-double-string ))
358                                        ((foreign-lambda* c-string ((message-iter-ptr iter))
359                                                "char* ret;
360                                                dbus_message_iter_get_basic(iter, &ret);
361                                                C_return (ret);") iter)]
362                                [(eq? type dbus:type-boolean)
363                                        ((foreign-lambda* bool ((message-iter-ptr iter))
364                                                "bool ret;
365                                                dbus_message_iter_get_basic(iter, &ret);
366                                                return (ret);") iter)]
367                                [(memq type `(,dbus:type-int32 ,dbus:type-byte
368                                                                ,dbus:type-int16 ))
369                                        ((foreign-lambda* int ((message-iter-ptr iter))
370                                                "int ret;
371                                                dbus_message_iter_get_basic(iter, &ret);
372                                                C_return (ret);") iter)]
373                                [(memq type `(,dbus:type-uint32 ,dbus:type-uint16))
374                                        ((foreign-lambda* unsigned-int ((message-iter-ptr iter))
375                                                "unsigned int ret;
376                                                dbus_message_iter_get_basic(iter, &ret);
377                                                C_return (ret);") iter)]
378                                [(memq type `(,dbus:type-flonum ,dbus:type-uint64))
379                                        ((foreign-lambda* double ((message-iter-ptr iter))
380                                                "double ret;
381                                                dbus_message_iter_get_basic(iter, &ret);
382                                                C_return (ret);") iter)]
383                                [(eq? type dbus:type-int64)
384                                        ((foreign-lambda* integer64 ((message-iter-ptr iter))
385                                                "int64_t ret;
386                                                dbus_message_iter_get_basic(iter, &ret);
387                                                C_return (ret);") iter)]
388                                [(eq? type dbus:type-array)
389                                        (iter->vector (make-sub-iter iter))]
390                                ;; unsupported so far (not understood well enough):
391                                ;;      dbus:type-object-path and dbus:type-signature
392                                ;; dbus:type-invalid is returned as #f (could be (void) but that
393                                ;; would be the termination condition for the iterator)
394                                [else #f] )))
395
396        (define (make-sub-iter iter)
397                (let* ([sub ((foreign-lambda* message-iter-ptr ((message-iter-ptr iter))
398                                "DBusMessageIter* i = malloc(sizeof(DBusMessageIter));
399                                dbus_message_iter_recurse(iter, i);
400                                C_return (i);") iter) ]
401                                [has-next sub]
402                                )
403                        (lambda ()
404                                (if has-next
405                                        (let ([ret (iter-cond sub)])
406                                                (set! has-next ((foreign-lambda bool
407                                                        "dbus_message_iter_next" message-iter-ptr) sub))
408                                                ret     )
409                                        (begin
410                                                (free-iter sub)
411                                                iterm)
412                                ))))
413
414        ;; iterator for reading parameters from a message
415        ;; returns a lambda which provides one param at a time, terminating with (void)
416        (define (make-iter msg)
417                (let* ([iter ((foreign-lambda* message-iter-ptr ((message-ptr msg))
418                                "DBusMessageIter* i = malloc(sizeof(DBusMessageIter));
419                                if (!dbus_message_iter_init (msg, i))
420                                        i = (DBusMessageIter*)0;        // Message has no parameters
421                                C_return (i);") msg) ]
422                                [has-next iter]
423                                )
424                        (lambda ()
425                                (if has-next
426                                        (let ([ret (iter-cond iter)])
427                                                (set! has-next ((foreign-lambda bool
428                                                        "dbus_message_iter_next" message-iter-ptr) iter))
429                                                ret     )
430                                        (begin
431                                                (free-iter iter)
432                                                iterm)
433                                ))))
434
435        ;; todo maybe: rewrite to avoid the reverse
436        (define (iter->list iter)
437                (let loop ([retval '()])
438                        (let ([next (iter)])
439                                (if (eq? next iterm)
440                                        (reverse retval)
441                                        (loop (cons next retval))))))
442
443        (define (iter->vector iter)
444                (let ([l (iter->list iter)])
445                        (list->vector l)))
446
447        (define msg-path
448                (foreign-lambda c-string "dbus_message_get_path" message-ptr))
449
450        (define msg-interface
451                (foreign-lambda c-string "dbus_message_get_interface" message-ptr))
452
453        (define msg-member
454                (foreign-lambda c-string "dbus_message_get_member" message-ptr))
455
456        (define msg-error-name
457                (foreign-lambda c-string "dbus_message_get_error_name" message-ptr))
458
459        (define msg-service
460                (foreign-lambda c-string "dbus_message_get_destination" message-ptr))
461
462        (set! find-callback (lambda (bus msg)
463                (let ([path (string?->symbol (msg-path msg))]
464                                [iface (string?->symbol (msg-interface msg))]
465                                [mber (string?->symbol (msg-member msg))]
466                                [svc (string?->symbol (msg-service msg))]
467                                )
468                        ; (printf "   svc ~s~%" svc)
469                        ; (printf "   path ~s~%" path)
470                        ; (printf "   iface ~s~%" iface)
471                        ; (printf "   mber ~s~%" mber)
472                        ;; The service name is not included as part of the signal, so svc will be #f.
473                        ;; In that case the callback is registered under bus/path/iface/signal-name.
474                        (if svc
475                                (tassq callbacks-table bus path svc iface mber)
476                                (tassq callbacks-table bus path iface mber) ))))
477
478        (set! dbus:make-context (lambda (#!key (bus dbus:session-bus) service interface (path "/"))
479                (vector (next-context-ID) bus (string?->symbol service)
480                        (string?->symbol path) (string?->symbol interface)) ))
481
482        (define send-impl
483                (foreign-lambda int "dbus_connection_send" connection-ptr message-ptr uint-ptr))
484
485        (set! dbus:send (lambda (context name . params)
486                (let* ( [service (symbol?->string (vector-ref context context-idx-service))]
487                                [msg (make-signal
488                                                        (symbol?->string (vector-ref context context-idx-path))
489                                                        (symbol?->string (vector-ref context context-idx-interface))
490                                                        name)]
491                                [iter (make-iter-append msg)] )
492                        (let ([conn (conn-or-abort (vector-ref context context-idx-bus))])
493                                ; (exists-or-abort conn (format "no connection to bus ~s~%" (vector-ref context context-idx-bus)))
494                                (for-each (lambda (parm)
495                                        (iter-append-basic iter parm))  params)
496                                (send-impl conn msg #f)
497                                (free-iter iter)
498                                ; ((foreign-lambda void "dbus_connection_flush" connection-ptr) conn)
499                        ))))
500
501        (set! dbus:call (lambda (context name . params)
502                (let* ( [service (symbol->string (vector-ref context context-idx-service))]
503                                [msg (make-message service
504                                                        (symbol->string (vector-ref context context-idx-path))
505                                                        (symbol->string (vector-ref context context-idx-interface))
506                                                        name)]
507                                [iter (make-iter-append msg)] )
508                        (let ([conn (conn-or-abort (vector-ref context context-idx-bus))])
509                                ; (exists-or-abort conn (format "no connection to bus ~s~%" (vector-ref context context-idx-bus)))
510                                (for-each (lambda (parm)
511                                        (iter-append-basic iter parm))  params)
512                                (free-iter iter)
513                                (let* ( [reply-msg ((foreign-lambda* message-ptr ((connection-ptr conn) (message-ptr msg))
514                                                        ;; idealistic code here; todo: error checking
515                                                        ;; todo: timeout comes from where?  (make-parameter) maybe
516                                                        "DBusMessage *reply;
517                                                        DBusError error;
518                                                        dbus_error_init (&error);
519                                                        reply = dbus_connection_send_with_reply_and_block(conn, msg, 5000, &error);
520                                                        if (dbus_error_is_set (&error))
521                                                                fprintf (stderr, \"Error %s: %s\\n\", error.name, error.message);
522                                                        dbus_message_unref(msg);
523                                                        C_return(reply);") conn msg) ]
524                                                [reply-iter (make-iter reply-msg)]
525                                                [reply-args (iter->list reply-iter)] )
526                                        reply-args)))))
527
528        (set! dbus:make-method-proxy (lambda (context name)
529                (let (  [service (symbol->string (vector-ref context context-idx-service))]
530                                [conn (conn-or-abort (vector-ref context context-idx-bus))] )
531                                ; (exists-or-abort conn (format "no connection to bus ~s~%" (vector-ref context context-idx-bus)))
532                                (lambda params
533                                        (let* ( [msg (make-message service
534                                                                        (symbol->string (vector-ref context context-idx-path))
535                                                                        (symbol->string (vector-ref context context-idx-interface))
536                                                                        name)]
537                                                        [iter (make-iter-append msg)] )
538                                                (for-each (lambda (parm)
539                                                        (iter-append-basic iter parm))  params)
540                                                (free-iter iter)
541                                                ;; TODO: pull this out into a helper function
542                                                (let* ( [reply-msg ((foreign-lambda* message-ptr ((connection-ptr conn) (message-ptr msg))
543                                                                        ;; idealistic code here; todo: error checking
544                                                                        "DBusPendingCall* pending;
545                                                                        dbus_connection_send_with_reply(conn, msg, &pending, -1);
546                                                                        dbus_connection_flush(conn);
547                                                                        dbus_message_unref(msg);
548                                                                        dbus_pending_call_block(pending);
549                                                                        msg = dbus_pending_call_steal_reply(pending);
550                                                                        C_return(msg);") conn msg) ]
551                                                                [reply-iter (make-iter reply-msg)]
552                                                                [reply-args (iter->list reply-iter)] )
553                                                        reply-args))))))
554
555        (define-foreign-record-type (dbus:vtable "struct DBusObjectPathVTable")
556                (constructor: dbus:make-vtable-impl)
557                (destructor: dbus:free-vtable)
558                (c-pointer unregister_function dbus:vtable-unregister_function dbus:vtable-unregister_function-set!)
559                (c-pointer message_function dbus:vtable-message_function dbus:vtable-message_function-set!)
560                (c-pointer dbus_internal_pad1 dbus:vtable-dbus_internal_pad1)
561                (c-pointer dbus_internal_pad2 dbus:vtable-dbus_internal_pad2)
562                (c-pointer dbus_internal_pad3 dbus:vtable-dbus_internal_pad3)
563                (c-pointer dbus_internal_pad4 dbus:vtable-dbus_internal_pad4))
564
565        (define (dbus:make-vtable cb unreg-cb)
566                (let ()
567                        (define (fn conn msg user-data)
568                                ; (printf "fixin' to call ~a with ~a, ~a, ~a~%" cb conn msg user-data)
569                                (let ([ret (cb conn msg user-data)])
570                                        ;; TODO: return ret as the result
571                                        dbus:result-handled ))
572                        (let ([ret (dbus:make-vtable-impl)])
573                                (dbus:vtable-message_function-set! ret fn)
574                                (dbus:vtable-unregister_function-set! ret unreg-cb)
575                                ret) ))
576
577        ; (set! dbus:add-match-self (lambda ()
578                ; ((foreign-safe-lambda void "dbus_bus_add_match" connection-ptr c-string error-ptr)
579                        ; (get-conn (vector-ref context context-idx-bus)) rule #f) ))
580
581        (set! dbus:read-write (lambda (conn timeout)
582                (let ()
583                        ((foreign-safe-lambda bool "dbus_connection_read_write" connection-ptr int)
584                                conn timeout))))
585
586        (set! dbus:request-name (lambda (context)
587                (let ([service-name (symbol?->string (vector-ref context context-idx-service))])
588                        (conn-or-abort (vector-ref context context-idx-bus))
589                        (when service-name
590                                ((foreign-safe-lambda void "dbus_bus_request_name" connection-ptr c-string int error-ptr)
591                                        (get-conn (vector-ref context context-idx-bus))
592                                        service-name
593                                        dbus:name-flag-replace-existing #f) ))))
594
595        (set! dbus:add-match (lambda (context)
596                ;; TODO is it always type signal?  We are using this for methods too.
597                (let ([rule (format "type='signal', interface='~s'" (vector-ref context context-idx-interface))])
598                        (conn-or-abort (vector-ref context context-idx-bus))
599                        ((foreign-safe-lambda void "dbus_bus_add_match" connection-ptr c-string error-ptr)
600                                (get-conn (vector-ref context context-idx-bus)) rule #f) )))
601
602        ;; return #t if it received a message, #f if not
603        (set! dbus:poll-for-message (lambda (#!key (bus dbus:session-bus) (timeout 0))
604                (let ([conn (conn-or-abort bus)])
605                        ; (exists-or-abort conn (format "no connection to bus ~s~%" (vector-ref context context-idx-bus)))
606                        ((foreign-safe-lambda* bool
607                                ((connection-ptr conn) (dbus:bus bus) (int timeout))
608                                "DBusMessage* msg = NULL;
609                                dbus_connection_read_write(conn, timeout);
610                                msg = dbus_connection_pop_message(conn);
611                                if (msg)
612                                {
613                                        //printf(\"rcv: %s\\n\", dbus_message_get_interface(msg));
614                                        C_msg_cb(bus, msg);
615                                        dbus_message_unref(msg);
616                                        C_return(true);         // yes there was a message
617                                }
618                                C_return (false);               // we polled, we came back empty-handed
619                                ") conn bus timeout)
620                )))
621
622        ;; TODO: one polling thread is necessary for each connection
623        (define (dbus:start-polling! bus interval)
624                (vector-set! polling-interval bus interval)
625                ; (pretty-print callbacks-table)
626                (when (vector-ref polling-enabled bus)
627                        (unless (vector-ref polling-threads bus)
628                                (vector-set! polling-threads bus (thread-start! (lambda ()
629                                        (let loop ()
630                                                ; (printf "polling~%")
631                                                (dbus:poll-for-message bus: bus timeout: 0)
632                                                (thread-sleep! (vector-ref polling-interval bus))
633                                                (when (vector-ref polling-enabled bus) (loop)))))))))
634
635        (set! dbus:enable-polling-thread! (lambda (#!key (bus dbus:session-bus) (enable #t) (interval default-polling-interval))
636                (vector-set! polling-enabled bus enable)
637                (if enable
638                        (dbus:start-polling! bus interval)
639                        (let ([th (vector-ref polling-threads bus)])
640                                (when th (thread-join! th))))))
641
642        ;; Wraps a user-provided callback so as to pass it the
643        ;; received dbus message's parameters, and return a dbus response
644        ;; with the parameter(s) returned from the callback.
645        ;; msg-cb is the user-provided one.
646        (define (method-wrapper conn msg-cb)
647                (lambda (msg)
648                        (let (  [args (iter->list (make-iter msg))]
649                                        [response ((foreign-lambda message-ptr
650                                                        "dbus_message_new_method_return" message-ptr) msg)])
651                                (let (  [ret (apply msg-cb args)]
652                                                [iter (make-iter-append response)] )
653                                        (if (pair? ret)
654                                                (for-each (lambda (parm)
655                                                        ; (printf "appending return parameter ~s~%" parm)
656                                                        (iter-append-basic iter parm))  ret)
657                                                (iter-append-basic iter ret))
658                                        ;; send response
659                                        (send-impl conn response #f)
660                                        (free-iter iter)
661                                        ))))
662
663        (define (handler-wrapper conn msg-cb)
664                (lambda (msg)
665                        (let ([args (iter->list (make-iter msg))])
666                                (apply msg-cb args)     )))
667
668        ;; msg-cb: the handler implementation.  Its return value is ignored.
669        (set! dbus:register-signal-handler (lambda (context name msg-cb)
670                (dbus:request-name context)
671                (dbus:add-match context)
672                (tasset! callbacks-table
673                        (handler-wrapper (conn-or-abort (vector-ref context context-idx-bus)) msg-cb)
674                        (vector-ref context context-idx-bus)
675                        (vector-ref context context-idx-path)
676                        (vector-ref context context-idx-interface)
677                        (string?->symbol name))
678                (dbus:start-polling! (vector-ref context context-idx-bus) default-polling-interval)
679        ))
680
681        ;; msg-cb: the method implementation.  Its return value is sent back as the response.
682        (set! dbus:register-method (lambda (context name msg-cb)
683                (dbus:request-name context)
684                ; (dbus:add-match context)      doesn't seem to be necessary
685                (tasset! callbacks-table
686                        (method-wrapper (conn-or-abort (vector-ref context context-idx-bus)) msg-cb)
687                        (vector-ref context context-idx-bus)
688                        (vector-ref context context-idx-path)
689                        (vector-ref context context-idx-service)
690                        (vector-ref context context-idx-interface)
691                        (string?->symbol name))
692                (dbus:start-polling! (vector-ref context context-idx-bus) default-polling-interval)
693        ))
694
695        ; dbus_bool_t dbus_connection_register_object_path   (DBusConnection              *connection,
696                                                                                                                ; const char                  *path,
697                                                                                                                ; const DBusObjectPathVTable  *vtable,
698                                                                                                                ; void                        *user_data);
699        (set! dbus:register-path (lambda (bus path fn unreg-fn)
700                ; (let ([unreg-fn (lambda (parm . rest) #f)])
701                ((foreign-safe-lambda bool "dbus_connection_register_object_path"
702                                connection-ptr c-string vtable-ptr c-pointer)
703                        (conn-or-abort bus)
704                        (symbol?->string path)
705                        (dbus:make-vtable fn unreg-fn) #f)))
706
707        (set! dbus:discover-api-xml (lambda (ctxt)
708                (let ([ctxt (list->vector (vector->list ctxt))])        ;; todo: efficiency?
709                        (vector-set! ctxt context-idx-interface 'org.freedesktop.DBus.Introspectable)
710                        (let ([xml (dbus:call ctxt "Introspect")])
711                                (and (pair? xml) (car xml))))))
712
713)
714)
Note: See TracBrowser for help on using the repository browser.