;; Common Lisp bindings for GTK+ v2.x
-;; Copyright 2000-2005 Espen S. Johnsen <espen@users.sf.net>
+;; Copyright 2000-2006 Espen S. Johnsen <espen@users.sf.net>
;;
;; Permission is hereby granted, free of charge, to any person obtaining
;; a copy of this software and associated documentation files (the
;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-;; $Id: gtype.lisp,v 1.46 2006/02/19 22:25:31 espen Exp $
+;; $Id: gtype.lisp,v 1.55 2006/08/25 10:37:33 espen Exp $
(in-package "GLIB")
(defbinding type-init () nil)
(type-init)
-(deftype type-number () '(unsigned 32))
+(deftype type-number () 'unsigned-long)
(deftype gtype () 'symbol)
-(defmethod alien-type ((type (eql 'gtype)) &rest args)
- (declare (ignore type args))
+(define-type-method alien-type ((type gtype))
+ (declare (ignore type))
(alien-type 'type-number))
-(defmethod size-of ((type (eql 'gtype)) &rest args)
- (declare (ignore type args))
+(define-type-method size-of ((type gtype) &key (inlined t))
+ (assert-inlined type inlined)
(size-of 'type-number))
-(defmethod to-alien-form (gtype (type (eql 'gtype)) &rest args)
- (declare (ignore type args))
+(define-type-method to-alien-form ((type gtype) gtype &optional copy-p)
+ (declare (ignore type copy-p))
`(find-type-number ,gtype t))
-(defmethod to-alien-function ((type (eql 'gtype)) &rest args)
- (declare (ignore type args))
+(define-type-method to-alien-function ((type gtype) &optional copy-p)
+ (declare (ignore type copy-p))
#'(lambda (gtype)
(find-type-number gtype t)))
-(defmethod from-alien-form (type-number (type (eql 'gtype)) &rest args)
- (declare (ignore type args))
- `(type-from-number ,type-number))
+(define-type-method from-alien-form ((type gtype) form &key ref)
+ (declare (ignore type ref))
+ `(type-from-number ,form))
-(defmethod from-alien-function ((type (eql 'gtype)) &rest args)
- (declare (ignore type args))
+(define-type-method from-alien-function ((type gtype) &key ref)
+ (declare (ignore type ref))
#'(lambda (type-number)
(type-from-number type-number)))
-(defmethod writer-function ((type (eql 'gtype)) &rest args)
- (declare (ignore type args))
+(define-type-method writer-function ((type gtype) &key temp (inlined t))
+ (declare (ignore temp))
+ (assert-inlined type inlined)
(let ((writer (writer-function 'type-number)))
#'(lambda (gtype location &optional (offset 0))
(funcall writer (find-type-number gtype t) location offset))))
-(defmethod reader-function ((type (eql 'gtype)) &rest args)
- (declare (ignore type args))
+(define-type-method reader-function ((type gtype) &key ref (inlined t))
+ (declare (ignore ref))
+ (assert-inlined type inlined)
(let ((reader (reader-function 'type-number)))
- #'(lambda (location &optional (offset 0) weak-p)
- (declare (ignore weak-p))
+ #'(lambda (location &optional (offset 0))
(type-from-number (funcall reader location offset)))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defclass type-query (struct)
((type-number :allocation :alien :type type-number)
- (name :allocation :alien :type string)
+ (name :allocation :alien :type (copy-of string))
(class-size :allocation :alien :type unsigned-int)
(instance-size :allocation :alien :type unsigned-int))
(:metaclass struct-class)))
(defbinding type-query (type) nil
((find-type-number type t) type-number)
- ((make-instance 'type-query) type-query :return))
+ ((make-instance 'type-query) type-query :in/return))
(defun type-instance-size (type)
(slot-value (type-query type) 'instance-size))
(defbinding type-class-ref (type) pointer
((find-type-number type t) type-number))
-(defbinding type-class-unref (type) nil
- ((find-type-number type t) type-number))
+(defbinding type-class-unref () nil
+ (class pointer))
(defbinding type-class-peek (type) pointer
((find-type-number type t) type-number))
+
;;;; Mapping between lisp types and glib types
(defvar *registered-types* ())
((not (zerop type-number)) type-number)
(error-p (error "Invalid gtype name: ~A" name)))))
+(defun type-from-glib-name (name)
+ (type-from-number (type-number-from-glib-name name) t))
+
(defun register-type (type id)
- (pushnew (cons type id) *registered-types* :key #'car)
- (let ((type-number
- (typecase id
- (string (type-number-from-glib-name id))
- (symbol (funcall id)))))
- (setf (gethash type *lisp-type-to-type-number*) type-number)
- (setf (gethash type-number *type-number-to-lisp-type*) type)
- type-number))
+ (cond
+ ((find-type-number type))
+ ((not id) (warn "Can't register type with no foreign id: ~A" type))
+ (t
+ (pushnew (cons type id) *registered-types* :key #'car)
+ (let ((type-number
+ (typecase id
+ (string (type-number-from-glib-name id))
+ (symbol (funcall id)))))
+ (setf (gethash type *lisp-type-to-type-number*) type-number)
+ (setf (gethash type-number *type-number-to-lisp-type*) type)
+ type-number))))
(defun register-type-alias (type alias)
(pushnew (cons type alias) *registered-type-aliases* :key #'car)
(mapc #'(lambda (type)
(register-type (car type) (cdr type)))
*registered-types*)
+ (mapc #'(lambda (type)
+ (apply #'register-new-type type))
+ (reverse *registered-static-types*))
(mapc #'(lambda (type)
(register-type-alias (car type) (cdr type)))
*registered-type-aliases*))
(pushnew 'reinitialize-all-types
#+cmu *after-save-initializations*
- #+sbcl *init-hooks*)
+ #+sbcl *init-hooks*
+ #+clisp custom:*init-hooks*)
#+cmu
(pushnew 'system::reinitialize-global-table ; we shouldn't have to do this?
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *type-initializers* ())
(defun %find-types-in-library (pathname prefixes ignore)
- (let ((process (run-program
- "/usr/bin/nm" (list "--defined-only" "-D" (namestring (truename pathname)))
- :output :stream :wait nil)))
+ (let ((process
+ (run-program
+ "/usr/bin/nm"
+ #+clisp :arguments
+ (list "--defined-only" "-D" (namestring (truename pathname)))
+ :output :stream :wait nil)))
(unwind-protect
(loop
- as symbol = (let ((line (read-line (process-output process) nil)))
- (when line (subseq line 11)))
+ as symbol = (let ((line (read-line
+ #+(or cmu sbcl)
+ (process-output process)
+ #+clisp process
+ nil)))
+ (when line
+ (subseq line (1+ (position #\Space line :from-end t)))))
while symbol
when (and
(> (length symbol) 9)
(string= "_get_type" symbol :start2 (- (length symbol) 9))
(not (member symbol ignore :test #'string=)))
collect symbol)
- (process-close process)))))
+ (#+(or cmu sbcl)process-close
+ #+clisp close
+ process)))))
(defmacro init-types-in-library (filename &key prefix ignore)
(defun update-size (class)
(let ((type-number (find-type-number class)))
(cond
- ((not (slot-boundp class 'size))
- (setf (slot-value class 'size) (type-instance-size type-number)))
+ ((not (foreign-size-p class))
+ (setf (foreign-size class) (type-instance-size type-number)))
((and
- (slot-boundp class 'size)
- (not (= (type-instance-size type-number) (slot-value class 'size))))
+ (foreign-size-p class)
+ (not (= (type-instance-size type-number) (foreign-size class))))
(warn "Size mismatch for class ~A" class)))))
(defmethod finalize-inheritance ((class ginstance-class))
- (call-next-method)
- (let* ((class-name (class-name class))
- (super (most-specific-proxy-superclass class))
- (gtype (or
- (first (ginstance-class-gtype class))
- (default-alien-type-name class-name)))
- (type-number
- (or
- (find-type-number class-name)
- (let ((type-number
- (if (or
- (symbolp gtype)
- (type-number-from-glib-name gtype nil))
- (register-type class-name gtype)
- (register-new-type class-name (class-name super) gtype))))
- (type-class-ref type-number)
- type-number))))
- (when (and
- (supertype type-number)
- (not (eq (class-name super) (supertype type-number))))
- (warn "~A is the super type for ~A in the gobject type system."
- (supertype type-number) class-name)))
-
- (update-size class))
+ (prog1
+ #+clisp(call-next-method)
+ (let* ((class-name (class-name class))
+ (super (most-specific-proxy-superclass class))
+ (gtype (or
+ (first (ginstance-class-gtype class))
+ (default-alien-type-name class-name)))
+ (type-number
+ (or
+ (find-type-number class-name)
+ (let ((type-number
+ (if (or
+ (symbolp gtype)
+ (type-number-from-glib-name gtype nil))
+ (register-type class-name gtype)
+ (register-new-type class-name (class-name super) gtype))))
+ (type-class-ref type-number)
+ type-number))))
+ #+nil
+ (when (and
+ (supertype type-number)
+ (not (eq (class-name super) (supertype type-number))))
+ (warn "Super class mismatch between CLOS and GObject for ~A"
+ class-name)))
+ (update-size class))
+ #-clisp(call-next-method))
(defmethod shared-initialize ((class ginstance-class) names &rest initargs)
- (declare (ignore initargs))
+ (declare (ignore names initargs))
(call-next-method)
(when (class-finalized-p class)
(update-size class)))
(:metaclass proxy-class)
(:size #.(size-of 'pointer))))
+(defun ref-type-number (location &optional offset)
+ (declare (ignore location offset)))
+
+(setf (symbol-function 'ref-type-number) (reader-function 'type-number))
+
(defun %type-number-of-ginstance (location)
- (let ((class (sap-ref-sap location 0)))
- (sap-ref-32 class 0)))
+ (let ((class (ref-pointer location)))
+ (ref-type-number class)))
-(defmethod make-proxy-instance :around ((class ginstance-class) location &rest initargs)
+(defmethod make-proxy-instance :around ((class ginstance-class) location
+ &rest initargs)
(declare (ignore class))
(let ((class (labels ((find-known-class (type-number)
(or
(unless (zerop type-number)
(find-known-class (type-parent type-number))))))
(find-known-class (%type-number-of-ginstance location)))))
- ;; Note that chancing the class argument must not alter "the
+ ;; Note that chancing the class argument should not alter "the
;; ordered set of applicable methods" as specified in the
;; Hyperspec
(if class
(error "Object at ~A has an unkown type number: ~A"
location (%type-number-of-ginstance location)))))
-(defmethod make-proxy-instance ((class ginstance-class) location &rest initargs)
- (declare (ignore initargs))
- (reference-foreign class location)
- ;; Since we make an explicit reference to the foreign object, we
- ;; always have to release it when the proxy is garbage collected
- ;; and therefor ignore the weak-p argument.
- (call-next-method class location :weak nil))
-
-(defmethod invalidate-instance ((instance ginstance))
- (declare (ignore instance))
- ;; A ginstance should never be invalidated since it is ref counted
- nil)
-
-(defmethod callback-from-alien-form (form (class ginstance-class) &rest args)
- (declare (ignore args))
- (from-alien-form form class))
-
-(defmethod copy-from-alien-form (location (class ginstance-class) &rest args)
- (declare (ignore location class args))
- (error "Doing copy-from-alien on a ref. counted class is most certainly an error, but if it really is what you want you should use REFERENCE-FOREIGN on the returned instance instead."))
+(define-type-method from-alien-form ((type ginstance) form &key (ref :copy))
+ (call-next-method type form :ref ref))
-(defmethod copy-from-alien-function ((class ginstance-class) &rest args)
- (declare (ignore class args))
- (error "Doing copy-from-alien on a ref. counted class is most certainly an error, but if it really is what you want you should use REFERENCE-FOREIGN on the returned instance instead."))
-
-(defmethod reader-function ((class ginstance-class) &rest args)
- (declare (ignore args))
- #'(lambda (location &optional (offset 0) weak-p)
- (declare (ignore weak-p))
- (ensure-proxy-instance class (sap-ref-sap location offset))))
+(define-type-method from-alien-function ((type ginstance) &key (ref :copy))
+ (call-next-method type :ref ref))
;;;; Registering fundamental types
(let ((expander (first (find-type-info type))))
(funcall expander (find-type-number type t) forward-p options)))
+
(defbinding type-parent (type) type-number
((find-type-number type t) type-number))
*derivable-type-info*)
type-list))
-(defun find-type-dependencies (type)
- (let ((list-dependencies (second (find-type-info type))))
- (when list-dependencies
- (funcall list-dependencies (find-type-number type t)))))
-
-(defun %sort-types-topologicaly (types)
- (let ((partial-sorted
- (sort
- (mapcar
- #'(lambda (type)
- (cons type (remove-if #'(lambda (dep)
- (not (find dep types)))
- (find-type-dependencies type))))
- types)
- #'(lambda (type1 type2) (type-is-p type2 type1)) :key #'car))
- (sorted ()))
-
- (loop
- as tmp = partial-sorted then (or (rest tmp) partial-sorted)
- while tmp
- do (destructuring-bind (type . dependencies) (first tmp)
- (cond
- ((every #'(lambda (dep)
- (assoc dep sorted))
- dependencies)
- (push (cons type nil) sorted) ; no forward definition needed
- (setq partial-sorted (delete type partial-sorted :key #'first)))
- ((some #'(lambda (dep)
- (find type (find-type-dependencies dep)))
- dependencies)
- (push (cons type t) sorted) ; forward definition needed
- (setq partial-sorted (delete type partial-sorted :key #'first))))))
- (nreverse sorted)))
+(defun find-type-dependencies (type &optional options)
+ (let ((find-dependencies (second (find-type-info type))))
+ (when find-dependencies
+ (remove-duplicates
+ (mapcar #'find-type-number
+ (funcall find-dependencies (find-type-number type t) options))))))
+
+
+;; The argument is a list where each elements is on the form
+;; (type . dependencies). This function will not handle indirect
+;; dependencies and types depending on them selve.
+(defun sort-types-topologicaly (unsorted)
+ (flet ((depend-p (type1)
+ (find-if #'(lambda (type2)
+ (and
+ ;; If a type depends a subtype it has to be
+ ;; forward defined
+ (not (type-is-p (car type2) (car type1)))
+ (find (car type2) (cdr type1))))
+ unsorted)))
+ (let ((sorted
+ (loop
+ while unsorted
+ nconc (multiple-value-bind (sorted remaining)
+ (delete-collect-if
+ #'(lambda (type)
+ (or (not (cdr type)) (not (depend-p type))))
+ unsorted)
+ (cond
+ ((not sorted)
+ ;; We have a circular dependency which have to
+ ;; be resolved
+ (let ((selected
+ (find-if
+ #'(lambda (type)
+ (every
+ #'(lambda (dep)
+ (or
+ (not (type-is-p (car type) dep))
+ (not (find dep unsorted :key #'car))))
+ (cdr type)))
+ unsorted)))
+ (unless selected
+ (error "Couldn't resolve circular dependency"))
+ (setq unsorted (delete selected unsorted))
+ (list selected)))
+ (t
+ (setq unsorted remaining)
+ sorted))))))
+
+ ;; Mark types which have to be forward defined
+ (loop
+ for tmp on sorted
+ as (type . dependencies) = (first tmp)
+ collect (cons type (and
+ dependencies
+ (find-if #'(lambda (type)
+ (find (car type) dependencies))
+ (rest tmp))
+ t))))))
(defun expand-type-definitions (prefix &optional args)
(getf (type-options type-number) :type (default-type-name name))
(register-type-as type-number))))
- (let ((sorted-type-list (%sort-types-topologicaly type-list)))
+ ;; This is needed for some unknown reason to get type numbers right
+ (mapc #'find-type-dependencies type-list)
+
+ (let ((sorted-type-list
+ #+clisp (mapcar #'list type-list)
+ #-clisp
+ (sort-types-topologicaly
+ (mapcar
+ #'(lambda (type)
+ (cons type (find-type-dependencies type (type-options type))))
+ type-list))))
`(progn
,@(mapcar
#'(lambda (pair)
(defmacro define-types-by-introspection (prefix &rest args)
(expand-type-definitions prefix args))
+(defexport define-types-by-introspection (prefix &rest args)
+ (list-autoexported-symbols (expand-type-definitions prefix args)))
+
;;;; Initialize all non static types in GObject