chiark / gitweb /
Major cleanup of ffi abstraction layer
[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.16 2004/11/06 21:39: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
76 ;;;; Quarks
77
78 (internal *quark-counter* *quark-from-object* *quark-to-object*)
79
80 (deftype quark () 'unsigned)
81
82 ;(defbinding %quark-get-reserved () quark)
83
84 (defbinding %quark-from-string () quark
85   (string string))
86
87 (defvar *quark-counter* 0)
88
89 (defun %quark-get-reserved ()
90   ;; The string is just a dummy
91   (%quark-from-string (format nil "#@£$%&-quark-~D" (incf *quark-counter*))))
92
93 (defvar *quark-from-object* (make-hash-table))
94 (defvar *quark-to-object* (make-hash-table))
95
96 (defun quark-from-object (object &key (test #'eq))
97   (let ((hash-code (sxhash object)))
98     (or
99      (assoc-ref object (gethash hash-code *quark-from-object*) :test test)
100      (let ((quark (%quark-get-reserved)))
101        (setf
102         (gethash hash-code *quark-from-object*)
103         (append
104          (gethash hash-code *quark-from-object*)
105          (list (cons object quark))))
106        (setf (gethash quark *quark-to-object*) object)
107        quark))))
108
109 (defun quark-to-object (quark) 
110   (gethash quark *quark-to-object*))
111   
112 (defun remove-quark (quark)
113   (let* ((object (gethash quark *quark-to-object*))
114          (hash-code (sxhash object)))
115     (remhash quark *quark-to-object*)
116     (unless (setf
117              (gethash hash-code *quark-from-object*)
118              (assoc-delete object (gethash hash-code *quark-from-object*)))
119       (remhash hash-code *quark-from-object*))))
120
121
122
123 ;;;; Linked list (GList)
124
125 (deftype glist (type &key copy) 
126   (declare (ignore copy))
127   `(or (null (cons ,type list))))
128
129 (defbinding (%glist-append-unsigned "g_list_append") () pointer
130   (glist pointer)
131   (data unsigned))
132
133 (defbinding (%glist-append-signed "g_list_append") () pointer
134   (glist pointer)
135   (data signed))
136
137 (defbinding (%glist-append-sap "g_list_append") () pointer
138   (glist pointer)
139   (data pointer))
140
141 (defun make-glist (type list)
142   (let ((new-element (ecase (alien-type type)
143                        (system-area-pointer #'%glist-append-sap)
144                        ((signed-byte c-call:short c-call:int c-call:long)
145                         #'%glist-append-signed)
146                        ((unsigned-byte c-call:unsigned-short 
147                          c-call:unsigned-int c-call:unsigned-long)
148                         #'%glist-append-unsigned)))
149         (to-alien (to-alien-function type)))
150     (loop
151      for element in list
152      as glist = (funcall new-element (or glist (make-pointer 0)) 
153                  (funcall to-alien element))
154      finally (return glist))))
155
156 (defun glist-next (glist)
157   (unless (null-pointer-p glist)
158     (sap-ref-sap glist +size-of-pointer+)))
159   
160 ;; Also used for gslists
161 (defun map-glist (seqtype function glist element-type)
162   (let ((reader (reader-function element-type)))
163     (case seqtype 
164      ((nil)
165       (loop
166        as tmp = glist then (glist-next tmp)
167        until (null-pointer-p tmp)
168        do (funcall function (funcall reader tmp))))
169      (list
170       (loop
171        as tmp = glist then (glist-next tmp)
172        until (null-pointer-p tmp)
173        collect (funcall function (funcall reader tmp))))
174      (t
175       (coerce 
176        (loop
177         as tmp = glist then (glist-next tmp)
178         until (null-pointer-p tmp)
179         collect (funcall function (funcall reader tmp)))
180        seqtype)))))
181
182 (defbinding (glist-free "g_list_free") () nil
183   (glist pointer))
184
185
186 (defmethod alien-type ((type (eql 'glist)) &rest args)
187   (declare (ignore type args))
188   (alien-type 'pointer))
189
190 (defmethod size-of ((type (eql 'glist)) &rest args)
191   (declare (ignore type args))
192   (size-of 'pointer))
193
194 (defmethod to-alien-form (list (type (eql 'glist)) &rest args)
195   (declare (ignore type))
196   (destructuring-bind (element-type) args    
197     `(make-glist ',element-type ,list)))
198
199 (defmethod to-alien-function ((type (eql 'glist)) &rest args)
200   (declare (ignore type args))
201   (destructuring-bind (element-type) args    
202     #'(lambda (list)
203         (make-glist element-type list))))
204
205 (defmethod from-alien-form (glist (type (eql 'glist)) &rest args)
206   (declare (ignore type))
207   (destructuring-bind (element-type) args
208     `(let ((glist ,glist))
209       (unwind-protect
210            (map-glist 'list #'identity glist ',element-type)
211         (glist-free glist)))))
212
213 (defmethod from-alien-function ((type (eql 'glist)) &rest args)
214   (declare (ignore type))
215   (destructuring-bind (element-type) args
216     #'(lambda (glist)
217         (unwind-protect
218              (map-glist 'list #'identity glist element-type)
219           (glist-free glist)))))
220
221 (defmethod cleanup-form (glist (type (eql 'glist)) &rest args)
222   (declare (ignore type args))
223   `(glist-free ,glist))
224
225 (defmethod cleanup-function ((type (eql 'glist)) &rest args)
226   (declare (ignore type args))
227   #'glist-free)
228
229
230
231 ;;;; Single linked list (GSList)
232
233 (deftype gslist (type) `(or (null (cons ,type list))))
234
235 (defbinding (%gslist-prepend-unsigned "g_slist_prepend") () pointer
236   (gslist pointer)
237   (data unsigned))
238
239 (defbinding (%gslist-prepend-signed "g_slist_prepend") () pointer
240   (gslist pointer)
241   (data signed))
242
243 (defbinding (%gslist-prepend-sap "g_slist_prepend") () pointer
244   (gslist pointer)
245   (data pointer))
246
247 (defun make-gslist (type list)
248   (let ((new-element (ecase (alien-type type)
249                        (system-area-pointer #'%gslist-prepend-sap)
250                        ((signed-byte c-call:short c-call:int c-call:long)
251                         #'%gslist-prepend-signed)
252                        ((unsigned-byte c-call:unsigned-short 
253                          c-call:unsigned-int c-call:unsigned-long)
254                         #'%gslist-prepend-unsigned)))
255         (to-alien (to-alien-function type)))
256     (loop
257      for element in (reverse list)
258      as gslist = (funcall new-element (or gslist (make-pointer 0)) 
259                   (funcall to-alien element))
260      finally (return gslist))))
261
262 (defbinding (gslist-free "g_slist_free") () nil
263   (gslist pointer))
264
265
266 (defmethod alien-type ((type (eql 'gslist)) &rest args)
267   (declare (ignore type args))
268   (alien-type 'pointer))
269
270 (defmethod size-of ((type (eql 'gslist)) &rest args)
271   (declare (ignore type args))
272   (size-of 'pointer))
273
274 (defmethod to-alien-form (list (type (eql 'gslist)) &rest args)
275   (declare (ignore type))
276   (destructuring-bind (element-type) args    
277     `(make-sglist ',element-type ,list)))
278
279 (defmethod to-alien-function ((type (eql 'gslist)) &rest args)
280   (declare (ignore type args))
281   (destructuring-bind (element-type) args    
282     #'(lambda (list)
283         (make-gslist element-type list))))
284
285 (defmethod from-alien-form (gslist (type (eql 'gslist)) &rest args)
286   (declare (ignore type))
287   (destructuring-bind (element-type) args
288     `(let ((gslist ,gslist))
289       (unwind-protect
290            (map-glist 'list #'identity gslist ',element-type)
291         (gslist-free gslist)))))
292
293 (defmethod from-alien-function ((type (eql 'gslist)) &rest args)
294   (declare (ignore type))
295   (destructuring-bind (element-type) args
296     #'(lambda (gslist)
297         (unwind-protect
298              (map-glist 'list #'identity gslist element-type)
299           (gslist-free gslist)))))
300
301 (defmethod cleanup-form (list (type (eql 'gslist)) &rest args)
302   (declare (ignore type args))
303   `(gslist-free ,list))
304
305 (defmethod cleanup-function ((type (eql 'gslist)) &rest args)
306   (declare (ignore type args))
307   #'gslist-free)
308
309
310
311 ;;; Vector
312
313 (defun make-c-vector (type length &optional content location)
314   (let* ((size-of-type (size-of type))
315          (location (or location (allocate-memory (* size-of-type length))))
316          (writer (writer-function type)))
317     (loop
318      for element across content
319      for i from 0 below length
320      as offset = 0 then (+ offset size-of-type)
321      do (funcall writer element location offset))
322     location))
323
324
325 (defun map-c-vector (seqtype function location element-type length)
326   (let ((reader (reader-function element-type))
327         (size-of-element (size-of element-type)))
328     (case seqtype 
329      ((nil)
330       (loop
331        for i from 0 below length
332        as offset = 0 then (+ offset size-of-element)
333        do (funcall function (funcall reader location offset))))
334      (list
335       (loop
336        for i from 0 below length
337        as offset = 0 then (+ offset size-of-element)
338        collect (funcall function (funcall reader location offset))))
339      (t
340       (loop
341        with sequence = (make-sequence seqtype length)
342        for i from 0 below length
343        as offset = 0 then (+ offset size-of-element)
344        do (setf 
345            (elt sequence i)
346            (funcall function (funcall reader location offset)))
347        finally (return sequence))))))
348
349
350 (defmethod alien-type ((type (eql 'vector)) &rest args)
351   (declare (ignore type args))
352   (alien-type 'pointer))
353
354 (defmethod size-of ((type (eql 'vector)) &rest args)
355   (declare (ignore type args))
356   (size-of 'pointer))
357
358 (defmethod to-alien-form (vector (type (eql 'vector)) &rest args)
359   (declare (ignore type))
360   (destructuring-bind (element-type &optional (length '*)) args
361     (if (eq length '*)
362         `(let* ((vector ,vector)
363                 (location (sap+
364                            (allocate-memory (+ ,+size-of-int+ 
365                                                (* ,(size-of element-type) 
366                                                   (length vector))))
367                            ,+size-of-int+)))
368           (make-c-vector ',element-type (length vector) vector location)
369           (setf (sap-ref-32 location ,(- +size-of-int+)) (length vector))
370           location)       
371       `(make-c-vector ',element-type ,length ,vector))))
372
373 (defmethod from-alien-form (location (type (eql 'vector)) &rest args)
374   (declare (ignore type))
375   (destructuring-bind (element-type &optional (length '*)) args
376     (if (eq length '*)
377         (error "Can't use vector of variable size as return type")
378       `(map-c-vector 'vector #'identity ',element-type ',length ,location))))
379
380 (defmethod cleanup-form (location (type (eql 'vector)) &rest args)
381   (declare (ignore type))
382   (destructuring-bind (element-type &optional (length '*)) args
383     `(let* ((location ,location)
384             (length ,(if (eq length '*)
385                          `(sap-ref-32 location ,(- +size-of-int+))
386                          length)))
387       (loop
388        with destroy = (destroy-function ',element-type)
389        for i from 0 below length
390        as offset = 0 then (+ offset ,(size-of element-type))
391        do (funcall destroy location offset))
392       (deallocate-memory ,(if (eq length '*) 
393                               `(sap+ location  ,(- +size-of-int+))
394                             'location)))))