chiark / gitweb /
Win32 patch applied
[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
4dc69f6a 23;; $Id: gcallback.lisp,v 1.43 2007/06/15 12:03:26 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)
6f937184 43 (callback-id pointer-data))
08d14e5e 44 (declare (ignore gclosure invocation-hint))
26109728 45 (callback-trampoline #'invoke-signal-handler callback-id n-params param-values return-value))
c9819f3e 46
6f937184 47;; Callback marshaller for class handlers
26109728 48(define-callback class-handler-marshal nil
49 ((gclosure gclosure) (return-value gvalue) (n-params unsigned-int)
50 (param-values pointer) (invocation-hint pointer)
6f937184 51 (callback-id pointer-data))
26109728 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)
6f937184 58 (callback-id pointer-data))
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)
4dc69f6a 90 (disconnect () :report "Disconnect and exit signal handler"
26109728 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
4dc69f6a 121(define-callback source-callback-marshal boolean ((callback-id unsigned-int))
122 (invoke-source-callback callback-id))
123
124(defun invoke-source-callback (callback-id)
125 (restart-case (funcall (find-user-data callback-id))
126 (remove () :report "Exit and remove source callback"
127 nil)
128 (continue () :report "Return from source callback"
129 t)
130 (re-invoke nil :report "Re-invoke source callback"
131 (invoke-source-callback callback-id))))
132
60cfb912 133
134(defbinding (timeout-add "g_timeout_add_full")
0f2fb864 135 (interval function &optional (priority +priority-default+)) unsigned-int
60cfb912 136 (priority int)
137 (interval unsigned-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 timeout-remove (timeout)
143 (source-remove timeout))
144
60cfb912 145(defbinding (idle-add "g_idle_add_full")
0f2fb864 146 (function &optional (priority +priority-default-idle+)) unsigned-int
60cfb912 147 (priority int)
a92553bd 148 (source-callback-marshal callback)
60cfb912 149 ((register-callback-function function) unsigned-long)
a92553bd 150 (user-data-destroy-callback callback))
60cfb912 151
0f2fb864 152(defun idle-remove (idle)
153 (source-remove idle))
60cfb912 154
c9819f3e 155
3b8e5eb0 156;;;; Signal information querying
c9819f3e 157
3b8e5eb0 158(defbinding signal-lookup (name type) unsigned-int
c9819f3e 159 ((signal-name-to-string name) string)
3b8e5eb0 160 ((find-type-number type t) type-number))
c9819f3e 161
3b8e5eb0 162(defbinding signal-name () (copy-of string)
c9819f3e 163 (signal-id unsigned-int))
164
3b8e5eb0 165(defbinding signal-list-ids (type) (vector unsigned-int n-ids)
166 ((find-type-number type t) type-number)
167 (n-ids unsigned-int :out))
168
169(defun signal-list-names (type)
170 (map 'list #'signal-name (signal-list-ids type)))
171
172(defun ensure-signal-id-from-type (signal-id type)
c9819f3e 173 (etypecase signal-id
3b8e5eb0 174 (integer (if (signal-name signal-id)
175 signal-id
176 (error "Invalid signal id: ~D" signal-id)))
177 ((or symbol string)
178 (let ((numeric-id (signal-lookup signal-id type)))
179 (if (zerop numeric-id)
180 (error "Invalid signal name for ~S: ~D" type signal-id)
181 numeric-id)))))
182
183(defun ensure-signal-id (signal-id instance)
184 (ensure-signal-id-from-type signal-id (type-of instance)))
c9819f3e 185
3b8e5eb0 186(eval-when (:compile-toplevel :load-toplevel :execute)
187 (deftype signal-flags ()
188 '(flags :run-first :run-last :run-cleanup :no-recurse
189 :detailed :action :no-hooks))
190
26109728 191 (define-flags-type signal-match-type
192 :id :detail :closure :func :data :unblocked)
193
3b8e5eb0 194 (defclass signal-query (struct)
195 ((id :allocation :alien :type unsigned-int)
196 (name :allocation :alien :type (copy-of string))
197 (type :allocation :alien :type type-number)
198 (flags :allocation :alien :type signal-flags)
199 (return-type :allocation :alien :type type-number)
200 (n-params :allocation :alien :type unsigned-int)
201 (param-types :allocation :alien :type pointer))
202 (:metaclass struct-class)))
203
204(defbinding signal-query
205 (signal-id &optional (signal-query (make-instance 'signal-query))) nil
206 (signal-id unsigned-int)
10ede675 207 (signal-query signal-query :in/return))
3b8e5eb0 208
209(defun signal-param-types (info)
210 (with-slots (n-params param-types) info
211 (map-c-vector 'list
212 #'(lambda (type-number)
213 (type-from-number type-number))
214 param-types 'type-number n-params)))
215
216
217(defun describe-signal (signal-id &optional type)
218 (let ((info (signal-query (ensure-signal-id-from-type signal-id type))))
219 (with-slots (id name type flags return-type n-params) info
40d51d98 220 (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))
221 (when flags
222 (format t " It has the followin invocation flags: ~{~S ~}" flags))
223 (format t "~%~%Signal handlers should take ~A and return ~A~%"
3b8e5eb0 224 (if (zerop n-params)
225 "no arguments"
226 (format nil "arguments with the following types: ~A"
40d51d98 227 (signal-param-types info)))
228 (cond
229 ((= return-type (find-type-number "void")) "no values")
230 ((not (type-from-number return-type)) "values of unknown type")
231 ((format nil "values of type ~S" (type-from-number return-type))))))))
3b8e5eb0 232
233
234;;;; Signal connecting and controlling
235
2d3de529 236(defvar *overridden-signals* (make-hash-table :test 'equalp))
237
238(defbinding %signal-override-class-closure () nil
239 (signal-id unsigned-int)
240 (type-number type-number)
241 (callback-closure pointer))
242
243
244(defun signal-override-class-closure (name type function)
245 (let* ((signal-id (ensure-signal-id-from-type name type))
246 (type-number (find-type-number type t))
247 (callback-id (gethash (cons type-number signal-id) *overridden-signals*)))
248 (if callback-id
249 (update-user-data callback-id function)
250 (multiple-value-bind (callback-closure callback-id)
26109728 251 (make-callback-closure function class-handler-marshal)
2d3de529 252 (%signal-override-class-closure signal-id type-number callback-closure)
253 (setf
254 (gethash (cons type-number signal-id) *overridden-signals*)
255 callback-id)))))
256
257
258(defbinding %signal-chain-from-overridden () nil
259 (args pointer)
260 (return-value (or null gvalue)))
261
e9151788 262
263(defun %call-next-handler (n-params types args return-type)
2d3de529 264 (let ((params (allocate-memory (* n-params +gvalue-size+))))
265 (loop
e9151788 266 for arg in args
2d3de529 267 for type in types
268 for offset from 0 by +gvalue-size+
10ede675 269 do (gvalue-init (pointer+ params offset) type arg))
2d3de529 270
271 (unwind-protect
272 (if return-type
273 (with-gvalue (return-value return-type)
274 (%signal-chain-from-overridden params return-value))
275 (%signal-chain-from-overridden params nil))
276 (progn
277 (loop
278 repeat n-params
279 for offset from 0 by +gvalue-size+
10ede675 280 do (gvalue-unset (pointer+ params offset)))
2d3de529 281 (deallocate-memory params)))))
282
283
284(defmacro define-signal-handler (name ((object class) &rest args) &body body)
285 (let* ((info (signal-query (ensure-signal-id-from-type name class)))
286 (types (cons class (signal-param-types info)))
287 (n-params (1+ (slot-value info 'n-params)))
288 (return-type (type-from-number (slot-value info 'return-type)))
289 (vars (loop
290 for arg in args
291 until (eq arg '&rest)
292 collect arg))
293 (rest (cadr (member '&rest args)))
e9151788 294 (next (make-symbol "ARGS"))
295 (default (make-symbol "DEFAULT")))
2d3de529 296
297 `(progn
298 (signal-override-class-closure ',name ',class
299 #'(lambda (,object ,@args)
e9151788 300 (let ((,default (list* ,object ,@vars ,rest)))
301 (flet ((call-next-handler (&rest ,next)
2d3de529 302 (%call-next-handler
2e8019d5 303 ,n-params ',types (or ,next ,default) ',return-type)))
304 ,@body))))
2d3de529 305 ',name)))
306
307
3b8e5eb0 308(defbinding %signal-stop-emission () nil
c9819f3e 309 (instance ginstance)
3b8e5eb0 310 (signal-id unsigned-int)
311 (detail quark))
312
313(defvar *signal-stop-emission* nil)
314(declaim (special *signal-stop-emission*))
c9819f3e 315
3b8e5eb0 316(defun signal-stop-emission ()
317 (if *signal-stop-emission*
318 (funcall *signal-stop-emission*)
319 (error "Not inside a signal handler")))
320
321
322(defbinding signal-add-emission-hook (type signal function &key (detail 0))
26109728 323 unsigned-long
3b8e5eb0 324 ((ensure-signal-id-from-type signal type) unsigned-int)
325 (detail quark)
26109728 326 (emission-hook-marshal callback)
3b8e5eb0 327 ((register-callback-function function) unsigned-int)
a92553bd 328 (user-data-destroy-callback callback))
3b8e5eb0 329
330(defbinding signal-remove-emission-hook (type signal hook-id) nil
331 ((ensure-signal-id-from-type signal type) unsigned-int)
26109728 332 (hook-id unsigned-long))
c9819f3e 333
c9819f3e 334
3f4249c7 335(defbinding (signal-has-handler-pending-p "g_signal_has_handler_pending")
c9819f3e 336 (instance signal-id &key detail blocked) boolean
337 (instance ginstance)
7eec806d 338 ((ensure-signal-id signal-id instance) unsigned-int)
c9819f3e 339 ((or detail 0) quark)
3d36c5d6 340 (blocked boolean))
c9819f3e 341
26109728 342(defbinding %signal-connect-closure-by-id () unsigned-long
c9819f3e 343 (instance ginstance)
3b8e5eb0 344 (signal-id unsigned-int)
345 (detail quark)
346 (closure pointer)
c9819f3e 347 (after boolean))
348
3f4249c7 349(defbinding signal-handler-block () nil
c9819f3e 350 (instance ginstance)
26109728 351 (handler-id unsigned-long))
c9819f3e 352
3f4249c7 353(defbinding signal-handler-unblock () nil
c9819f3e 354 (instance ginstance)
26109728 355 (handler-id unsigned-long))
356
357;; Internal
358(defbinding signal-handler-find () unsigned-long
359 (instance gobject)
360 (mask signal-match-type)
361 (signal-id unsigned-int)
362 (detail quark)
363 (closure (or null pointer))
364 (func (or null pointer))
99d59d2a 365 (data pointer-data))
c9819f3e 366
3f4249c7 367(defbinding signal-handler-disconnect () nil
c9819f3e 368 (instance ginstance)
26109728 369 (handler-id unsigned-long))
3b8e5eb0 370
371(defbinding signal-handler-is-connected-p () boolean
372 (instance ginstance)
26109728 373 (handler-id unsigned-long))
c9819f3e 374
9c7196d0 375(defbinding (closure-new "g_cclosure_new") () gclosure
376 ((make-pointer #xFFFFFFFF) pointer)
3b8e5eb0 377 (callback-id unsigned-int)
a92553bd 378 (destroy-notify callback))
c9819f3e 379
9c7196d0 380(defbinding closure-set-meta-marshal () nil
381 (gclosure gclosure)
382 (callback-id unsigned-int)
383 (callback callback))
384
385(defun callback-closure-new (callback-id callback destroy-notify)
386 (let ((gclosure (closure-new callback-id destroy-notify)))
387 (closure-set-meta-marshal gclosure callback-id callback)
388 gclosure))
389
99d59d2a 390(defun make-callback-closure (function &optional (marshaller signal-handler-marshal))
3b8e5eb0 391 (let ((callback-id (register-callback-function function)))
392 (values
26109728 393 (callback-closure-new callback-id marshaller user-data-destroy-callback)
3b8e5eb0 394 callback-id)))
395
40d51d98 396(defgeneric compute-signal-function (gobject signal function object args))
a6e13fb0 397
40d51d98 398(defmethod compute-signal-function ((gobject gobject) signal function object args)
54ea42fe 399 (declare (ignore signal))
3b8e5eb0 400 (cond
40d51d98 401 ((or (eq object t) (eq object gobject))
402 (if args
403 #'(lambda (&rest emission-args)
404 (apply function (nconc emission-args args)))
405 function))
406 (object
407 (if args
408 #'(lambda (&rest emission-args)
409 (apply function object (nconc (rest emission-args) args)))
410 #'(lambda (&rest emission-args)
411 (apply function object (rest emission-args)))))
412 (args
413 #'(lambda (&rest emission-args)
414 (apply function (nconc (rest emission-args) args))))
3b8e5eb0 415 (t
40d51d98 416 #'(lambda (&rest emission-args)
417 (apply function (rest emission-args))))))
54ea42fe 418
419(defgeneric compute-signal-id (gobject signal))
420
421(defmethod compute-signal-id ((gobject gobject) signal)
422 (ensure-signal-id signal gobject))
423
424
40d51d98 425(defgeneric signal-connect (gobject signal function &key detail after object remove args))
54ea42fe 426
427(defmethod signal-connect :around ((gobject gobject) signal function &rest args)
428 (declare (ignore gobject signal args))
429 (when function
430 (call-next-method)))
3b8e5eb0 431
a6e13fb0 432
3b8e5eb0 433(defmethod signal-connect ((gobject gobject) signal function
40d51d98 434 &key detail after object remove args)
3b8e5eb0 435"Connects a callback function to a signal for a particular object. If
436:OBJECT is T, the object connected to is passed as the first argument
437to the callback function, or if :OBJECT is any other non NIL value, it
438is passed as the first argument instead. If :AFTER is non NIL, the
439handler will be called after the default handler for the signal. If
440:REMOVE is non NIL, the handler will be removed after beeing invoked
40d51d98 441once. ARGS is a list of additional arguments passed to the callback
442function."
54ea42fe 443(let* ((signal-id (compute-signal-id gobject signal))
444 (detail-quark (if detail (quark-intern detail) 0))
445 (signal-stop-emission
446 #'(lambda ()
447 (%signal-stop-emission gobject signal-id detail-quark)))
40d51d98 448 (callback (compute-signal-function gobject signal function object args))
54ea42fe 449 (wrapper #'(lambda (&rest args)
450 (let ((*signal-stop-emission* signal-stop-emission))
451 (apply callback args)))))
3b8e5eb0 452 (multiple-value-bind (closure-id callback-id)
26109728 453 (make-callback-closure wrapper signal-handler-marshal)
3b8e5eb0 454 (let ((handler-id (%signal-connect-closure-by-id
54ea42fe 455 gobject signal-id detail-quark closure-id after)))
3b8e5eb0 456 (when remove
457 (update-user-data callback-id
458 #'(lambda (&rest args)
459 (unwind-protect
460 (let ((*signal-stop-emission* signal-stop-emission))
461 (apply callback args))
26109728 462 (when (signal-handler-is-connected-p gobject handler-id)
463 (signal-handler-disconnect gobject handler-id))))))
54ea42fe 464 handler-id))))
3b8e5eb0 465
466
467;;;; Signal emission
468
469(defbinding %signal-emitv () nil
470 (gvalues pointer)
471 (signal-id unsigned-int)
472 (detail quark)
473 (return-value gvalue))
474
475(defvar *signal-emit-functions* (make-hash-table))
476
477(defun create-signal-emit-function (signal-id)
478 (let ((info (signal-query signal-id)))
479 (let* ((type (type-from-number (slot-value info 'type)))
480 (param-types (cons type (signal-param-types info)))
481 (return-type (type-from-number (slot-value info 'return-type)))
482 (n-params (1+ (slot-value info 'n-params)))
483 (params (allocate-memory (* n-params +gvalue-size+))))
484 #'(lambda (detail object &rest args)
485 (unless (= (length args) (1- n-params))
486 (error "Invalid number of arguments: ~A" (+ 2 (length args))))
487 (unwind-protect
488 (loop
489 for arg in (cons object args)
490 for type in param-types
10ede675 491 as tmp = params then (pointer+ tmp +gvalue-size+)
3b8e5eb0 492 do (gvalue-init tmp type arg)
493 finally
494 (if return-type
495 (return
496 (with-gvalue (return-value)
497 (%signal-emitv params signal-id detail return-value)))
498 (%signal-emitv params signal-id detail (make-pointer 0))))
499 (loop
500 repeat n-params
10ede675 501 as tmp = params then (pointer+ tmp +gvalue-size+)
3b8e5eb0 502 while (gvalue-p tmp)
503 do (gvalue-unset tmp)))))))
504
505(defun signal-emit-with-detail (object signal detail &rest args)
506 (let* ((signal-id (ensure-signal-id signal object))
507 (function (or
508 (gethash signal-id *signal-emit-functions*)
509 (setf
510 (gethash signal-id *signal-emit-functions*)
511 (create-signal-emit-function signal-id)))))
512 (apply function detail object args)))
513
514(defun signal-emit (object signal &rest args)
515 (apply #'signal-emit-with-detail object signal 0 args))
516
dd181a20 517
40d51d98 518;;;; Signal registration
519
520(defbinding %signal-newv (name itype flags return-type param-types)
521 unsigned-int
522 ((signal-name-to-string name) string)
523 (itype gtype)
524 (flags signal-flags)
525 (nil null) ; class closure
526 (nil null) ; accumulator
527 (nil null) ; accumulator data
528 (nil null) ; c marshaller
529 (return-type gtype)
530 ((length param-types) unsigned-int)
531 (param-types (vector gtype)))
532
533(defun signal-new (name itype flags return-type param-types)
534 (when (zerop (signal-lookup name itype))
535 (%signal-newv name itype flags return-type param-types)))
536
11e1e57c 537;;;; Convenient macros
538
a92553bd 539(defmacro define-callback-marshal (name return-type args &key (callback-id :last))
540 (let* ((ignore ())
541 (params ())
542 (names (loop
543 for arg in args
544 collect (if (or
545 (eq arg :ignore)
546 (and (consp arg) (eq (first arg) :ignore)))
547 (let ((name (gensym "IGNORE")))
548 (push name ignore)
549 name)
550 (let ((name (if (atom arg)
551 (gensym (string arg))
552 (first arg))))
553 (push name params)
554 name))))
555 (types (loop
556 for arg in args
557 collect (cond
558 ((eq arg :ignore) 'pointer)
559 ((atom arg) arg)
560 (t (second arg))))))
561 `(define-callback ,name ,return-type
562 ,(ecase callback-id
865efd45 563 (:first `((callback-id pointer-data) ,@(mapcar #'list names types)))
564 (:last `(,@(mapcar #'list names types) (callback-id pointer-data))))
a92553bd 565 (declare (ignore ,@ignore))
ad3e0b2b 566 (invoke-callback callback-id ',return-type ,@(nreverse params)))))
11e1e57c 567
568(defmacro with-callback-function ((id function) &body body)
569 `(let ((,id (register-callback-function ,function)))
570 (unwind-protect
571 (progn ,@body)
572 (destroy-user-data ,id))))