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