Added with-gvalue macro
[clg] / glib / gtype.lisp
CommitLineData
0d07716f 1;; Common Lisp bindings for GTK+ v2.0
3a935dfa 2;; Copyright (C) 2000-2001 Espen S. Johnsen <esj@stud.cs.uit.no>
0d07716f 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
4d1d3921 18;; $Id: gtype.lisp,v 1.19 2004/11/07 01:21:04 espen Exp $
0d07716f 19
20(in-package "GLIB")
21
22(use-prefix "g")
23
d1266407 24;; Initialize the glib type system
25(defbinding type-init () nil)
26(type-init)
0d07716f 27
28(deftype type-number () '(unsigned 32))
29
fc47a022 30(eval-when (:compile-toplevel :load-toplevel :execute)
3a935dfa 31 (defclass type-query (struct)
fc47a022 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))
4d1d3921 36 (:metaclass struct-class)))
fc47a022 37
38
fc47a022 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)))
3a935dfa 45 (%type-query (find-type-number type t) query)
fc47a022 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))
0d07716f 53
3a935dfa 54(defbinding type-class-ref (type) pointer
55 ((find-type-number type t) type-number))
0d07716f 56
3a935dfa 57(defbinding type-class-unref (type) nil
58 ((find-type-number type t) type-number))
fc47a022 59
3a935dfa 60(defbinding type-class-peek (type) pointer
61 ((find-type-number type t) type-number))
fc47a022 62
0d07716f 63
3a935dfa 64;;;; Mapping between lisp types and glib types
0d07716f 65
66(defvar *type-to-number-hash* (make-hash-table))
67(defvar *number-to-type-hash* (make-hash-table))
68
3a935dfa 69(defun register-type (type id)
70 (let ((type-number
71 (etypecase id
72 (integer id)
935a783c 73 (string (find-type-number id t))
74 (symbol (gethash id *type-to-number-hash*)))))
3a935dfa 75 (setf (gethash type *type-to-number-hash*) type-number)
935a783c 76 (unless (symbolp id)
77 (setf (gethash type-number *number-to-type-hash*) type))
3a935dfa 78 type-number))
79
80(defbinding %type-from-name () type-number
81 (name string))
82
83(defun find-type-number (type &optional error)
0d07716f 84 (etypecase type
85 (integer type)
3a935dfa 86 (string
87 (let ((type-number (%type-from-name type)))
88 (cond
89 ((and (zerop type-number) error)
4d1d3921 90 (error "Invalid gtype name: ~A" type))
3a935dfa 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))))
0d07716f 99
b011356b 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))
0d07716f 109
3a935dfa 110(defun type-from-name (name)
111 (etypecase name
112 (string (type-from-number (find-type-number name t)))))
0d07716f 113
3a935dfa 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
4d1d3921 126(defun %init-types-in-library (pathname prefix ignore)
3a935dfa 127 (let ((process (ext:run-program
27487b3d 128 "nm" (list "-D" (namestring (truename pathname)))
3a935dfa 129 :output :stream :wait nil))
130 (fnames ()))
131 (labels ((read-symbols ()
132 (let ((line (read-line (ext:process-output process) nil)))
133 (when line
b011356b 134 (let ((symbol (subseq line 11)))
135 (when (and
4d1d3921 136 (> (length symbol) (length prefix))
137 (string= prefix symbol :end2 (length prefix))
b011356b 138 (search "_get_type" symbol)
139 (not (member symbol ignore :test #'string=)))
140 (push symbol fnames)))
3a935dfa 141 (read-symbols)))))
142 (read-symbols)
143 (ext:process-close process)
144 `(init-type ',fnames))))
0d07716f 145
4d1d3921 146(defmacro init-types-in-library (filename &key (prefix "") ignore)
147 (%init-types-in-library filename prefix ignore))
b011356b 148
149
0d07716f 150
fc47a022 151;;;; Superclass for wrapping types in the glib type system
0d07716f 152
153(eval-when (:compile-toplevel :load-toplevel :execute)
fc47a022 154 (defclass ginstance (proxy)
3a935dfa 155 ((class :allocation :alien :type pointer))
156 (:metaclass proxy-class)))
0d07716f 157
fc47a022 158(defun %type-of-ginstance (location)
159 (let ((class (sap-ref-sap location 0)))
4d1d3921 160 (type-from-number (sap-ref-32 class 0))))
0d07716f 161
4d1d3921 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)))
0d07716f 169
170
fc47a022 171;;;; Metaclass for subclasses of ginstance
0d07716f 172
173(eval-when (:compile-toplevel :load-toplevel :execute)
935a783c 174 (defclass ginstance-class (proxy-class)
175 ()))
0d07716f 176
177
c9819f3e 178(defmethod shared-initialize ((class ginstance-class) names
4d1d3921 179 &rest initargs &key name alien-name)
180 (declare (ignore names))
fc47a022 181 (let* ((class-name (or name (class-name class)))
182 (type-number
3a935dfa 183 (find-type-number
b011356b 184 (or (first alien-name) (default-alien-type-name class-name)) t)))
3a935dfa 185 (register-type class-name type-number)
935a783c 186 (if (getf initargs :size)
187 (call-next-method)
188 (let ((size (type-instance-size type-number)))
4d1d3921 189 (apply #'call-next-method class names :size (list size) initargs)))))
190
191
192(defmethod validate-superclass ((class ginstance-class) (super standard-class))
c9819f3e 193 (subtypep (class-name super) 'ginstance))
0d07716f 194
195
3a935dfa 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")
b011356b 209(register-type 'string "gchararray")
3a935dfa 210
211
212;;;;
213
4812615b 214(defvar *derivable-type-info* (make-hash-table))
3a935dfa 215
4812615b 216(defun register-derivable-type (type id expander)
3a935dfa 217 (register-type type id)
4812615b 218 (let ((type-number (register-type type id)))
219 (setf (gethash type-number *derivable-type-info*) expander)))
3a935dfa 220
b011356b 221(defun find-type-info (type)
222 (dolist (super (cdr (type-hierarchy type)))
4812615b 223 (let ((info (gethash super *derivable-type-info*)))
b011356b 224 (return-if info))))
225
4812615b 226(defun expand-type-definition (type options)
227 (let ((expander (find-type-info type)))
228 (funcall expander (find-type-number type t) options)))
3a935dfa 229
3a935dfa 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
7858d45e 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
4d1d3921 243 (map-c-vector 'list #'identity array 'type-number length)
7858d45e 244 (deallocate-memory array))))
245
246(defun implements (type)
247 (mapcar #'type-from-number (type-interfaces type)))
248
3a935dfa 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
4d1d3921 266 (map-c-vector
3a935dfa 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))
4812615b 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*)
3a935dfa 287 type-list))
288
289(defun %sort-types-topologicaly (unsorted)
290 (let ((sorted ()))
291 (loop while unsorted do
292 (dolist (type unsorted)
7858d45e 293 (let ((dependencies
294 (append (rest (type-hierarchy type)) (type-interfaces type))))
3a935dfa 295 (cond
296 ((null dependencies)
297 (push type sorted)
298 (setq unsorted (delete type unsorted)))
299 (t
300 (unless (dolist (dep dependencies)
4812615b 301 (when (find type (rest (type-hierarchy dep)))
302 (error "Cyclic type dependencie"))
3a935dfa 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)
4812615b 310 (flet ((type-options (type-number)
3a935dfa 311 (let ((name (find-type-name type-number)))
b011356b 312 (cdr (assoc name args :test #'string=)))))
3a935dfa 313
4812615b 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)
17c607d0 324 (getf (cdr options) :ignore-prefix)
325 (not (some
326 #'(lambda (exception)
327 (string= name exception))
328 (getf (cdr options) :except)))))
4812615b 329 args))))
330 (find-types prefix))))
3a935dfa 331
4812615b 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
3a935dfa 344(defmacro define-types-by-introspection (prefix &rest args)
b011356b 345 (expand-type-definitions prefix args))
4812615b 346
347
348