chiark / gitweb /
glib-package.lisp renamed
[clg] / glib / gtype.lisp
CommitLineData
560af5c5 1;; Common Lisp bindings for GTK+ v2.0
d4b21b08 2;; Copyright (C) 2000-2001 Espen S. Johnsen <esj@stud.cs.uit.no>
560af5c5 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
80a09c29 18;; $Id: gtype.lisp,v 1.16 2002-03-24 12:56:03 espen Exp $
560af5c5 19
20(in-package "GLIB")
21
22(use-prefix "g")
23
560af5c5 24;;;;
25
26(deftype type-number () '(unsigned 32))
27
93aa67db 28(eval-when (:compile-toplevel :load-toplevel :execute)
d4b21b08 29 (defclass type-query (struct)
93aa67db 30 ((type-number :allocation :alien :type type-number)
31 (name :allocation :alien :type string)
32 (class-size :allocation :alien :type unsigned-int)
33 (instance-size :allocation :alien :type unsigned-int))
34 (:metaclass proxy-class)))
35
36
93aa67db 37(defbinding %type-query () nil
38 (type type-number)
39 (query type-query))
40
41(defun type-query (type)
42 (let ((query (make-instance 'type-query)))
d4b21b08 43 (%type-query (find-type-number type t) query)
93aa67db 44 query))
45
46(defun type-instance-size (type)
47 (slot-value (type-query type) 'instance-size))
48
49(defun type-class-size (type)
50 (slot-value (type-query type) 'class-size))
560af5c5 51
d4b21b08 52(defbinding type-class-ref (type) pointer
53 ((find-type-number type t) type-number))
560af5c5 54
d4b21b08 55(defbinding type-class-unref (type) nil
56 ((find-type-number type t) type-number))
93aa67db 57
d4b21b08 58(defbinding type-class-peek (type) pointer
59 ((find-type-number type t) type-number))
93aa67db 60
560af5c5 61
d4b21b08 62;;;; Mapping between lisp types and glib types
560af5c5 63
64(defvar *type-to-number-hash* (make-hash-table))
65(defvar *number-to-type-hash* (make-hash-table))
66
d4b21b08 67(defun register-type (type id)
68 (let ((type-number
69 (etypecase id
70 (integer id)
71 (string (find-type-number id t)))))
72 (setf (gethash type *type-to-number-hash*) type-number)
73 (setf (gethash type-number *number-to-type-hash*) type)
74 type-number))
75
76(defbinding %type-from-name () type-number
77 (name string))
78
79(defun find-type-number (type &optional error)
560af5c5 80 (etypecase type
81 (integer type)
d4b21b08 82 (string
83 (let ((type-number (%type-from-name type)))
84 (cond
85 ((and (zerop type-number) error)
86 (error "Invalid alien type name: ~A" type))
87 ((zerop type-number) nil)
88 (t type-number))))
89 (symbol
90 (let ((type-number (gethash type *type-to-number-hash*)))
91 (or
92 type-number
93 (and error (error "Type not registered: ~A" type)))))
94 (pcl::class (find-type-number (class-name type) error))))
560af5c5 95
4de90d10 96(defun type-from-number (type-number &optional error)
97 (multiple-value-bind (type found)
98 (gethash type-number *number-to-type-hash*)
99 (when (and error (not found))
100 (let ((name (find-type-name type-number)))
101 (if name
102 (error "Type number not registered: ~A (~A)" type-number name)
103 (error "Invalid type number: ~A" type-number))))
104 type))
560af5c5 105
d4b21b08 106(defun type-from-name (name)
107 (etypecase name
108 (string (type-from-number (find-type-number name t)))))
560af5c5 109
d4b21b08 110(defbinding (find-type-name "g_type_name") (type) string
111 ((find-type-number type t) type-number))
112
113(defun type-number-of (object)
114 (find-type-number (type-of object) t))
115
116(defun init-type (init)
117 (mapc
118 #'(lambda (fname)
119 (funcall (mkbinding fname 'type-number)))
120 (mklist init)))
121
4de90d10 122(defun %init-types-in-library (pathname ignore)
d4b21b08 123 (let ((process (ext:run-program
0c0db5e2 124 "nm" (list "-D" (namestring (truename pathname)))
d4b21b08 125 :output :stream :wait nil))
126 (fnames ()))
127 (labels ((read-symbols ()
128 (let ((line (read-line (ext:process-output process) nil)))
129 (when line
4de90d10 130 (let ((symbol (subseq line 11)))
131 (when (and
132 (search "_get_type" symbol)
133 (not (member symbol ignore :test #'string=)))
134 (push symbol fnames)))
d4b21b08 135 (read-symbols)))))
136 (read-symbols)
137 (ext:process-close process)
138 `(init-type ',fnames))))
560af5c5 139
0c0db5e2 140(defmacro init-types-in-library (filename &key ignore)
141 (%init-types-in-library
b2bea410 142 (format nil "~A/~A" *gtk-library-path* filename) ignore))
4de90d10 143
144
560af5c5 145
93aa67db 146;;;; Superclass for wrapping types in the glib type system
560af5c5 147
148(eval-when (:compile-toplevel :load-toplevel :execute)
93aa67db 149 (defclass ginstance (proxy)
d4b21b08 150 ((class :allocation :alien :type pointer))
151 (:metaclass proxy-class)))
560af5c5 152
93aa67db 153(defun %type-of-ginstance (location)
154 (let ((class (sap-ref-sap location 0)))
155 (type-from-number (sap-ref-unsigned class 0))))
560af5c5 156
157(deftype-method translate-from-alien
93aa67db 158 ginstance (type-spec location &optional weak-ref)
159 (declare (ignore type-spec))
560af5c5 160 `(let ((location ,location))
161 (unless (null-pointer-p location)
93aa67db 162 (ensure-proxy-instance
163 (%type-of-ginstance location) location ,weak-ref))))
560af5c5 164
560af5c5 165
166
93aa67db 167;;;; Metaclass for subclasses of ginstance
560af5c5 168
169(eval-when (:compile-toplevel :load-toplevel :execute)
d4b21b08 170 (defclass ginstance-class (proxy-class)))
560af5c5 171
172
c8c48a4c 173(defmethod shared-initialize ((class ginstance-class) names
d4b21b08 174 &rest initargs &key name alien-name
175 size ref unref)
560af5c5 176 (declare (ignore initargs names))
93aa67db 177 (let* ((class-name (or name (class-name class)))
178 (type-number
d4b21b08 179 (find-type-number
4de90d10 180 (or (first alien-name) (default-alien-type-name class-name)) t)))
d4b21b08 181 (register-type class-name type-number)
182 (let ((size (or size (type-instance-size type-number))))
4de90d10 183 (declare (special size))
d4b21b08 184 (call-next-method)))
185
186 (when ref
187 (let ((ref (mkbinding (first ref) 'pointer 'pointer)))
93aa67db 188 (setf
d4b21b08 189 (slot-value class 'copy)
190 #'(lambda (type location)
191 (declare (ignore type))
192 (funcall ref location)))))
193 (when unref
194 (let ((unref (mkbinding (first unref) 'nil 'pointer)))
93aa67db 195 (setf
d4b21b08 196 (slot-value class 'free)
197 #'(lambda (type location)
198 (declare (ignore type))
199 (funcall unref location))))))
560af5c5 200
201
202(defmethod validate-superclass
c8c48a4c 203 ((class ginstance-class) (super pcl::standard-class))
204 (subtypep (class-name super) 'ginstance))
560af5c5 205
206
d4b21b08 207;;;; Registering fundamental types
208
209(register-type 'pointer "gpointer")
210(register-type 'char "gchar")
211(register-type 'unsigned-char "guchar")
212(register-type 'boolean "gboolean")
213(register-type 'fixnum "gint")
214(register-type 'int "gint")
215(register-type 'unsigned-int "guint")
216(register-type 'long "glong")
217(register-type 'unsigned-long "gulong")
218(register-type 'single-float "gfloat")
219(register-type 'double-float "gdouble")
4de90d10 220(register-type 'string "gchararray")
d4b21b08 221
222
223;;;;
224
e77e7713 225(defvar *derivable-type-info* (make-hash-table))
d4b21b08 226
e77e7713 227(defun register-derivable-type (type id expander)
d4b21b08 228 (register-type type id)
e77e7713 229 (let ((type-number (register-type type id)))
230 (setf (gethash type-number *derivable-type-info*) expander)))
d4b21b08 231
4de90d10 232(defun find-type-info (type)
233 (dolist (super (cdr (type-hierarchy type)))
e77e7713 234 (let ((info (gethash super *derivable-type-info*)))
4de90d10 235 (return-if info))))
236
e77e7713 237(defun expand-type-definition (type options)
238 (let ((expander (find-type-info type)))
239 (funcall expander (find-type-number type t) options)))
d4b21b08 240
d4b21b08 241(defbinding type-parent (type) type-number
242 ((find-type-number type t) type-number))
243
244(defun supertype (type)
245 (type-from-number (type-parent type)))
246
337933d8 247(defbinding %type-interfaces (type) pointer
248 ((find-type-number type t) type-number)
249 (n-interfaces unsigned-int :out))
250
251(defun type-interfaces (type)
252 (multiple-value-bind (array length) (%type-interfaces type)
253 (unwind-protect
254 (map-c-array 'list #'identity array 'type-number length)
255 (deallocate-memory array))))
256
257(defun implements (type)
258 (mapcar #'type-from-number (type-interfaces type)))
259
d4b21b08 260(defun type-hierarchy (type)
261 (let ((type-number (find-type-number type t)))
262 (unless (= type-number 0)
263 (cons type-number (type-hierarchy (type-parent type-number))))))
264
265(defbinding (type-is-p "g_type_is_a") (type super) boolean
266 ((find-type-number type) type-number)
267 ((find-type-number super) type-number))
268
269(defbinding %type-children () pointer
270 (type-number type-number)
271 (num-children unsigned-int :out))
272
273(defun map-subtypes (function type &optional prefix)
274 (let ((type-number (find-type-number type t)))
275 (multiple-value-bind (array length) (%type-children type-number)
276 (unwind-protect
277 (map-c-array
278 'nil
279 #'(lambda (type-number)
280 (when (or
281 (not prefix)
282 (string-prefix-p prefix (find-type-name type-number)))
283 (funcall function type-number))
284 (map-subtypes function type-number prefix))
285 array 'type-number length)
286 (deallocate-memory array)))))
287
288(defun find-types (prefix)
289 (let ((type-list nil))
e77e7713 290 (maphash
291 #'(lambda (type-number expander)
292 (declare (ignore expander))
293 (map-subtypes
294 #'(lambda (type-number)
295 (pushnew type-number type-list))
296 type-number prefix))
297 *derivable-type-info*)
d4b21b08 298 type-list))
299
300(defun %sort-types-topologicaly (unsorted)
301 (let ((sorted ()))
302 (loop while unsorted do
303 (dolist (type unsorted)
337933d8 304 (let ((dependencies
305 (append (rest (type-hierarchy type)) (type-interfaces type))))
d4b21b08 306 (cond
307 ((null dependencies)
308 (push type sorted)
309 (setq unsorted (delete type unsorted)))
310 (t
311 (unless (dolist (dep dependencies)
e77e7713 312 (when (find type (rest (type-hierarchy dep)))
313 (error "Cyclic type dependencie"))
d4b21b08 314 (return-if (find dep unsorted)))
315 (push type sorted)
316 (setq unsorted (delete type unsorted))))))))
317 (nreverse sorted)))
318
319
320(defun expand-type-definitions (prefix &optional args)
e77e7713 321 (flet ((type-options (type-number)
d4b21b08 322 (let ((name (find-type-name type-number)))
4de90d10 323 (cdr (assoc name args :test #'string=)))))
d4b21b08 324
e77e7713 325 (let ((type-list
326 (delete-if
327 #'(lambda (type-number)
328 (let ((name (find-type-name type-number)))
329 (or
330 (getf (type-options type-number) :ignore)
331 (find-if
332 #'(lambda (options)
333 (and
334 (string-prefix-p (first options) name)
80a09c29 335 (getf (cdr options) :ignore-prefix)
336 (not (some
337 #'(lambda (exception)
338 (string= name exception))
339 (getf (cdr options) :except)))))
e77e7713 340 args))))
341 (find-types prefix))))
d4b21b08 342
e77e7713 343 (dolist (type-number type-list)
344 (let ((name (find-type-name type-number)))
345 (register-type
346 (getf (type-options type-number) :type (default-type-name name))
347 type-number)))
348
349 `(progn
350 ,@(mapcar
351 #'(lambda (type)
352 (expand-type-definition type (type-options type)))
353 (%sort-types-topologicaly type-list))))))
354
d4b21b08 355(defmacro define-types-by-introspection (prefix &rest args)
4de90d10 356 (expand-type-definitions prefix args))
e77e7713 357
358
359