1 ;; Common Lisp bindings for GTK+ v1.2.x
2 ;; Copyright (C) 1999-2005 Espen S. Johnsen <espen@users-sf-net>
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.
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.
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
18 ;; $Id: glib.lisp,v 1.29 2005-04-18 10:34:51 espen Exp $
26 ;;;; Memory management
28 (defbinding (allocate-memory "g_malloc0") () pointer
31 (defbinding (reallocate-memory "g_realloc") () pointer
35 (defbinding (deallocate-memory "g_free") () nil
37 ;; (defun deallocate-memory (address)
38 ;; (declare (ignore address)))
40 (defun copy-memory (from length &optional (to (allocate-memory length)))
41 #+cmu(system-area-copy from 0 to 0 (* 8 length))
42 #+sbcl(system-area-ub8-copy from 0 to 0 length)
46 ;;;; User data mechanism
48 (internal *user-data* *user-data-count*)
50 (defvar *user-data* (make-hash-table))
51 (defvar *user-data-count* 0)
53 (defun register-user-data (object &optional destroy-function)
54 (check-type destroy-function (or null symbol function))
55 (incf *user-data-count*)
57 (gethash *user-data-count* *user-data*)
58 (cons object destroy-function))
61 (defun find-user-data (id)
62 (check-type id fixnum)
63 (multiple-value-bind (user-data p) (gethash id *user-data*)
64 (values (car user-data) p)))
66 (defun user-data-exists-p (id)
67 (nth-value 1 (find-user-data id)))
69 (defun update-user-data (id object)
70 (check-type id fixnum)
71 (multiple-value-bind (user-data exists-p) (gethash id *user-data*)
73 ((not exists-p) (error "User data id ~A does not exist" id))
76 (funcall (cdr user-data) (car user-data)))
77 (setf (car user-data) object)))))
79 (defun destroy-user-data (id)
80 (check-type id fixnum)
81 (let ((user-data (gethash id *user-data*)))
83 (funcall (cdr user-data) (car user-data))))
84 (remhash id *user-data*))
89 (deftype quark () 'unsigned)
91 (defbinding %quark-from-string () quark
94 (defun quark-intern (object)
97 (string (%quark-from-string object))
98 (symbol (%quark-from-string (format nil "clg-~A:~A"
99 (package-name (symbol-package object))
102 (defbinding quark-to-string () (copy-of string)
106 ;;;; Linked list (GList)
108 (deftype glist (type)
109 `(or (null (cons ,type list))))
111 (defbinding (%glist-append "g_list_append") () pointer
115 (defun make-glist (type list)
117 with writer = (writer-function type)
119 as glist = (%glist-append (or glist (make-pointer 0)))
120 do (funcall writer element glist)
121 finally (return glist)))
123 (defun glist-next (glist)
124 (unless (null-pointer-p glist)
125 (sap-ref-sap glist +size-of-pointer+)))
127 ;; Also used for gslists
128 (defun map-glist (seqtype function glist element-type)
129 (let ((reader (reader-function element-type)))
133 as tmp = glist then (glist-next tmp)
134 until (null-pointer-p tmp)
135 do (funcall function (funcall reader tmp))))
138 as tmp = glist then (glist-next tmp)
139 until (null-pointer-p tmp)
140 collect (funcall function (funcall reader tmp))))
144 as tmp = glist then (glist-next tmp)
145 until (null-pointer-p tmp)
146 collect (funcall function (funcall reader tmp)))
149 (defbinding (glist-free "g_list_free") () nil
152 (defun destroy-glist (glist element-type)
154 with destroy = (destroy-function element-type)
155 as tmp = glist then (glist-next tmp)
156 until (null-pointer-p tmp)
157 do (funcall destroy tmp 0))
160 (defmethod alien-type ((type (eql 'glist)) &rest args)
161 (declare (ignore type args))
162 (alien-type 'pointer))
164 (defmethod size-of ((type (eql 'glist)) &rest args)
165 (declare (ignore type args))
168 (defmethod to-alien-form (list (type (eql 'glist)) &rest args)
169 (declare (ignore type))
170 (destructuring-bind (element-type) args
171 `(make-glist ',element-type ,list)))
173 (defmethod to-alien-function ((type (eql 'glist)) &rest args)
174 (declare (ignore type))
175 (destructuring-bind (element-type) args
177 (make-glist element-type list))))
179 (defmethod from-alien-form (glist (type (eql 'glist)) &rest args)
180 (declare (ignore type))
181 (destructuring-bind (element-type) args
182 `(let ((glist ,glist))
184 (map-glist 'list #'identity glist ',element-type)
185 (destroy-glist glist ',element-type)))))
187 (defmethod from-alien-function ((type (eql 'glist)) &rest args)
188 (declare (ignore type))
189 (destructuring-bind (element-type) args
192 (map-glist 'list #'identity glist element-type)
193 (destroy-glist glist element-type)))))
195 (defmethod copy-from-alien-form (glist (type (eql 'glist)) &rest args)
196 (declare (ignore type))
197 (destructuring-bind (element-type) args
198 `(map-glist 'list #'identity ,glist ',element-type)))
200 (defmethod copy-from-alien-function ((type (eql 'glist)) &rest args)
201 (declare (ignore type))
202 (destructuring-bind (element-type) args
204 (map-glist 'list #'identity glist element-type))))
206 (defmethod cleanup-form (glist (type (eql 'glist)) &rest args)
207 (declare (ignore type))
208 (destructuring-bind (element-type) args
209 `(destroy-glist ,glist ',element-type)))
211 (defmethod cleanup-function ((type (eql 'glist)) &rest args)
212 (declare (ignore type))
213 (destructuring-bind (element-type) args
215 (destroy-glist glist element-type))))
217 (defmethod writer-function ((type (eql 'glist)) &rest args)
218 (declare (ignore type))
219 (destructuring-bind (element-type) args
220 #'(lambda (list location &optional (offset 0))
222 (sap-ref-sap location offset)
223 (make-glist element-type list)))))
225 (defmethod reader-function ((type (eql 'glist)) &rest args)
226 (declare (ignore type))
227 (destructuring-bind (element-type) args
228 #'(lambda (location &optional (offset 0))
229 (unless (null-pointer-p (sap-ref-sap location offset))
230 (map-glist 'list #'identity (sap-ref-sap location offset) element-type)))))
232 (defmethod destroy-function ((type (eql 'glist)) &rest args)
233 (declare (ignore type))
234 (destructuring-bind (element-type) args
235 #'(lambda (location &optional (offset 0))
236 (unless (null-pointer-p (sap-ref-sap location offset))
237 (destroy-glist (sap-ref-sap location offset) element-type)
238 (setf (sap-ref-sap location offset) (make-pointer 0))))))
242 ;;;; Single linked list (GSList)
244 (deftype gslist (type) `(or (null (cons ,type list))))
246 (defbinding (%gslist-prepend "g_slist_prepend") () pointer
250 (defun make-gslist (type list)
252 with writer = (writer-function type)
253 for element in (reverse list)
254 as gslist = (%gslist-prepend (or gslist (make-pointer 0)))
255 do (funcall writer element gslist)
256 finally (return gslist)))
258 (defbinding (gslist-free "g_slist_free") () nil
261 (defun destroy-gslist (gslist element-type)
263 with destroy = (destroy-function element-type)
264 as tmp = gslist then (glist-next tmp)
265 until (null-pointer-p tmp)
266 do (funcall destroy tmp 0))
267 (gslist-free gslist))
269 (defmethod alien-type ((type (eql 'gslist)) &rest args)
270 (declare (ignore type args))
271 (alien-type 'pointer))
273 (defmethod size-of ((type (eql 'gslist)) &rest args)
274 (declare (ignore type args))
277 (defmethod to-alien-form (list (type (eql 'gslist)) &rest args)
278 (declare (ignore type))
279 (destructuring-bind (element-type) args
280 `(make-sglist ',element-type ,list)))
282 (defmethod to-alien-function ((type (eql 'gslist)) &rest args)
283 (declare (ignore type))
284 (destructuring-bind (element-type) args
286 (make-gslist element-type list))))
288 (defmethod from-alien-form (gslist (type (eql 'gslist)) &rest args)
289 (declare (ignore type))
290 (destructuring-bind (element-type) args
291 `(let ((gslist ,gslist))
293 (map-glist 'list #'identity gslist ',element-type)
294 (destroy-gslist gslist ',element-type)))))
296 (defmethod from-alien-function ((type (eql 'gslist)) &rest args)
297 (declare (ignore type))
298 (destructuring-bind (element-type) args
301 (map-glist 'list #'identity gslist element-type)
302 (destroy-gslist gslist element-type)))))
304 (defmethod copy-from-alien-form (gslist (type (eql 'gslist)) &rest args)
305 (declare (ignore type))
306 (destructuring-bind (element-type) args
307 `(map-glist 'list #'identity ,gslist ',element-type)))
309 (defmethod copy-from-alien-function ((type (eql 'gslist)) &rest args)
310 (declare (ignore type))
311 (destructuring-bind (element-type) args
313 (map-glist 'list #'identity gslist element-type))))
315 (defmethod cleanup-form (gslist (type (eql 'gslist)) &rest args)
316 (declare (ignore type))
317 (destructuring-bind (element-type) args
318 `(destroy-gslist ,gslist ',element-type)))
320 (defmethod cleanup-function ((type (eql 'gslist)) &rest args)
321 (declare (ignore type))
322 (destructuring-bind (element-type) args
324 (destroy-gslist gslist element-type))))
326 (defmethod writer-function ((type (eql 'gslist)) &rest args)
327 (declare (ignore type))
328 (destructuring-bind (element-type) args
329 #'(lambda (list location &optional (offset 0))
331 (sap-ref-sap location offset)
332 (make-gslist element-type list)))))
334 (defmethod reader-function ((type (eql 'gslist)) &rest args)
335 (declare (ignore type))
336 (destructuring-bind (element-type) args
337 #'(lambda (location &optional (offset 0))
338 (unless (null-pointer-p (sap-ref-sap location offset))
339 (map-glist 'list #'identity (sap-ref-sap location offset) element-type)))))
341 (defmethod destroy-function ((type (eql 'gslist)) &rest args)
342 (declare (ignore type))
343 (destructuring-bind (element-type) args
344 #'(lambda (location &optional (offset 0))
345 (unless (null-pointer-p (sap-ref-sap location offset))
346 (destroy-gslist (sap-ref-sap location offset) element-type)
347 (setf (sap-ref-sap location offset) (make-pointer 0))))))
352 (defun make-c-vector (type length &optional content location)
353 (let* ((size-of-type (size-of type))
354 (location (or location (allocate-memory (* size-of-type length))))
355 (writer (writer-function type)))
359 for element across content
360 for i from 0 below length
361 as offset = 0 then (+ offset size-of-type)
362 do (funcall writer element location offset)))
365 for element in content
366 for i from 0 below length
367 as offset = 0 then (+ offset size-of-type)
368 do (funcall writer element location offset))))
372 (defun map-c-vector (seqtype function location element-type length)
373 (let ((reader (reader-function element-type))
374 (size-of-element (size-of element-type)))
378 for i from 0 below length
379 as offset = 0 then (+ offset size-of-element)
380 do (funcall function (funcall reader location offset))))
383 for i from 0 below length
384 as offset = 0 then (+ offset size-of-element)
385 collect (funcall function (funcall reader location offset))))
388 with sequence = (make-sequence seqtype length)
389 for i from 0 below length
390 as offset = 0 then (+ offset size-of-element)
393 (funcall function (funcall reader location offset)))
394 finally (return sequence))))))
397 (defun destroy-c-vector (location element-type length)
399 with destroy = (destroy-function element-type)
400 with element-size = (size-of element-type)
401 for i from 0 below length
402 as offset = 0 then (+ offset element-size)
403 do (funcall destroy location offset))
404 (deallocate-memory location))
407 (defmethod alien-type ((type (eql 'vector)) &rest args)
408 (declare (ignore type args))
409 (alien-type 'pointer))
411 (defmethod size-of ((type (eql 'vector)) &rest args)
412 (declare (ignore type args))
415 (defmethod to-alien-form (vector (type (eql 'vector)) &rest args)
416 (declare (ignore type))
417 (destructuring-bind (element-type &optional (length '*)) args
419 `(let* ((vector ,vector)
421 (allocate-memory (+ ,+size-of-int+
422 (* ,(size-of element-type)
425 (make-c-vector ',element-type (length vector) vector location)
426 (setf (sap-ref-32 location ,(- +size-of-int+)) (length vector))
428 `(make-c-vector ',element-type ,length ,vector))))
430 (defmethod from-alien-form (c-vector (type (eql 'vector)) &rest args)
431 (declare (ignore type))
432 (destructuring-bind (element-type &optional (length '*)) args
434 (error "Can't use vector of variable size as return type")
435 `(let ((c-vector ,c-vector))
437 (map-c-vector 'vector #'identity c-vector ',element-type ,length)
438 (destroy-c-vector c-vector ',element-type ,length))))))
440 (defmethod copy-from-alien-form (c-vector (type (eql 'vector)) &rest args)
441 (declare (ignore type))
442 (destructuring-bind (element-type &optional (length '*)) args
444 (error "Can't use vector of variable size as return type")
445 `(map-c-vector 'vector #'identity ,c-vector ',element-type ',length))))
447 (defmethod copy-from-alien-function ((type (eql 'vector)) &rest args)
448 (declare (ignore type))
449 (destructuring-bind (element-type &optional (length '*)) args
451 (error "Can't use vector of variable size as return type")
453 (map-c-vector 'vector #'identity c-vector element-type length)))))
455 (defmethod cleanup-form (location (type (eql 'vector)) &rest args)
456 (declare (ignore type))
457 (destructuring-bind (element-type &optional (length '*)) args
458 `(let* ((location ,location)
459 (length ,(if (eq length '*)
460 `(sap-ref-32 location ,(- +size-of-int+))
463 with destroy = (destroy-function ',element-type)
464 for i from 0 below length
465 as offset = 0 then (+ offset ,(size-of element-type))
466 do (funcall destroy location offset))
467 (deallocate-memory ,(if (eq length '*)
468 `(sap+ location ,(- +size-of-int+))
471 (defmethod writer-function ((type (eql 'vector)) &rest args)
472 (declare (ignore type))
473 (destructuring-bind (element-type &optional (length '*)) args
474 #'(lambda (vector location &optional (offset 0))
476 (sap-ref-sap location offset)
477 (make-c-vector element-type length vector)))))
479 (defmethod reader-function ((type (eql 'vector)) &rest args)
480 (declare (ignore type))
481 (destructuring-bind (element-type &optional (length '*)) args
483 (error "Can't create reader function for vector of variable size")
484 #'(lambda (location &optional (offset 0))
485 (unless (null-pointer-p (sap-ref-sap location offset))
486 (map-c-vector 'vector #'identity (sap-ref-sap location offset)
487 element-type length))))))
489 (defmethod destroy-function ((type (eql 'vector)) &rest args)
490 (declare (ignore type))
491 (destructuring-bind (element-type &optional (length '*)) args
493 (error "Can't create destroy function for vector of variable size")
494 #'(lambda (location &optional (offset 0))
495 (unless (null-pointer-p (sap-ref-sap location offset))
497 (sap-ref-sap location offset) element-type length)
498 (setf (sap-ref-sap location offset) (make-pointer 0)))))))
501 ;;;; Null terminated vector
503 (defun make-0-vector (type content &optional location)
504 (let* ((size-of-type (size-of type))
507 (allocate-memory (* size-of-type (1+ (length content))))))
508 (writer (writer-function type)))
512 for element across content
513 as offset = 0 then (+ offset size-of-type)
514 do (funcall writer element location offset)
515 finally (setf (sap-ref-sap location offset) (make-pointer 0))))
518 for element in content
519 as offset = 0 then (+ offset size-of-type)
520 do (funcall writer element location offset)
521 finally (setf (sap-ref-sap location (+ offset size-of-type)) (make-pointer 0)))))
525 (defun map-0-vector (seqtype function location element-type)
526 (let ((reader (reader-function element-type))
527 (size-of-element (size-of element-type)))
531 as offset = 0 then (+ offset size-of-element)
532 until (null-pointer-p (sap-ref-sap location offset))
533 do (funcall function (funcall reader location offset))))
536 as offset = 0 then (+ offset size-of-element)
537 until (null-pointer-p (sap-ref-sap location offset))
538 collect (funcall function (funcall reader location offset))))
542 as offset = 0 then (+ offset size-of-element)
543 until (null-pointer-p (sap-ref-sap location offset))
544 collect (funcall function (funcall reader location offset)))
548 (defun destroy-0-vector (location element-type)
550 with destroy = (destroy-function element-type)
551 with element-size = (size-of element-type)
552 as offset = 0 then (+ offset element-size)
553 until (null-pointer-p (sap-ref-sap location offset))
554 do (funcall destroy location offset))
555 (deallocate-memory location))
557 (deftype null-terminated-vector (element-type) `(vector ,element-type))
559 (defmethod alien-type ((type (eql 'null-terminated-vector)) &rest args)
560 (declare (ignore type args))
561 (alien-type 'pointer))
563 (defmethod size-of ((type (eql 'null-terminated-vector)) &rest args)
564 (declare (ignore type args))
567 (defmethod to-alien-form (vector (type (eql 'null-terminated-vector)) &rest args)
568 (declare (ignore type))
569 (destructuring-bind (element-type) args
570 `(make-0-vector ',element-type ,vector)))
572 (defmethod from-alien-form (c-vector (type (eql 'null-terminated-vector)) &rest args)
573 (declare (ignore type))
574 (destructuring-bind (element-type) args
575 `(let ((c-vector ,c-vector))
577 (map-0-vector 'vector #'identity c-vector ',element-type)
578 (destroy-0-vector c-vector ',element-type)))))
580 (defmethod copy-from-alien-form (c-vector (type (eql 'null-terminated-vector)) &rest args)
581 (declare (ignore type))
582 (destructuring-bind (element-type) args
583 `(map-0-vector 'vector #'identity ,c-vector ',element-type)))
585 (defmethod cleanup-form (location (type (eql 'null-terminated-vector)) &rest args)
586 (declare (ignore type))
587 (destructuring-bind (element-type) args
588 `(destroy-0-vector ,location ',element-type)))
590 (defmethod writer-function ((type (eql 'null-terminated-vector)) &rest args)
591 (declare (ignore type))
592 (destructuring-bind (element-type) args
593 (unless (eq (alien-type element-type) (alien-type 'pointer))
594 (error "Elements in null-terminated vectors need to be of pointer types"))
595 #'(lambda (vector location &optional (offset 0))
597 (sap-ref-sap location offset)
598 (make-0-vector element-type vector)))))
600 (defmethod reader-function ((type (eql 'null-terminated-vector)) &rest args)
601 (declare (ignore type))
602 (destructuring-bind (element-type) args
603 (unless (eq (alien-type element-type) (alien-type 'pointer))
604 (error "Elements in null-terminated vectors need to be of pointer types"))
605 #'(lambda (location &optional (offset 0))
606 (unless (null-pointer-p (sap-ref-sap location offset))
607 (map-0-vector 'vector #'identity (sap-ref-sap location offset)
610 (defmethod destroy-function ((type (eql 'null-terminated-vector)) &rest args)
611 (declare (ignore type))
612 (destructuring-bind (element-type) args
613 (unless (eq (alien-type element-type) (alien-type 'pointer))
614 (error "Elements in null-terminated vectors need to be of pointer types"))
615 #'(lambda (location &optional (offset 0))
616 (unless (null-pointer-p (sap-ref-sap location offset))
618 (sap-ref-sap location offset) element-type)
619 (setf (sap-ref-sap location offset) (make-pointer 0))))))
621 (defmethod unbound-value ((type (eql 'null-terminated-vector)) &rest args)
622 (declare (ignore type args))
628 (defun make-counted-vector (type content)
629 (let* ((size-of-type (size-of type))
630 (length (length content))
632 (allocate-memory (+ +size-of-int+ (* size-of-type length)))))
633 (setf (sap-ref-32 location 0) length)
634 (make-c-vector type length content (sap+ location +size-of-int+))))
636 (defun map-counted-vector (seqtype function location element-type)
637 (let ((length (sap-ref-32 location 0)))
639 seqtype function (sap+ location +size-of-int+)
640 element-type length)))
642 (defun destroy-counted-vector (location element-type)
644 with destroy = (destroy-function element-type)
645 with element-size = (size-of element-type)
646 for i from 0 below (sap-ref-32 location 0)
647 as offset = +size-of-int+ then (+ offset element-size)
648 do (funcall destroy location offset))
649 (deallocate-memory location))
652 (deftype counted-vector (element-type) `(vector ,element-type))
654 (defmethod alien-type ((type (eql 'counted-vector)) &rest args)
655 (declare (ignore type args))
656 (alien-type 'pointer))
658 (defmethod size-of ((type (eql 'counted-vector)) &rest args)
659 (declare (ignore type args))
662 (defmethod to-alien-form (vector (type (eql 'counted-vector)) &rest args)
663 (declare (ignore type))
664 (destructuring-bind (element-type) args
665 `(make-counted-vector ',element-type ,vector)))
667 (defmethod from-alien-form (c-vector (type (eql 'counted-vector)) &rest args)
668 (declare (ignore type))
669 (destructuring-bind (element-type) args
670 `(let ((c-vector ,c-vector))
672 (map-counted-vector 'vector #'identity c-vector ',element-type)
673 (destroy-counted-vector c-vector ',element-type)))))
675 (defmethod copy-from-alien-form (c-vector (type (eql 'counted-vector)) &rest args)
676 (declare (ignore type))
677 (destructuring-bind (element-type) args
678 `(map-counted-vector 'vector #'identity ,c-vector ',element-type)))
680 (defmethod copy-from-alien-function ((type (eql 'counted-vector)) &rest args)
681 (declare (ignore type))
682 (destructuring-bind (element-type) args
684 (map-counted-vector 'vector #'identity c-vector element-type))))
686 (defmethod cleanup-form (location (type (eql 'counted-vector)) &rest args)
687 (declare (ignore type))
688 (destructuring-bind (element-type) args
689 `(destroy-counted-vector ,location ',element-type)))
691 (defmethod writer-function ((type (eql 'counted-vector)) &rest args)
692 (declare (ignore type))
693 (destructuring-bind (element-type) args
694 #'(lambda (vector location &optional (offset 0))
696 (sap-ref-sap location offset)
697 (make-counted-vector element-type vector)))))
699 (defmethod reader-function ((type (eql 'counted-vector)) &rest args)
700 (declare (ignore type))
701 (destructuring-bind (element-type) args
702 #'(lambda (location &optional (offset 0))
703 (unless (null-pointer-p (sap-ref-sap location offset))
704 (map-counted-vector 'vector #'identity
705 (sap-ref-sap location offset) element-type)))))
707 (defmethod destroy-function ((type (eql 'counted-vector)) &rest args)
708 (declare (ignore type))
709 (destructuring-bind (element-type) args
710 #'(lambda (location &optional (offset 0))
711 (unless (null-pointer-p (sap-ref-sap location offset))
712 (destroy-counted-vector
713 (sap-ref-sap location offset) element-type)
714 (setf (sap-ref-sap location offset) (make-pointer 0))))))