chiark / gitweb /
Added type definition for gclosure
[clg] / glib / gcallback.lisp
CommitLineData
c8c48a4c 1;; Common Lisp bindings for GTK+ v2.0
2;; Copyright (C) 2000 Espen S. Johnsen <esj@stud.cs.uit.no>
3;;
4;; This library is free software; you can redistribute it and/or
5;; modify it under the terms of the GNU Lesser General Public
6;; License as published by the Free Software Foundation; either
7;; version 2 of the License, or (at your option) any later version.
8;;
9;; This library is distributed in the hope that it will be useful,
10;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12;; Lesser General Public License for more details.
13;;
14;; You should have received a copy of the GNU Lesser General Public
15;; License along with this library; if not, write to the Free Software
16;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
d75a77ff 18;; $Id: gcallback.lisp,v 1.18 2005-01-30 14:23:20 espen Exp $
c8c48a4c 19
20(in-package "GLIB")
21
22(use-prefix "g")
23
24
e0d2987b 25;;;; Callback invokation
c8c48a4c 26
e378b861 27(defun register-callback-function (function)
28 (check-type function (or null symbol function))
29 (register-user-data function))
c8c48a4c 30
8755b1a5 31(defcallback %destroy-user-data (nil (id unsigned-int))
32 (destroy-user-data id))
c8c48a4c 33
e0d2987b 34;; Callback marshal for regular signal handlers
35(defcallback closure-marshal (nil
36 (gclosure pointer)
37 (return-value gvalue)
38 (n-params unsigned-int)
39 (param-values pointer)
40 (invocation-hint pointer)
41 (callback-id unsigned-int))
42 (callback-trampoline callback-id n-params param-values return-value))
c8c48a4c 43
e0d2987b 44;; Callback function for emission hooks
45(defcallback signal-emission-hook (nil
46 (invocation-hint pointer)
47 (n-params unsigned-int)
48 (param-values pointer)
49 (callback-id unsigned-int))
50 (callback-trampoline callback-id n-params param-values))
51
52(defun callback-trampoline (callback-id n-params param-values &optional
53 (return-value (make-pointer 0)))
c8c48a4c 54 (let* ((return-type (unless (null-pointer-p return-value)
e378b861 55 (gvalue-type return-value)))
34f9e1d4 56 (args (loop
57 for n from 0 below n-params
58 collect (gvalue-get (sap+ param-values (* n +gvalue-size+))))))
59 (let ((result (apply #'invoke-callback callback-id return-type args)))
60 (when return-type
61 (gvalue-set return-value result)))))
62
8755b1a5 63(defun invoke-callback (callback-id return-type &rest args)
34f9e1d4 64 (restart-case
65 (apply (find-user-data callback-id) args)
66 (continue nil :report "Return from callback function"
8755b1a5 67 (when return-type
68 (format *query-io* "Enter return value of type ~S: " return-type)
34f9e1d4 69 (force-output *query-io*)
70 (eval (read *query-io*))))
71 (re-invoke nil :report "Re-invoke callback function"
8755b1a5 72 (apply #'invoke-callback callback-id return-type args))))
c8c48a4c 73
c8c48a4c 74
e378b861 75;;;; Timeouts and idle functions
76
acd28982 77(defconstant +priority-high+ -100)
78(defconstant +priority-default+ 0)
79(defconstant +priority-high-idle+ 100)
80(defconstant +priority-default-idle+ 200)
81(defconstant +priority-low+ 300)
82
83(defbinding source-remove () boolean
84 (tag unsigned-int))
85
8755b1a5 86(defcallback source-callback-marshal (nil (callback-id unsigned-int))
e0d2987b 87 (callback-trampoline callback-id 0 nil))
e378b861 88
89(defbinding (timeout-add "g_timeout_add_full")
acd28982 90 (interval function &optional (priority +priority-default+)) unsigned-int
e378b861 91 (priority int)
92 (interval unsigned-int)
acd28982 93 ((callback source-callback-marshal) pointer)
e378b861 94 ((register-callback-function function) unsigned-long)
34f9e1d4 95 ((callback %destroy-user-data) pointer))
e378b861 96
acd28982 97(defun timeout-remove (timeout)
98 (source-remove timeout))
99
e378b861 100(defbinding (idle-add "g_idle_add_full")
acd28982 101 (function &optional (priority +priority-default-idle+)) unsigned-int
e378b861 102 (priority int)
acd28982 103 ((callback source-callback-marshal) pointer)
e378b861 104 ((register-callback-function function) unsigned-long)
34f9e1d4 105 ((callback %destroy-user-data) pointer))
e378b861 106
acd28982 107(defun idle-remove (idle)
108 (source-remove idle))
e378b861 109
c8c48a4c 110
e0d2987b 111;;;; Signal information querying
c8c48a4c 112
e0d2987b 113(defbinding signal-lookup (name type) unsigned-int
c8c48a4c 114 ((signal-name-to-string name) string)
e0d2987b 115 ((find-type-number type t) type-number))
c8c48a4c 116
e0d2987b 117(defbinding signal-name () (copy-of string)
c8c48a4c 118 (signal-id unsigned-int))
119
e0d2987b 120(defbinding signal-list-ids (type) (vector unsigned-int n-ids)
121 ((find-type-number type t) type-number)
122 (n-ids unsigned-int :out))
123
124(defun signal-list-names (type)
125 (map 'list #'signal-name (signal-list-ids type)))
126
127(defun ensure-signal-id-from-type (signal-id type)
c8c48a4c 128 (etypecase signal-id
e0d2987b 129 (integer (if (signal-name signal-id)
130 signal-id
131 (error "Invalid signal id: ~D" signal-id)))
132 ((or symbol string)
133 (let ((numeric-id (signal-lookup signal-id type)))
134 (if (zerop numeric-id)
135 (error "Invalid signal name for ~S: ~D" type signal-id)
136 numeric-id)))))
137
138(defun ensure-signal-id (signal-id instance)
139 (ensure-signal-id-from-type signal-id (type-of instance)))
c8c48a4c 140
e0d2987b 141(eval-when (:compile-toplevel :load-toplevel :execute)
142 (deftype signal-flags ()
143 '(flags :run-first :run-last :run-cleanup :no-recurse
144 :detailed :action :no-hooks))
145
146 (defclass signal-query (struct)
147 ((id :allocation :alien :type unsigned-int)
148 (name :allocation :alien :type (copy-of string))
149 (type :allocation :alien :type type-number)
150 (flags :allocation :alien :type signal-flags)
151 (return-type :allocation :alien :type type-number)
152 (n-params :allocation :alien :type unsigned-int)
153 (param-types :allocation :alien :type pointer))
154 (:metaclass struct-class)))
155
156(defbinding signal-query
157 (signal-id &optional (signal-query (make-instance 'signal-query))) nil
158 (signal-id unsigned-int)
159 (signal-query signal-query :return))
160
161(defun signal-param-types (info)
162 (with-slots (n-params param-types) info
163 (map-c-vector 'list
164 #'(lambda (type-number)
165 (type-from-number type-number))
166 param-types 'type-number n-params)))
167
168
169(defun describe-signal (signal-id &optional type)
170 (let ((info (signal-query (ensure-signal-id-from-type signal-id type))))
171 (with-slots (id name type flags return-type n-params) info
172 (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))
173 (format t "Signal handlers should return ~A and take ~A~%"
174 (cond
175 ((= return-type (find-type-number "void")) "no values")
176 ((not (type-from-number return-type)) "values of unknown type")
177 ((format nil "values of type ~S" (type-from-number return-type))))
178 (if (zerop n-params)
179 "no arguments"
180 (format nil "arguments with the following types: ~A"
181 (signal-param-types info)))))))
182
183
184;;;; Signal connecting and controlling
185
186(defbinding %signal-stop-emission () nil
c8c48a4c 187 (instance ginstance)
e0d2987b 188 (signal-id unsigned-int)
189 (detail quark))
190
191(defvar *signal-stop-emission* nil)
192(declaim (special *signal-stop-emission*))
c8c48a4c 193
e0d2987b 194(defun signal-stop-emission ()
195 (if *signal-stop-emission*
196 (funcall *signal-stop-emission*)
197 (error "Not inside a signal handler")))
198
199
200(defbinding signal-add-emission-hook (type signal function &key (detail 0))
201 unsigned-int
202 ((ensure-signal-id-from-type signal type) unsigned-int)
203 (detail quark)
204 ((callback signal-emission-hook) pointer)
205 ((register-callback-function function) unsigned-int)
206 ((callback %destroy-user-data) pointer))
207
208(defbinding signal-remove-emission-hook (type signal hook-id) nil
209 ((ensure-signal-id-from-type signal type) unsigned-int)
210 (hook-id unsigned-int))
c8c48a4c 211
c8c48a4c 212
0383dd48 213(defbinding (signal-has-handler-pending-p "g_signal_has_handler_pending")
c8c48a4c 214 (instance signal-id &key detail blocked) boolean
215 (instance ginstance)
e49e135a 216 ((ensure-signal-id signal-id instance) unsigned-int)
c8c48a4c 217 ((or detail 0) quark)
e0d2987b 218 (may-be-blocked boolean))
c8c48a4c 219
e0d2987b 220(defbinding %signal-connect-closure-by-id () unsigned-int
c8c48a4c 221 (instance ginstance)
e0d2987b 222 (signal-id unsigned-int)
223 (detail quark)
224 (closure pointer)
c8c48a4c 225 (after boolean))
226
0383dd48 227(defbinding signal-handler-block () nil
c8c48a4c 228 (instance ginstance)
e0d2987b 229 (handler-id unsigned-int))
c8c48a4c 230
0383dd48 231(defbinding signal-handler-unblock () nil
c8c48a4c 232 (instance ginstance)
e0d2987b 233 (handler-id unsigned-int))
c8c48a4c 234
0383dd48 235(defbinding signal-handler-disconnect () nil
c8c48a4c 236 (instance ginstance)
e0d2987b 237 (handler-id unsigned-int))
238
239(defbinding signal-handler-is-connected-p () boolean
240 (instance ginstance)
241 (handler-id unsigned-int))
c8c48a4c 242
d75a77ff 243(deftype gclosure () 'pointer)
244
245(defbinding (callback-closure-new "clg_callback_closure_new") () gclosure
e0d2987b 246 (callback-id unsigned-int)
247 (callback pointer)
248 (destroy-notify pointer))
c8c48a4c 249
e0d2987b 250(defun make-callback-closure (function)
251 (let ((callback-id (register-callback-function function)))
252 (values
253 (callback-closure-new
254 callback-id (callback closure-marshal)
255 (callback %destroy-user-data))
256 callback-id)))
257
258(defmethod create-callback-function ((gobject gobject) function arg1)
259 (cond
260 ((or (eq arg1 t) (eq arg1 gobject)) function)
261 ((not arg1)
262 #'(lambda (&rest args) (apply function (rest args))))
263 (t
264 #'(lambda (&rest args) (apply function arg1 (rest args))))))
265
266(defmethod signal-connect ((gobject gobject) signal function
267 &key (detail 0) after object remove)
268"Connects a callback function to a signal for a particular object. If
269:OBJECT is T, the object connected to is passed as the first argument
270to the callback function, or if :OBJECT is any other non NIL value, it
271is passed as the first argument instead. If :AFTER is non NIL, the
272handler will be called after the default handler for the signal. If
273:REMOVE is non NIL, the handler will be removed after beeing invoked
274once."
0f152c4e 275 (when function
e0d2987b 276 (let* ((signal-id (ensure-signal-id signal gobject))
277 (signal-stop-emission
278 #'(lambda ()
279 (%signal-stop-emission gobject signal-id detail)))
280 (callback (create-callback-function gobject function object))
281 (wrapper #'(lambda (&rest args)
282 (let ((*signal-stop-emission* signal-stop-emission))
283 (apply callback args)))))
284 (multiple-value-bind (closure-id callback-id)
285 (make-callback-closure wrapper)
286 (let ((handler-id (%signal-connect-closure-by-id
287 gobject signal-id detail closure-id after)))
288 (when remove
289 (update-user-data callback-id
290 #'(lambda (&rest args)
291 (unwind-protect
292 (let ((*signal-stop-emission* signal-stop-emission))
293 (apply callback args))
294 (signal-handler-disconnect gobject handler-id)))))
295 handler-id)))))
296
297
298;;;; Signal emission
299
300(defbinding %signal-emitv () nil
301 (gvalues pointer)
302 (signal-id unsigned-int)
303 (detail quark)
304 (return-value gvalue))
305
306(defvar *signal-emit-functions* (make-hash-table))
307
308(defun create-signal-emit-function (signal-id)
309 (let ((info (signal-query signal-id)))
310 (let* ((type (type-from-number (slot-value info 'type)))
311 (param-types (cons type (signal-param-types info)))
312 (return-type (type-from-number (slot-value info 'return-type)))
313 (n-params (1+ (slot-value info 'n-params)))
314 (params (allocate-memory (* n-params +gvalue-size+))))
315 #'(lambda (detail object &rest args)
316 (unless (= (length args) (1- n-params))
317 (error "Invalid number of arguments: ~A" (+ 2 (length args))))
318 (unwind-protect
319 (loop
320 for arg in (cons object args)
321 for type in param-types
322 as tmp = params then (sap+ tmp +gvalue-size+)
323 do (gvalue-init tmp type arg)
324 finally
325 (if return-type
326 (return
327 (with-gvalue (return-value)
328 (%signal-emitv params signal-id detail return-value)))
329 (%signal-emitv params signal-id detail (make-pointer 0))))
330 (loop
331 repeat n-params
332 as tmp = params then (sap+ tmp +gvalue-size+)
333 while (gvalue-p tmp)
334 do (gvalue-unset tmp)))))))
335
336(defun signal-emit-with-detail (object signal detail &rest args)
337 (let* ((signal-id (ensure-signal-id signal object))
338 (function (or
339 (gethash signal-id *signal-emit-functions*)
340 (setf
341 (gethash signal-id *signal-emit-functions*)
342 (create-signal-emit-function signal-id)))))
343 (apply function detail object args)))
344
345(defun signal-emit (object signal &rest args)
346 (apply #'signal-emit-with-detail object signal 0 args))
347
c0f178d0 348
349
350;;; Message logging
351
352;; TODO: define and signal conditions based on log-level
e0d2987b 353
9adccb27 354(def-callback log-handler (c-call:void (domain c-call:c-string)
355 (log-level c-call:int)
356 (message c-call:c-string))
c0f178d0 357 (error "~A: ~A" domain message))
358
34f9e1d4 359(setf (extern-alien "log_handler" system-area-pointer) (callback log-handler))
fd1e4a39 360
361
362;;;; Convenient macros
363
364(defmacro def-callback-marshal (name (return-type &rest args))
365 (let ((names (loop
366 for arg in args
367 collect (if (atom arg) (gensym) (first arg))))
368 (types (loop
369 for arg in args
370 collect (if (atom arg) arg (second arg)))))
371 `(defcallback ,name (,return-type ,@(mapcar #'list names types)
372 (callback-id unsigned-int))
373 (invoke-callback callback-id ',return-type ,@names))))
374
375(defmacro with-callback-function ((id function) &body body)
376 `(let ((,id (register-callback-function ,function)))
377 (unwind-protect
378 (progn ,@body)
379 (destroy-user-data ,id))))