chiark / gitweb /
Added reader and writer functions to list types
[clg] / glib / glib.lisp
1 ;; Common Lisp bindings for GTK+ v1.2.x
2 ;; Copyright (C) 1999 Espen S. Johnsen <espejohn@online.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
18 ;; $Id: glib.lisp,v 1.22 2004-12-26 11:40:58 espen Exp $
19
20
21 (in-package "GLIB")
22
23 (use-prefix "g")
24
25
26 ;;;; Memory management
27
28 (defbinding (allocate-memory "g_malloc0") () pointer
29   (size unsigned-long))
30
31 (defbinding (reallocate-memory "g_realloc") () pointer
32   (address pointer)
33   (size unsigned-long))
34
35 (defbinding (deallocate-memory "g_free") () nil
36   (address pointer))
37 ;; (defun deallocate-memory (address)
38 ;;   (declare (ignore address)))
39
40 (defun copy-memory (from length &optional (to (allocate-memory length)))
41   (kernel:system-area-copy from 0 to 0 (* 8 length))
42   to)
43
44
45 ;;;; User data mechanism
46
47 (internal *user-data* *user-data-count*)
48
49 (declaim (fixnum *user-data-count*))
50
51 (defvar *user-data* (make-hash-table))
52 (defvar *user-data-count* 0)
53
54 (defun register-user-data (object &optional destroy-function)
55   (check-type destroy-function (or null symbol function))
56   (incf *user-data-count*)
57   (setf
58    (gethash *user-data-count* *user-data*)
59    (cons object destroy-function))
60   *user-data-count*)
61
62 (defun find-user-data (id)
63   (check-type id fixnum)
64   (multiple-value-bind (user-data p) (gethash id *user-data*)
65     (values (car user-data) p)))
66
67 (defun destroy-user-data (id)
68   (check-type id fixnum)
69   (let ((user-data (gethash id *user-data*)))
70     (when (cdr user-data)
71       (funcall (cdr user-data) (car user-data))))
72   (remhash id *user-data*))
73
74
75 ;;;; Quarks
76
77 (internal *quark-counter* *quark-from-object* *quark-to-object*)
78
79 (deftype quark () 'unsigned)
80
81 ;(defbinding %quark-get-reserved () quark)
82
83 (defbinding %quark-from-string () quark
84   (string string))
85
86 (defvar *quark-counter* 0)
87
88 (defun %quark-get-reserved ()
89   ;; The string is just a dummy
90   (%quark-from-string (format nil "#@£$%&-quark-~D" (incf *quark-counter*))))
91
92 (defvar *quark-from-object* (make-hash-table))
93 (defvar *quark-to-object* (make-hash-table))
94
95 (defun quark-from-object (object &key (test #'eq))
96   (let ((hash-code (sxhash object)))
97     (or
98      (assoc-ref object (gethash hash-code *quark-from-object*) :test test)
99      (let ((quark (%quark-get-reserved)))
100        (setf
101         (gethash hash-code *quark-from-object*)
102         (append
103          (gethash hash-code *quark-from-object*)
104          (list (cons object quark))))
105        (setf (gethash quark *quark-to-object*) object)
106        quark))))
107
108 (defun quark-to-object (quark) 
109   (gethash quark *quark-to-object*))
110   
111 (defun remove-quark (quark)
112   (let* ((object (gethash quark *quark-to-object*))
113          (hash-code (sxhash object)))
114     (remhash quark *quark-to-object*)
115     (unless (setf
116              (gethash hash-code *quark-from-object*)
117              (assoc-delete object (gethash hash-code *quark-from-object*)))
118       (remhash hash-code *quark-from-object*))))
119
120
121
122 ;;;; Linked list (GList)
123
124 (deftype glist (type) 
125   `(or (null (cons ,type list))))
126
127 (defbinding (%glist-append "g_list_append") () pointer
128   (glist pointer)
129   (nil null))
130
131 (defun make-glist (type list)
132   (loop
133    with writer = (writer-function type)
134    for element in list
135    as glist = (%glist-append (or glist (make-pointer 0)))
136    do (funcall writer element glist)
137    finally (return glist)))
138
139 (defun glist-next (glist)
140   (unless (null-pointer-p glist)
141     (sap-ref-sap glist +size-of-pointer+)))
142   
143 ;; Also used for gslists
144 (defun map-glist (seqtype function glist element-type)
145   (let ((reader (reader-function element-type)))
146     (case seqtype 
147      ((nil)
148       (loop
149        as tmp = glist then (glist-next tmp)
150        until (null-pointer-p tmp)
151        do (funcall function (funcall reader tmp))))
152      (list
153       (loop
154        as tmp = glist then (glist-next tmp)
155        until (null-pointer-p tmp)
156        collect (funcall function (funcall reader tmp))))
157      (t
158       (coerce 
159        (loop
160         as tmp = glist then (glist-next tmp)
161         until (null-pointer-p tmp)
162         collect (funcall function (funcall reader tmp)))
163        seqtype)))))
164
165 (defbinding (glist-free "g_list_free") () nil
166   (glist pointer))
167
168 (defun destroy-glist (glist element-type)
169   (loop
170    with destroy = (destroy-function element-type)
171    as tmp = glist then (glist-next tmp)
172    until (null-pointer-p tmp)
173    do (funcall destroy tmp 0))
174   (glist-free glist))
175
176 (defmethod alien-type ((type (eql 'glist)) &rest args)
177   (declare (ignore type args))
178   (alien-type 'pointer))
179
180 (defmethod size-of ((type (eql 'glist)) &rest args)
181   (declare (ignore type args))
182   (size-of 'pointer))
183
184 (defmethod to-alien-form (list (type (eql 'glist)) &rest args)
185   (declare (ignore type))
186   (destructuring-bind (element-type) args    
187     `(make-glist ',element-type ,list)))
188
189 (defmethod to-alien-function ((type (eql 'glist)) &rest args)
190   (declare (ignore type))
191   (destructuring-bind (element-type) args    
192     #'(lambda (list)
193         (make-glist element-type list))))
194
195 (defmethod from-alien-form (glist (type (eql 'glist)) &rest args)
196   (declare (ignore type))
197   (destructuring-bind (element-type) args
198     `(let ((glist ,glist))
199       (unwind-protect
200            (map-glist 'list #'identity glist ',element-type)
201         (destroy-glist glist ',element-type)))))
202
203 (defmethod from-alien-function ((type (eql 'glist)) &rest args)
204   (declare (ignore type))
205   (destructuring-bind (element-type) args
206     #'(lambda (glist)
207         (unwind-protect
208              (map-glist 'list #'identity glist element-type)
209           (destroy-glist glist element-type)))))
210
211 (defmethod copy-from-alien-form (glist (type (eql 'glist)) &rest args)
212   (declare (ignore type))
213   (destructuring-bind (element-type) args
214     `(map-glist 'list #'identity ,glist ',element-type)))
215
216 (defmethod copy-from-alien-function ((type (eql 'glist)) &rest args)
217   (declare (ignore type))
218   (destructuring-bind (element-type) args
219     #'(lambda (glist)
220         (map-glist 'list #'identity glist element-type))))
221
222 (defmethod cleanup-form (glist (type (eql 'glist)) &rest args)
223   (declare (ignore type))
224   (destructuring-bind (element-type) args
225     `(destroy-glist ,glist ',element-type)))
226
227 (defmethod cleanup-function ((type (eql 'glist)) &rest args)
228   (declare (ignore type))
229   (destructuring-bind (element-type) args
230     #'(lambda (glist)
231         (destroy-glist glist element-type))))
232
233 (defmethod writer-function ((type (eql 'glist)) &rest args)
234   (declare (ignore type))
235   (destructuring-bind (element-type) args
236     #'(lambda (list location &optional (offset 0))
237         (setf 
238          (sap-ref-sap location offset)
239          (make-glist element-type list)))))
240
241 (defmethod reader-function ((type (eql 'glist)) &rest args)
242   (declare (ignore type))
243   (destructuring-bind (element-type) args
244     #'(lambda (location &optional (offset 0))
245         (unless (null-pointer-p (sap-ref-sap location offset))
246           (map-glist 'list #'identity (sap-ref-sap location offset) element-type)))))
247
248 (defmethod destroy-function ((type (eql 'glist)) &rest args)
249   (declare (ignore type))
250   (destructuring-bind (element-type) args
251     #'(lambda (location &optional (offset 0))
252         (unless (null-pointer-p (sap-ref-sap location offset))
253           (destroy-glist (sap-ref-sap location offset) element-type)
254           (setf (sap-ref-sap location offset) (make-pointer 0))))))
255
256
257
258 ;;;; Single linked list (GSList)
259
260 (deftype gslist (type) `(or (null (cons ,type list))))
261
262 (defbinding (%gslist-prepend "g_slist_prepend") () pointer
263   (gslist pointer)
264   (nil null))
265
266 (defun make-gslist (type list)
267   (loop
268    with writer = (writer-function type)
269    for element in (reverse list)
270    as gslist = (%gslist-prepend (or gslist (make-pointer 0)))
271    do (funcall writer element gslist)
272    finally (return gslist)))
273
274 (defbinding (gslist-free "g_slist_free") () nil
275   (gslist pointer))
276
277 (defun destroy-gslist (gslist element-type)
278   (loop
279    with destroy = (destroy-function element-type)
280    as tmp = gslist then (glist-next tmp)
281    until (null-pointer-p tmp)
282    do (funcall destroy tmp 0))
283   (gslist-free gslist))
284
285 (defmethod alien-type ((type (eql 'gslist)) &rest args)
286   (declare (ignore type args))
287   (alien-type 'pointer))
288
289 (defmethod size-of ((type (eql 'gslist)) &rest args)
290   (declare (ignore type args))
291   (size-of 'pointer))
292
293 (defmethod to-alien-form (list (type (eql 'gslist)) &rest args)
294   (declare (ignore type))
295   (destructuring-bind (element-type) args    
296     `(make-sglist ',element-type ,list)))
297
298 (defmethod to-alien-function ((type (eql 'gslist)) &rest args)
299   (declare (ignore type))
300   (destructuring-bind (element-type) args    
301     #'(lambda (list)
302         (make-gslist element-type list))))
303
304 (defmethod from-alien-form (gslist (type (eql 'gslist)) &rest args)
305   (declare (ignore type))
306   (destructuring-bind (element-type) args
307     `(let ((gslist ,gslist))
308       (unwind-protect
309            (map-glist 'list #'identity gslist ',element-type)
310         (destroy-gslist gslist ',element-type)))))
311
312 (defmethod from-alien-function ((type (eql 'gslist)) &rest args)
313   (declare (ignore type))
314   (destructuring-bind (element-type) args
315     #'(lambda (gslist)
316         (unwind-protect
317              (map-glist 'list #'identity gslist element-type)
318           (destroy-gslist gslist element-type)))))
319
320 (defmethod copy-from-alien-form (gslist (type (eql 'gslist)) &rest args)
321   (declare (ignore type))
322   (destructuring-bind (element-type) args
323     `(map-glist 'list #'identity ,gslist ',element-type)))
324
325 (defmethod from-alien-function ((type (eql 'gslist)) &rest args)
326   (declare (ignore type))
327   (destructuring-bind (element-type) args
328     #'(lambda (gslist)
329         (map-glist 'list #'identity gslist element-type))))
330
331 (defmethod cleanup-form (gslist (type (eql 'gslist)) &rest args)
332   (declare (ignore type))
333   (destructuring-bind (element-type) args
334     `(destroy-gslist ,gslist ',element-type)))
335
336 (defmethod cleanup-function ((type (eql 'gslist)) &rest args)
337   (declare (ignore type))
338   (destructuring-bind (element-type) args
339     #'(lambda (gslist)
340         (destroy-gslist gslist element-type))))
341
342 (defmethod writer-function ((type (eql 'gslist)) &rest args)
343   (declare (ignore type))
344   (destructuring-bind (element-type) args
345     #'(lambda (list location &optional (offset 0))
346         (setf 
347          (sap-ref-sap location offset)
348          (make-gslist element-type list)))))
349
350 (defmethod reader-function ((type (eql 'gslist)) &rest args)
351   (declare (ignore type))
352   (destructuring-bind (element-type) args
353     #'(lambda (location &optional (offset 0))
354         (unless (null-pointer-p (sap-ref-sap location offset))
355           (map-glist 'list #'identity (sap-ref-sap location offset) element-type)))))
356
357 (defmethod destroy-function ((type (eql 'gslist)) &rest args)
358   (declare (ignore type))
359   (destructuring-bind (element-type) args
360     #'(lambda (location &optional (offset 0))
361         (unless (null-pointer-p (sap-ref-sap location offset))
362           (destroy-gslist (sap-ref-sap location offset) element-type)
363           (setf (sap-ref-sap location offset) (make-pointer 0))))))
364
365
366 ;;; Vector
367
368 (defun make-c-vector (type length &optional content location)
369   (let* ((size-of-type (size-of type))
370          (location (or location (allocate-memory (* size-of-type length))))
371          (writer (writer-function type)))
372     (etypecase content
373       (vector
374        (loop
375         for element across content
376         for i from 0 below length
377         as offset = 0 then (+ offset size-of-type)
378         do (funcall writer element location offset)))
379       (list
380        (loop
381         for element in content
382         for i from 0 below length
383         as offset = 0 then (+ offset size-of-type)
384         do (funcall writer element location offset))))
385     location))
386
387
388 (defun map-c-vector (seqtype function location element-type length)
389   (let ((reader (reader-function element-type))
390         (size-of-element (size-of element-type)))
391     (case seqtype 
392      ((nil)
393       (loop
394        for i from 0 below length
395        as offset = 0 then (+ offset size-of-element)
396        do (funcall function (funcall reader location offset))))
397      (list
398       (loop
399        for i from 0 below length
400        as offset = 0 then (+ offset size-of-element)
401        collect (funcall function (funcall reader location offset))))
402      (t
403       (loop
404        with sequence = (make-sequence seqtype length)
405        for i from 0 below length
406        as offset = 0 then (+ offset size-of-element)
407        do (setf 
408            (elt sequence i)
409            (funcall function (funcall reader location offset)))
410        finally (return sequence))))))
411
412
413 (defun destroy-c-vector (location element-type length)
414   (loop
415    with destroy = (destroy-function element-type)
416    with element-size = (size-of element-type)
417    for i from 0 below length
418    as offset = 0 then (+ offset element-size)
419    do (funcall destroy location offset))
420   (deallocate-memory location))
421
422
423 (defmethod alien-type ((type (eql 'vector)) &rest args)
424   (declare (ignore type args))
425   (alien-type 'pointer))
426
427 (defmethod size-of ((type (eql 'vector)) &rest args)
428   (declare (ignore type args))
429   (size-of 'pointer))
430
431 (defmethod to-alien-form (vector (type (eql 'vector)) &rest args)
432   (declare (ignore type))
433   (destructuring-bind (element-type &optional (length '*)) args
434     (if (eq length '*)
435         `(let* ((vector ,vector)
436                 (location (sap+
437                            (allocate-memory (+ ,+size-of-int+ 
438                                                (* ,(size-of element-type) 
439                                                   (length vector))))
440                            ,+size-of-int+)))
441           (make-c-vector ',element-type (length vector) vector location)
442           (setf (sap-ref-32 location ,(- +size-of-int+)) (length vector))
443           location)       
444       `(make-c-vector ',element-type ,length ,vector))))
445
446 (defmethod from-alien-form (c-vector (type (eql 'vector)) &rest args)
447   (declare (ignore type))
448   (destructuring-bind (element-type &optional (length '*)) args
449     (if (eq length '*)
450         (error "Can't use vector of variable size as return type")
451       `(let ((c-vector ,c-vector))
452         (prog1
453             (map-c-vector 'vector #'identity ',element-type ,length c-vector)
454           (destroy-c-vector c-vector ',element-type ,length))))))
455
456 (defmethod copy-from-alien-form (c-vector (type (eql 'vector)) &rest args)
457   (declare (ignore type))
458   (destructuring-bind (element-type &optional (length '*)) args
459     (if (eq length '*)
460         (error "Can't use vector of variable size as return type")
461       `(map-c-vector 'vector #'identity ',element-type ',length ,c-vector))))
462
463 (defmethod cleanup-form (location (type (eql 'vector)) &rest args)
464   (declare (ignore type))
465   (destructuring-bind (element-type &optional (length '*)) args
466     `(let* ((location ,location)
467             (length ,(if (eq length '*)
468                          `(sap-ref-32 location ,(- +size-of-int+))
469                          length)))
470       (loop
471        with destroy = (destroy-function ',element-type)
472        for i from 0 below length
473        as offset = 0 then (+ offset ,(size-of element-type))
474        do (funcall destroy location offset))
475       (deallocate-memory ,(if (eq length '*) 
476                               `(sap+ location  ,(- +size-of-int+))
477                             'location)))))
478
479 (defmethod writer-function ((type (eql 'vector)) &rest args)
480   (declare (ignore type))
481   (destructuring-bind (element-type &optional (length '*)) args
482     #'(lambda (vector location &optional (offset 0))
483         (setf 
484          (sap-ref-sap location offset)
485          (make-c-vector element-type length vector)))))
486
487 (defmethod reader-function ((type (eql 'vector)) &rest args)
488   (declare (ignore type))
489   (destructuring-bind (element-type &optional (length '*)) args
490     (if (eq length '*)
491         (error "Can't create reader function for vector of variable size")
492       #'(lambda (location &optional (offset 0))
493           (unless (null-pointer-p (sap-ref-sap location offset))
494             (map-c-vector 'vector #'identity (sap-ref-sap location offset) 
495              element-type length))))))
496
497 (defmethod destroy-function ((type (eql 'vector)) &rest args)
498   (declare (ignore type))
499   (destructuring-bind (element-type &optional (length '*)) args
500     (if (eq length '*)
501         (error "Can't create destroy function for vector of variable size")
502       #'(lambda (location &optional (offset 0))
503           (unless (null-pointer-p (sap-ref-sap location offset))
504             (destroy-c-vector 
505              (sap-ref-sap location offset) element-type length)
506             (setf (sap-ref-sap location offset) (make-pointer 0)))))))