Added function for user data
[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.8 2001-02-11 20:21:13 espen Exp $
19
20
21 (in-package "GLIB")
22
23 (use-prefix "g")
24
25
26 ;;;; Memory management
27
28 (define-foreign ("g_malloc0" allocate-memory) () pointer
29 (size unsigned-long))
30
31 (define-foreign ("g_realloc" reallocate-memory) () pointer
32 (address pointer)
33 (size unsigned-long))
34
35 (define-foreign ("g_free" deallocate-memory) () nil
36 (address pointer))
37
38 (defun copy-memory (from length &optional (to (allocate-memory length)))
39 (kernel:system-area-copy from 0 to 0 (* 8 length))
40 to)
41
42
43 ;;;; User data mechanism
44
45 (internal *user-data* *user-data-count*)
46
47 (declaim (fixnum *user-data-count*))
48
49 (defvar *destroy-notify* (system:foreign-symbol-address "destroy_notify"))
50 (defvar *user-data* (make-hash-table))
51 (defvar *user-data-count* 0)
52
53 (defun register-user-data (object &optional destroy-function)
54 (check-type destroy-function (or null symbol function))
55 (incf *user-data-count*)
56 (setf
57 (gethash *user-data-count* *user-data*)
58 (cons object destroy-function))
59 *user-data-count*)
60
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)))
65
66 (defun destroy-user-data (id)
67 (check-type id fixnum)
68 (let ((user-data (gethash id *user-data*)))
69 (when (cdr user-data)
70 (funcall (cdr user-data) (car user-data))))
71 (remhash id *user-data*))
72
73
74
75 ;;;; Quarks
76
77 (internal *quark-counter* *quark-from-object* *quark-to-object*)
78
79 (deftype quark () 'unsigned)
80
81 ;(define-foreign %quark-get-reserved () quark)
82
83 (define-foreign %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) `(or (null (cons ,type list))))
125
126 (define-foreign ("g_list_append" %glist-append-unsigned) () pointer
127 (glist pointer)
128 (data unsigned))
129
130 (define-foreign ("g_list_append" %glist-append-signed) () pointer
131 (glist pointer)
132 (data signed))
133
134 (define-foreign ("g_list_append" %glist-append-sap) () pointer
135 (glist pointer)
136 (data pointer))
137
138 (defmacro glist-append (glist value type-spec)
139 (ecase (first (mklist (translate-type-spec type-spec)))
140 (unsigned `(%glist-append-unsigned ,glist ,value))
141 (signed `(%glist-append-signed ,glist ,value))
142 (system-area-pointer `(%glist-append-sap ,glist ,value))))
143
144 (defmacro glist-data (glist type-spec)
145 (ecase (first (mklist (translate-type-spec type-spec)))
146 (unsigned `(sap-ref-unsigned ,glist 0))
147 (signed `(sap-ref-signed ,glist 0))
148 (system-area-pointer `(sap-ref-sap ,glist 0))))
149
150 (defun glist-next (glist)
151 (unless (null-pointer-p glist)
152 (sap-ref-sap glist +size-of-sap+)))
153
154 (define-foreign ("g_list_free" glist-free) () nil
155 (glist pointer))
156
157 (deftype-method translate-type-spec glist (type-spec)
158 (declare (ignore type-spec))
159 (translate-type-spec 'pointer))
160
161 (deftype-method size-of glist (type-spec)
162 (declare (ignore type-spec))
163 (size-of 'pointer))
164
165 (deftype-method translate-to-alien glist (type-spec list &optional copy)
166 (declare (ignore copy))
167 (let* ((element-type-spec (second (type-expand-to 'glist type-spec)))
168 (to-alien (translate-to-alien element-type-spec 'element t)))
169 `(let ((glist (make-pointer 0)))
170 (dolist (element ,list glist)
171 (setq glist (glist-append glist ,to-alien ,element-type-spec))))))
172
173 (deftype-method translate-from-alien
174 glist (type-spec glist &optional (alloc :reference))
175 (let ((element-type-spec (second (type-expand-to 'glist type-spec))))
176 `(let ((glist ,glist)
177 (list nil))
178 (do ((tmp glist (glist-next tmp)))
179 ((null-pointer-p tmp))
180 (push
181 ,(translate-from-alien
182 element-type-spec `(glist-data tmp ,element-type-spec) alloc)
183 list))
184 ,(when (eq alloc :reference)
185 '(glist-free glist))
186 (nreverse list))))
187
188 (deftype-method cleanup-alien glist (type-spec glist &optional copied)
189 (declare (ignore copied))
190 (let* ((element-type-spec (second (type-expand-to 'glist type-spec)))
191 (alien-type-spec (translate-type-spec element-type-spec)))
192 `(let ((glist ,glist))
193 (unless (null-pointer-p glist)
194 ,(when (eq alien-type-spec 'system-area-pointer)
195 `(do ((tmp glist (glist-next tmp)))
196 ((null-pointer-p tmp))
197 ,(cleanup-alien
198 element-type-spec `(glist-data tmp ,element-type-spec) t)))
199 (glist-free glist)))))
200
201
202
203 ;;;; Single linked list (GSList)
204
205 (deftype gslist (type) `(or (null (cons ,type list))))
206
207 (define-foreign ("g_slist_prepend" %gslist-prepend-unsigned) () pointer
208 (gslist pointer)
209 (data unsigned))
210
211 (define-foreign ("g_slist_prepend" %gslist-prepend-signed) () pointer
212 (gslist pointer)
213 (data signed))
214
215 (define-foreign ("g_slist_prepend" %gslist-prepend-sap) () pointer
216 (gslist pointer)
217 (data pointer))
218
219 (defmacro gslist-prepend (gslist value type-spec)
220 (ecase (first (mklist (translate-type-spec type-spec)))
221 (unsigned `(%gslist-prepend-unsigned ,gslist ,value))
222 (signed `(%gslist-prepend-signed ,gslist ,value))
223 (system-area-pointer `(%gslist-prepend-sap ,gslist ,value))))
224
225 (define-foreign ("g_slist_free" gslist-free) () nil
226 (gslist pointer))
227
228 (deftype-method translate-type-spec gslist (type-spec)
229 (declare (ignore type-spec))
230 (translate-type-spec 'pointer))
231
232 (deftype-method size-of gslist (type-spec)
233 (declare (ignore type-spec))
234 (size-of 'pointer))
235
236 (deftype-method translate-to-alien gslist (type-spec list &optional copy)
237 (declare (ignore copy))
238 (let* ((element-type-spec (second (type-expand-to 'gslist type-spec)))
239 (to-alien (translate-to-alien element-type-spec 'element t)))
240 `(let ((gslist (make-pointer 0)))
241 (dolist (element (reverse ,list) gslist)
242 (setq gslist (gslist-prepend gslist ,to-alien ,element-type-spec))))))
243
244 (deftype-method translate-from-alien
245 gslist (type-spec gslist &optional (alloc :reference))
246 (let ((element-type-spec (second (type-expand-to 'gslist type-spec))))
247 `(let ((gslist ,gslist)
248 (list nil))
249 (do ((tmp gslist (glist-next tmp)))
250 ((null-pointer-p tmp))
251 (push
252 ,(translate-from-alien
253 element-type-spec `(glist-data tmp ,element-type-spec) alloc)
254 list))
255 ,(when (eq alloc :reference)
256 '(gslist-free gslist))
257 (nreverse list))))
258
259 (deftype-method cleanup-alien gslist (type-spec gslist &optional copied)
260 (declare (ignore copied))
261 (let* ((element-type-spec (second (type-expand-to 'gslist type-spec)))
262 (alien-type-spec (translate-type-spec element-type-spec)))
263 `(let ((gslist ,gslist))
264 (unless (null-pointer-p gslist)
265 ,(when (eq alien-type-spec 'system-area-pointer)
266 `(do ((tmp gslist (glist-next tmp)))
267 ((null-pointer-p tmp))
268 ,(cleanup-alien
269 element-type-spec `(glist-data tmp ,element-type-spec) t)))
270 (gslist-free gslist)))))
271
272
273
274 ;;; Vector
275
276 (deftype-method translate-type-spec vector (type-spec)
277 (declare (ignore type-spec))
278 (translate-type-spec 'pointer))
279
280 (deftype-method size-of vector (type-spec)
281 (declare (ignore type-spec))
282 (size-of 'pointer))
283
284 (deftype-method translate-to-alien vector (type-spec vector &optional copy)
285 (declare (ignore copy))
286 (destructuring-bind (element-type &optional (length '*))
287 (cdr (type-expand-to 'vector type-spec))
288 (let ((element-size (size-of element-type)))
289 `(let ((vector ,vector))
290 (let ((c-vector
291 (allocate-memory
292 ,(if (eq length '*)
293 `(* ,element-size (length vector))
294 (* element-size length)))))
295 (dotimes (i ,(if (eq length '*) '(length vector) length) c-vector)
296 (setf
297 (,(sap-ref-fname element-type) c-vector (* i ,element-size))
298 ,(translate-to-alien element-type '(aref vector i) :copy))))))))
299
300 (deftype-method translate-from-alien
301 vector (type-spec sap &optional (alloc :reference))
302 (destructuring-bind (element-type &optional (length '*))
303 (cdr (type-expand-to 'vector type-spec))
304 (when (eq length '*)
305 (error "Can't use vectors of variable length as return type"))
306 (let ((element-size (size-of element-type)))
307 `(let ((sap ,sap)
308 (vector (make-array ,length :element-type ',element-type)))
309 (dotimes (i ,length vector)
310 (setf
311 (aref vector i)
312 ,(translate-to-alien
313 element-type
314 `(,(sap-ref-fname element-type) sap (* i ,element-size))
315 alloc)))))))
316
317
318 (deftype-method cleanup-alien vector (type-spec sap &optional copied)
319 (declare (ignore type-spec copied))
320 ;; The individual elements also have to be cleaned up to avoid memory leaks,
321 ;; but this is currently not possible because we can't always tell the
322 ;; length of the vector
323 `(deallocate-memory ,sap))