Quarks simplified
[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)))))))