;; 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.37 2006-02-02 17:56:09 espen Exp $
+;; $Id: gtype.lisp,v 1.64 2007-06-25 21:31:09 espen Exp $
(in-package "GLIB")
(defbinding type-init () nil)
(type-init)
-(deftype type-number () '(unsigned 32))
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defbinding (bitsize-of-gtype "bitsize_of_gtype") () unsigned-int))
+
+(deftype type-number () `(unsigned-byte ,(bitsize-of-gtype)))
(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))
(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* ())
(defvar *registered-type-aliases* ())
+(defvar *registered-static-types* ())
(defvar *lisp-type-to-type-number* (make-hash-table))
(defvar *type-number-to-lisp-type* (make-hash-table))
((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?
(defun type-number-of (object)
(find-type-number (type-of object) t))
+;; For #+(SBCL WIN32):
+;; The first 2 lines of the output from "pexports" are:
+;; LIBRARY XXX.dll
+;; EXPORTS
+;; We don't do anything to skip these 2 lines because they won't pass the
+;; WHEN (AND ...) in the LOOP
+;; - cph 19-May-2007
+
(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 ((outname (tmpname "types")))
(unwind-protect
- (loop
- as symbol = (let ((line (read-line (process-output process) nil)))
- (when line (subseq line 11)))
- while symbol
- when (and
- (> (length symbol) 9)
- (or
- (not prefixes)
- (some #'(lambda (prefix)
- (and
- (> (length symbol) (length prefix))
- (string= prefix symbol :end2 (length prefix))))
- (mklist prefixes)))
- (string= "_get_type" symbol :start2 (- (length symbol) 9))
- (not (member symbol ignore :test #'string=)))
- collect symbol)
- (process-close process)))))
-
-
-(defmacro init-types-in-library (filename &key prefix ignore)
- (let ((names (%find-types-in-library filename prefix ignore)))
+ (let ((asdf::*verbose-out* nil))
+ #-win32
+ (asdf:run-shell-command "nm ~A ~A > ~A"
+ #-darwin "--defined-only --dynamic --extern-only"
+ #+darwin "-f -s __TEXT __text"
+ (namestring (truename pathname)) outname)
+ ;; Note about win32 port:
+ ;; 1. (TRUENAME PATHNAME) will bomb.
+ ;; 2. either
+ ;; pexports "d:\\whatever\\bin\\zlib1.dll"
+ ;; or
+ ;; pexports d:/whatever/bin/zlib1.dll
+ ;; anything else will bomb. this is why ~S is used below.
+ #+win32
+ (asdf:run-shell-command "pexports ~S > ~A"
+ (namestring pathname) outname)
+
+ (with-open-file (output outname)
+ (loop
+ as line = (read-line output nil)
+ as symbol = (when line
+ #-win32
+ (let ((pos (position #\space line :from-end t)))
+ #-darwin(subseq line (1+ pos))
+ #+darwin
+ (when (char= (char line (1- pos)) #\T)
+ (subseq line (+ pos 2))))
+ #+win32
+ (subseq line 0 (1- (length line))))
+ while line
+ when (and
+ symbol (> (length symbol) 9)
+ (not (char= (char symbol 0) #\_))
+ (or
+ (not prefixes)
+ (some #'(lambda (prefix)
+ (and
+ (> (length symbol) (length prefix))
+ (string= prefix symbol :end2 (length prefix))))
+ (mklist prefixes)))
+ (string= "_get_type" symbol :start2 (- (length symbol) 9))
+ (not (member symbol ignore :test #'string=)))
+ collect symbol)))
+ (delete-file outname)))))
+
+
+(defmacro init-types-in-library (system library &key prefix ignore)
+ (let* ((filename (asdf:component-pathname (asdf:find-component (asdf:find-system system) library)))
+ (names (%find-types-in-library filename prefix ignore)))
`(progn
,@(mapcar #'(lambda (name)
`(progn
parent-number
(or foreign-name (default-alien-type-name type))
(make-instance 'type-info :class-size class-size :instance-size instance-size))))
- (setf (gethash type *lisp-type-to-type-number*) type-number)
- (setf (gethash type-number *type-number-to-lisp-type*) type)
- type-number))))
+ (pushnew (list type parent foreign-name) *registered-static-types* :key #'car)
+ (setf (gethash type *lisp-type-to-type-number*) type-number)
+ (setf (gethash type-number *type-number-to-lisp-type*) type)
+ type-number))))
((gtype :initarg :gtype :initform nil :reader ginstance-class-gtype))))
+(defun update-size (class)
+ (let ((type-number (find-type-number class)))
+ (cond
+ ((not (foreign-size-p class))
+ (setf (foreign-size class) (type-instance-size type-number)))
+ ((and
+ (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))
+ (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 names initargs))
(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)
- (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)))))
- (unless (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))
-
- (unless (slot-boundp class 'size)
- (setf (slot-value class 'size) (type-instance-size type-number)))))
+ (when (class-finalized-p class)
+ (update-size class)))
(defmethod validate-superclass ((class ginstance-class) (super standard-class))
;;;; Superclass for wrapping types in the glib type system
(eval-when (:compile-toplevel :load-toplevel :execute)
- (defclass ginstance (proxy)
- ((class :allocation :alien :type pointer))
- (:metaclass proxy-class)))
+ (defclass ginstance (ref-counted-object)
+ (;(class :allocation :alien :type pointer :offset 0)
+ )
+ (: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 ensure-proxy-instance ((class ginstance-class) location)
+(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 should not alter "the
+ ;; ordered set of applicable methods" as specified in the
+ ;; Hyperspec
(if class
- (make-instance class :location (reference-foreign class location))
- (error "Object at ~A has an unkown type number: ~A"
- location (%type-number-of-ginstance location)))))
-
-(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."))
-
-(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))
- (ensure-proxy-instance class (sap-ref-sap location offset))))
+ (apply #'call-next-method class location initargs)
+ (error "Object at ~A has an unkown type number: ~A"
+ location (%type-number-of-ginstance location)))))
;;;; 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 selves.
+(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
-(init-types-in-library #.(concatenate 'string (pkg-config:pkg-variable "glib-2.0" "libdir") "/libgobject-2.0.so"))
+(init-types-in-library glib "libgobject-2.0")