chiark / gitweb /
Removed obsolete TODO comment
[clg] / glib / gcallback.lisp
CommitLineData
55212af1 1;; Common Lisp bindings for GTK+ v2.x
2;; Copyright 2000 Espen S. Johnsen <espen@users.sf.net>
c9819f3e 3;;
55212af1 4;; Permission is hereby granted, free of charge, to any person obtaining
5;; a copy of this software and associated documentation files (the
6;; "Software"), to deal in the Software without restriction, including
7;; without limitation the rights to use, copy, modify, merge, publish,
8;; distribute, sublicense, and/or sell copies of the Software, and to
9;; permit persons to whom the Software is furnished to do so, subject to
10;; the following conditions:
c9819f3e 11;;
55212af1 12;; The above copyright notice and this permission notice shall be
13;; included in all copies or substantial portions of the Software.
c9819f3e 14;;
55212af1 15;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
865efd45 23;; $Id: gcallback.lisp,v 1.40 2007/02/19 13:46:44 espen Exp $
c9819f3e 24
25(in-package "GLIB")
26
27(use-prefix "g")
28
29
40d51d98 30;;;; Callback invocation
c9819f3e 31
f7573853 32(deftype gclosure () 'pointer)
33(register-type 'gclosure '|g_closure_get_type|)
34
60cfb912 35(defun register-callback-function (function)
36 (check-type function (or null symbol function))
37 (register-user-data function))
c9819f3e 38
26109728 39;; Callback marshaller for regular signal handlers
40(define-callback signal-handler-marshal nil
a92553bd 41 ((gclosure gclosure) (return-value gvalue) (n-params unsigned-int)
42 (param-values pointer) (invocation-hint pointer)
26109728 43 (callback-id unsigned-long))
08d14e5e 44 (declare (ignore gclosure invocation-hint))
26109728 45 (callback-trampoline #'invoke-signal-handler callback-id n-params param-values return-value))
c9819f3e 46
26109728 47;; Callback marshaller for class handlers
48(define-callback class-handler-marshal nil
49 ((gclosure gclosure) (return-value gvalue) (n-params unsigned-int)
50 (param-values pointer) (invocation-hint pointer)
51 (callback-id unsigned-long))
52 (declare (ignore gclosure invocation-hint))
53 (callback-trampoline #'invoke-callback callback-id n-params param-values return-value))
54
55;; Callback marshaller for emission hooks
56(define-callback emission-hook-marshal nil
a92553bd 57 ((invocation-hint pointer) (n-params unsigned-int) (param-values pointer)
26109728 58 (callback-id unsigned-long))
f84e7a8e 59 (declare (ignore invocation-hint))
26109728 60 (callback-trampoline #'invoke-callback callback-id n-params param-values))
3b8e5eb0 61
26109728 62(defun callback-trampoline (restart-wrapper callback-id n-params param-values
63 &optional (return-value (make-pointer 0)))
c9819f3e 64 (let* ((return-type (unless (null-pointer-p return-value)
60cfb912 65 (gvalue-type return-value)))
831668e8 66 (args (loop
67 for n from 0 below n-params
ad112f20 68 for offset from 0 by +gvalue-size+
10ede675 69 collect (gvalue-peek (pointer+ param-values offset)))))
ad112f20 70 (unwind-protect
26109728 71 (multiple-value-bind (result aborted-p)
72 (apply restart-wrapper callback-id nil args)
73 (when (and return-type (not aborted-p))
ad112f20 74 (gvalue-set return-value result)))
10ede675 75 ;; TODO: this should be made more general, by adding a type
26109728 76 ;; method to return invalidating functions.
ad112f20 77 (loop
78 for arg in args
10ede675 79 when (typep arg 'struct)
ad112f20 80 do (invalidate-instance arg)))))
81
26109728 82(defun invoke-signal-handler (callback-id return-type &rest args)
83 (declare (ignore return-type))
84 (let* ((instance (first args))
85 (handler-id (signal-handler-find instance '(:data)
86 0 0 nil nil callback-id)))
87 (signal-handler-block instance handler-id)
88 (unwind-protect
89 (restart-case (apply #'invoke-callback callback-id nil args)
90 (abort () :report "Disconnect and exit signal handler"
91 (when (signal-handler-is-connected-p instance handler-id)
92 (signal-handler-disconnect instance handler-id))
93 (values nil t))))
94 (when (signal-handler-is-connected-p instance handler-id)
95 (signal-handler-unblock instance handler-id))))
831668e8 96
7bde5a67 97(defun invoke-callback (callback-id return-type &rest args)
26109728 98 (restart-case (apply (find-user-data callback-id) args)
831668e8 99 (continue nil :report "Return from callback function"
26109728 100 (cond
101 (return-type
102 (format *query-io* "Enter return value of type ~S: " return-type)
103 (force-output *query-io*)
104 (eval (read *query-io*)))
105 (t (values nil t))))
831668e8 106 (re-invoke nil :report "Re-invoke callback function"
26109728 107 (apply #'invoke-callback callback-id return-type args))))
c9819f3e 108
c9819f3e 109
60cfb912 110;;;; Timeouts and idle functions
111
0f2fb864 112(defconstant +priority-high+ -100)
113(defconstant +priority-default+ 0)
114(defconstant +priority-high-idle+ 100)
115(defconstant +priority-default-idle+ 200)
116(defconstant +priority-low+ 300)
117
118(defbinding source-remove () boolean
119 (tag unsigned-int))
120
a92553bd 121(define-callback source-callback-marshal nil ((callback-id unsigned-int))
7ff34bf3 122 (callback-trampoline #'invoke-callback callback-id 0 nil))
60cfb912 123
124(defbinding (timeout-add "g_timeout_add_full")
0f2fb864 125 (interval function &optional (priority +priority-default+)) unsigned-int
60cfb912 126 (priority int)
127 (interval unsigned-int)
a92553bd 128 (source-callback-marshal callback)
60cfb912 129 ((register-callback-function function) unsigned-long)
a92553bd 130 (user-data-destroy-callback callback))
60cfb912 131
0f2fb864 132(defun timeout-remove (timeout)
133 (source-remove timeout))
134
60cfb912 135(defbinding (idle-add "g_idle_add_full")
0f2fb864 136 (function &optional (priority +priority-default-idle+)) unsigned-int
60cfb912 137 (priority int)
a92553bd 138 (source-callback-marshal callback)
60cfb912 139 ((register-callback-function function) unsigned-long)
a92553bd 140 (user-data-destroy-callback callback))
60cfb912 141
0f2fb864 142(defun idle-remove (idle)
143 (source-remove idle))
60cfb912 144
c9819f3e 145
3b8e5eb0 146;;;; Signal information querying
c9819f3e 147
3b8e5eb0 148(defbinding signal-lookup (name type) unsigned-int
c9819f3e 149 ((signal-name-to-string name) string)
3b8e5eb0 150 ((find-type-number type t) type-number))
c9819f3e 151
3b8e5eb0 152(defbinding signal-name () (copy-of string)
c9819f3e 153 (signal-id unsigned-int))
154
3b8e5eb0 155(defbinding signal-list-ids (type) (vector unsigned-int n-ids)
156 ((find-type-number type t) type-number)
157 (n-ids unsigned-int :out))
158
159(defun signal-list-names (type)
160 (map 'list #'signal-name (signal-list-ids type)))
161
162(defun ensure-signal-id-from-type (signal-id type)
c9819f3e 163 (etypecase signal-id
3b8e5eb0 164 (integer (if (signal-name signal-id)
165 signal-id
166 (error "Invalid signal id: ~D" signal-id)))
167 ((or symbol string)
168 (let ((numeric-id (signal-lookup signal-id type)))
169 (if (zerop numeric-id)
170 (error "Invalid signal name for ~S: ~D" type signal-id)
171 numeric-id)))))
172
173(defun ensure-signal-id (signal-id instance)
174 (ensure-signal-id-from-type signal-id (type-of instance)))
c9819f3e 175
3b8e5eb0 176(eval-when (:compile-toplevel :load-toplevel :execute)
177 (deftype signal-flags ()
178 '(flags :run-first :run-last :run-cleanup :no-recurse
179 :detailed :action :no-hooks))
180
26109728 181 (define-flags-type signal-match-type
182 :id :detail :closure :func :data :unblocked)
183
3b8e5eb0 184 (defclass signal-query (struct)
185 ((id :allocation :alien :type unsigned-int)
186 (name :allocation :alien :type (copy-of string))
187 (type :allocation :alien :type type-number)
188 (flags :allocation :alien :type signal-flags)
189 (return-type :allocation :alien :type type-number)
190 (n-params :allocation :alien :type unsigned-int)
191 (param-types :allocation :alien :type pointer))
192 (:metaclass struct-class)))
193
194(defbinding signal-query
195 (signal-id &optional (signal-query (make-instance 'signal-query))) nil
196 (signal-id unsigned-int)
10ede675 197 (signal-query signal-query :in/return))
3b8e5eb0 198
199(defun signal-param-types (info)
200 (with-slots (n-params param-types) info
201 (map-c-vector 'list
202 #'(lambda (type-number)
203 (type-from-number type-number))
204 param-types 'type-number n-params)))
205
206
207(defun describe-signal (signal-id &optional type)
208 (let ((info (signal-query (ensure-signal-id-from-type signal-id type))))
209 (with-slots (id name type flags return-type n-params) info
40d51d98 210 (format t "The signal with id ~D is named '~A' and may be emitted on instances of type ~S." id name (type-from-number type t))
211 (when flags
212 (format t " It has the followin invocation flags: ~{~S ~}" flags))
213 (format t "~%~%Signal handlers should take ~A and return ~A~%"
3b8e5eb0 214 (if (zerop n-params)
215 "no arguments"
216 (format nil "arguments with the following types: ~A"
40d51d98 217 (signal-param-types info)))
218 (cond
219 ((= return-type (find-type-number "void")) "no values")
220 ((not (type-from-number return-type)) "values of unknown type")
221 ((format nil "values of type ~S" (type-from-number return-type))))))))
3b8e5eb0 222
223
224;;;; Signal connecting and controlling
225
2d3de529 226(defvar *overridden-signals* (make-hash-table :test 'equalp))
227
228(defbinding %signal-override-class-closure () nil
229 (signal-id unsigned-int)
230 (type-number type-number)
231 (callback-closure pointer))
232
233
234(defun signal-override-class-closure (name type function)
235 (let* ((signal-id (ensure-signal-id-from-type name type))
236 (type-number (find-type-number type t))
237 (callback-id (gethash (cons type-number signal-id) *overridden-signals*)))
238 (if callback-id
239 (update-user-data callback-id function)
240 (multiple-value-bind (callback-closure callback-id)
26109728 241 (make-callback-closure function class-handler-marshal)
2d3de529 242 (%signal-override-class-closure signal-id type-number callback-closure)
243 (setf
244 (gethash (cons type-number signal-id) *overridden-signals*)
245 callback-id)))))
246
247
248(defbinding %signal-chain-from-overridden () nil
249 (args pointer)
250 (return-value (or null gvalue)))
251
e9151788 252
253(defun %call-next-handler (n-params types args return-type)
2d3de529 254 (let ((params (allocate-memory (* n-params +gvalue-size+))))
255 (loop
e9151788 256 for arg in args
2d3de529 257 for type in types
258 for offset from 0 by +gvalue-size+
10ede675 259 do (gvalue-init (pointer+ params offset) type arg))
2d3de529 260
261 (unwind-protect
262 (if return-type
263 (with-gvalue (return-value return-type)
264 (%signal-chain-from-overridden params return-value))
265 (%signal-chain-from-overridden params nil))
266 (progn
267 (loop
268 repeat n-params
269 for offset from 0 by +gvalue-size+
10ede675 270 do (gvalue-unset (pointer+ params offset)))
2d3de529 271 (deallocate-memory params)))))
272
273
274(defmacro define-signal-handler (name ((object class) &rest args) &body body)
275 (let* ((info (signal-query (ensure-signal-id-from-type name class)))
276 (types (cons class (signal-param-types info)))
277 (n-params (1+ (slot-value info 'n-params)))
278 (return-type (type-from-number (slot-value info 'return-type)))
279 (vars (loop
280 for arg in args
281 until (eq arg '&rest)
282 collect arg))
283 (rest (cadr (member '&rest args)))
e9151788 284 (next (make-symbol "ARGS"))
285 (default (make-symbol "DEFAULT")))
2d3de529 286
287 `(progn
288 (signal-override-class-closure ',name ',class
289 #'(lambda (,object ,@args)
e9151788 290 (let ((,default (list* ,object ,@vars ,rest)))
291 (flet ((call-next-handler (&rest ,next)
2d3de529 292 (%call-next-handler
2e8019d5 293 ,n-params ',types (or ,next ,default) ',return-type)))
294 ,@body))))
2d3de529 295 ',name)))
296
297
3b8e5eb0 298(defbinding %signal-stop-emission () nil
c9819f3e 299 (instance ginstance)
3b8e5eb0 300 (signal-id unsigned-int)
301 (detail quark))
302
303(defvar *signal-stop-emission* nil)
304(declaim (special *signal-stop-emission*))
c9819f3e 305
3b8e5eb0 306(defun signal-stop-emission ()
307 (if *signal-stop-emission*
308 (funcall *signal-stop-emission*)
309 (error "Not inside a signal handler")))
310
311
312(defbinding signal-add-emission-hook (type signal function &key (detail 0))
26109728 313 unsigned-long
3b8e5eb0 314 ((ensure-signal-id-from-type signal type) unsigned-int)
315 (detail quark)
26109728 316 (emission-hook-marshal callback)
3b8e5eb0 317 ((register-callback-function function) unsigned-int)
a92553bd 318 (user-data-destroy-callback callback))
3b8e5eb0 319
320(defbinding signal-remove-emission-hook (type signal hook-id) nil
321 ((ensure-signal-id-from-type signal type) unsigned-int)
26109728 322 (hook-id unsigned-long))
c9819f3e 323
c9819f3e 324
3f4249c7 325(defbinding (signal-has-handler-pending-p "g_signal_has_handler_pending")
c9819f3e 326 (instance signal-id &key detail blocked) boolean
327 (instance ginstance)
7eec806d 328 ((ensure-signal-id signal-id instance) unsigned-int)
c9819f3e 329 ((or detail 0) quark)
3d36c5d6 330 (blocked boolean))
c9819f3e 331
26109728 332(defbinding %signal-connect-closure-by-id () unsigned-long
c9819f3e 333 (instance ginstance)
3b8e5eb0 334 (signal-id unsigned-int)
335 (detail quark)
336 (closure pointer)
c9819f3e 337 (after boolean))
338
3f4249c7 339(defbinding signal-handler-block () nil
c9819f3e 340 (instance ginstance)
26109728 341 (handler-id unsigned-long))
c9819f3e 342
3f4249c7 343(defbinding signal-handler-unblock () nil
c9819f3e 344 (instance ginstance)
26109728 345 (handler-id unsigned-long))
346
347;; Internal
348(defbinding signal-handler-find () unsigned-long
349 (instance gobject)
350 (mask signal-match-type)
351 (signal-id unsigned-int)
352 (detail quark)
353 (closure (or null pointer))
354 (func (or null pointer))
355 (data unsigned-long))
c9819f3e 356
3f4249c7 357(defbinding signal-handler-disconnect () nil
c9819f3e 358 (instance ginstance)
26109728 359 (handler-id unsigned-long))
3b8e5eb0 360
361(defbinding signal-handler-is-connected-p () boolean
362 (instance ginstance)
26109728 363 (handler-id unsigned-long))
c9819f3e 364
9c7196d0 365(defbinding (closure-new "g_cclosure_new") () gclosure
366 ((make-pointer #xFFFFFFFF) pointer)
3b8e5eb0 367 (callback-id unsigned-int)
a92553bd 368 (destroy-notify callback))
c9819f3e 369
9c7196d0 370(defbinding closure-set-meta-marshal () nil
371 (gclosure gclosure)
372 (callback-id unsigned-int)
373 (callback callback))
374
375(defun callback-closure-new (callback-id callback destroy-notify)
376 (let ((gclosure (closure-new callback-id destroy-notify)))
377 (closure-set-meta-marshal gclosure callback-id callback)
378 gclosure))
379
26109728 380(defun make-callback-closure (function marshaller)
3b8e5eb0 381 (let ((callback-id (register-callback-function function)))
382 (values
26109728 383 (callback-closure-new callback-id marshaller user-data-destroy-callback)
3b8e5eb0 384 callback-id)))
385
40d51d98 386(defgeneric compute-signal-function (gobject signal function object args))
a6e13fb0 387
40d51d98 388(defmethod compute-signal-function ((gobject gobject) signal function object args)
54ea42fe 389 (declare (ignore signal))
3b8e5eb0 390 (cond
40d51d98 391 ((or (eq object t) (eq object gobject))
392 (if args
393 #'(lambda (&rest emission-args)
394 (apply function (nconc emission-args args)))
395 function))
396 (object
397 (if args
398 #'(lambda (&rest emission-args)
399 (apply function object (nconc (rest emission-args) args)))
400 #'(lambda (&rest emission-args)
401 (apply function object (rest emission-args)))))
402 (args
403 #'(lambda (&rest emission-args)
404 (apply function (nconc (rest emission-args) args))))
3b8e5eb0 405 (t
40d51d98 406 #'(lambda (&rest emission-args)
407 (apply function (rest emission-args))))))
54ea42fe 408
409(defgeneric compute-signal-id (gobject signal))
410
411(defmethod compute-signal-id ((gobject gobject) signal)
412 (ensure-signal-id signal gobject))
413
414
40d51d98 415(defgeneric signal-connect (gobject signal function &key detail after object remove args))
54ea42fe 416
417(defmethod signal-connect :around ((gobject gobject) signal function &rest args)
418 (declare (ignore gobject signal args))
419 (when function
420 (call-next-method)))
3b8e5eb0 421
a6e13fb0 422
3b8e5eb0 423(defmethod signal-connect ((gobject gobject) signal function
40d51d98 424 &key detail after object remove args)
3b8e5eb0 425"Connects a callback function to a signal for a particular object. If
426:OBJECT is T, the object connected to is passed as the first argument
427to the callback function, or if :OBJECT is any other non NIL value, it
428is passed as the first argument instead. If :AFTER is non NIL, the
429handler will be called after the default handler for the signal. If
430:REMOVE is non NIL, the handler will be removed after beeing invoked
40d51d98 431once. ARGS is a list of additional arguments passed to the callback
432function."
54ea42fe 433(let* ((signal-id (compute-signal-id gobject signal))
434 (detail-quark (if detail (quark-intern detail) 0))
435 (signal-stop-emission
436 #'(lambda ()
437 (%signal-stop-emission gobject signal-id detail-quark)))
40d51d98 438 (callback (compute-signal-function gobject signal function object args))
54ea42fe 439 (wrapper #'(lambda (&rest args)
440 (let ((*signal-stop-emission* signal-stop-emission))
441 (apply callback args)))))
3b8e5eb0 442 (multiple-value-bind (closure-id callback-id)
26109728 443 (make-callback-closure wrapper signal-handler-marshal)
3b8e5eb0 444 (let ((handler-id (%signal-connect-closure-by-id
54ea42fe 445 gobject signal-id detail-quark closure-id after)))
3b8e5eb0 446 (when remove
447 (update-user-data callback-id
448 #'(lambda (&rest args)
449 (unwind-protect
450 (let ((*signal-stop-emission* signal-stop-emission))
451 (apply callback args))
26109728 452 (when (signal-handler-is-connected-p gobject handler-id)
453 (signal-handler-disconnect gobject handler-id))))))
54ea42fe 454 handler-id))))
3b8e5eb0 455
456
457;;;; Signal emission
458
459(defbinding %signal-emitv () nil
460 (gvalues pointer)
461 (signal-id unsigned-int)
462 (detail quark)
463 (return-value gvalue))
464
465(defvar *signal-emit-functions* (make-hash-table))
466
467(defun create-signal-emit-function (signal-id)
468 (let ((info (signal-query signal-id)))
469 (let* ((type (type-from-number (slot-value info 'type)))
470 (param-types (cons type (signal-param-types info)))
471 (return-type (type-from-number (slot-value info 'return-type)))
472 (n-params (1+ (slot-value info 'n-params)))
473 (params (allocate-memory (* n-params +gvalue-size+))))
474 #'(lambda (detail object &rest args)
475 (unless (= (length args) (1- n-params))
476 (error "Invalid number of arguments: ~A" (+ 2 (length args))))
477 (unwind-protect
478 (loop
479 for arg in (cons object args)
480 for type in param-types
10ede675 481 as tmp = params then (pointer+ tmp +gvalue-size+)
3b8e5eb0 482 do (gvalue-init tmp type arg)
483 finally
484 (if return-type
485 (return
486 (with-gvalue (return-value)
487 (%signal-emitv params signal-id detail return-value)))
488 (%signal-emitv params signal-id detail (make-pointer 0))))
489 (loop
490 repeat n-params
10ede675 491 as tmp = params then (pointer+ tmp +gvalue-size+)
3b8e5eb0 492 while (gvalue-p tmp)
493 do (gvalue-unset tmp)))))))
494
495(defun signal-emit-with-detail (object signal detail &rest args)
496 (let* ((signal-id (ensure-signal-id signal object))
497 (function (or
498 (gethash signal-id *signal-emit-functions*)
499 (setf
500 (gethash signal-id *signal-emit-functions*)
501 (create-signal-emit-function signal-id)))))
502 (apply function detail object args)))
503
504(defun signal-emit (object signal &rest args)
505 (apply #'signal-emit-with-detail object signal 0 args))
506
dd181a20 507
40d51d98 508;;;; Signal registration
509
510(defbinding %signal-newv (name itype flags return-type param-types)
511 unsigned-int
512 ((signal-name-to-string name) string)
513 (itype gtype)
514 (flags signal-flags)
515 (nil null) ; class closure
516 (nil null) ; accumulator
517 (nil null) ; accumulator data
518 (nil null) ; c marshaller
519 (return-type gtype)
520 ((length param-types) unsigned-int)
521 (param-types (vector gtype)))
522
523(defun signal-new (name itype flags return-type param-types)
524 (when (zerop (signal-lookup name itype))
525 (%signal-newv name itype flags return-type param-types)))
526
11e1e57c 527;;;; Convenient macros
528
a92553bd 529(defmacro define-callback-marshal (name return-type args &key (callback-id :last))
530 (let* ((ignore ())
531 (params ())
532 (names (loop
533 for arg in args
534 collect (if (or
535 (eq arg :ignore)
536 (and (consp arg) (eq (first arg) :ignore)))
537 (let ((name (gensym "IGNORE")))
538 (push name ignore)
539 name)
540 (let ((name (if (atom arg)
541 (gensym (string arg))
542 (first arg))))
543 (push name params)
544 name))))
545 (types (loop
546 for arg in args
547 collect (cond
548 ((eq arg :ignore) 'pointer)
549 ((atom arg) arg)
550 (t (second arg))))))
551 `(define-callback ,name ,return-type
552 ,(ecase callback-id
865efd45 553 (:first `((callback-id pointer-data) ,@(mapcar #'list names types)))
554 (:last `(,@(mapcar #'list names types) (callback-id pointer-data))))
a92553bd 555 (declare (ignore ,@ignore))
ad3e0b2b 556 (invoke-callback callback-id ',return-type ,@(nreverse params)))))
11e1e57c 557
558(defmacro with-callback-function ((id function) &body body)
559 `(let ((,id (register-callback-function ,function)))
560 (unwind-protect
561 (progn ,@body)
562 (destroy-user-data ,id))))