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