3 (defvar *registered-types
* (make-hash-table :test
'equal
))
5 (defstruct object-type name class parent interfaces properties
)
7 (defun instance-init (instance class
)
8 (log-for :subclass
"(instance-init ~A ~A)~%" instance class
)
9 (log-for :subclass
"Initializing instance ~A for type ~A (creating ~A)~%" instance
(g-type-name (foreign-slot-value class
'g-type-class
:type
)) *current-creating-object
*)
10 (unless (or *current-creating-object
*
11 *currently-making-object-p
*
12 (gethash (pointer-address instance
) *foreign-gobjects-strong
*)
13 (gethash (pointer-address instance
) *foreign-gobjects-weak
*))
14 (log-for :subclass
"Proceeding with initialization...~%")
15 (let* ((g-type (foreign-slot-value class
'g-type-class
:type
))
16 (type-name (g-type-name g-type
))
17 (lisp-type-info (gethash type-name
*registered-types
*))
18 (lisp-class (object-type-class lisp-type-info
)))
19 (make-instance lisp-class
:pointer instance
))))
21 (defcallback c-instance-init
:void
((instance :pointer
) (class :pointer
))
22 (instance-init instance class
))
24 (defcallback c-class-init
:void
((class :pointer
) (data :pointer
))
25 (class-init class data
))
27 (defun minimum-foreign-integer (type &optional
(signed t
))
29 (- (ash 1 (1- (* 8 (foreign-type-size type
)))))
32 (defun maximum-foreign-integer (type &optional
(signed t
))
34 (1- (ash 1 (1- (* 8 (foreign-type-size type
)))))
35 (1- (ash 1 (* 8 (foreign-type-size type
))))))
37 (defun property->param-spec
(property)
38 (destructuring-bind (property-name property-type accessor property-get-fn property-set-fn
) property
39 (declare (ignore accessor
))
40 (let ((property-g-type (ensure-g-type property-type
))
41 (flags (append (when property-get-fn
(list :readable
))
42 (when property-set-fn
(list :writable
)))))
43 (ev-case (g-type-fundamental property-g-type
)
44 (+g-type-invalid
+ (error "GValue is of invalid type ~A (~A)" property-g-type
(g-type-name property-g-type
)))
46 (+g-type-char
+ (g-param-spec-char property-name property-name property-name
(minimum-foreign-integer :char
) (maximum-foreign-integer :char
) 0 flags
))
47 (+g-type-uchar
+ (g-param-spec-uchar property-name property-name property-name
(minimum-foreign-integer :uchar nil
) (maximum-foreign-integer :uchar nil
) 0 flags
))
48 (+g-type-boolean
+ (g-param-spec-boolean property-name property-name property-name nil flags
))
49 (+g-type-int
+ (g-param-spec-int property-name property-name property-name
(minimum-foreign-integer :int
) (maximum-foreign-integer :int
) 0 flags
))
50 (+g-type-uint
+ (g-param-spec-uint property-name property-name property-name
(minimum-foreign-integer :uint nil
) (maximum-foreign-integer :uint nil
) 0 flags
))
51 (+g-type-long
+ (g-param-spec-long property-name property-name property-name
(minimum-foreign-integer :long
) (maximum-foreign-integer :long
) 0 flags
))
52 (+g-type-ulong
+ (g-param-spec-ulong property-name property-name property-name
(minimum-foreign-integer :ulong nil
) (maximum-foreign-integer :ulong nil
) 0 flags
))
53 (+g-type-int64
+ (g-param-spec-int64 property-name property-name property-name
(minimum-foreign-integer :int64
) (maximum-foreign-integer :int64
) 0 flags
))
54 (+g-type-uint64
+ (g-param-spec-uint64 property-name property-name property-name
(minimum-foreign-integer :uint64 nil
) (maximum-foreign-integer :uint64 t
) 0 flags
))
55 (+g-type-enum
+ (g-param-spec-enum property-name property-name property-name property-g-type
(enum-item-value (first (get-enum-items property-g-type
))) flags
))
56 (+g-type-flags
+ (g-param-spec-enum property-name property-name property-name property-g-type
(flags-item-value (first (get-flags-items property-g-type
))) flags
))
57 (+g-type-float
+ (g-param-spec-float property-name property-name property-name most-negative-single-float most-positive-single-float
0.0 flags
))
58 (+g-type-double
+ (g-param-spec-double property-name property-name property-name most-negative-double-float most-positive-double-float
0.0d0 flags
))
59 (+g-type-string
+ (g-param-spec-string property-name property-name property-name
"" flags
))
60 (+g-type-pointer
+ (g-param-spec-pointer property-name property-name property-name flags
))
61 (+g-type-boxed
+ (g-param-spec-boxed property-name property-name property-name property-g-type flags
))
62 ;(+g-type-param+ (parse-g-value-param gvalue))
63 (+g-type-object
+ (g-param-spec-object property-name property-name property-name property-g-type flags
))
64 ;(+g-type-interface+ )
65 (t (error "Unknown type: ~A (~A)" property-g-type
(g-type-name property-g-type
)))))))
67 (defun install-properties (class)
68 (let* ((name (g-type-name (foreign-slot-value class
'g-type-class
:type
)))
69 (lisp-type-info (gethash name
*registered-types
*)))
70 (iter (for property in
(object-type-properties lisp-type-info
))
71 (for param-spec
= (property->param-spec property
))
72 (for property-id from
123)
73 (log-for :subclass
"installing property ~A~%" property
)
74 (g-object-class-install-property class property-id param-spec
))))
76 (defun vtable-item->cstruct-item
(item)
77 (if (eq :skip
(first item
))
79 (list (first item
) :pointer
)))
81 (defstruct vtable-method-info name return-type args callback-name
)
83 (defmethod make-load-form ((object vtable-method-info
) &optional environment
)
84 (declare (ignore environment
))
85 `(make-vtable-method-info :name
',(vtable-method-info-name object
)
86 :return-type
',(vtable-method-info-return-type object
)
87 :args
',(vtable-method-info-args object
)
88 :callback-name
',(vtable-method-info-callback-name object
)))
90 (defun vtable-methods (items)
91 (iter (for item in items
)
92 (when (eq :skip
(first item
)) (next-iteration))
93 (destructuring-bind (name callback-name return-type
&rest args
) item
94 (collect (make-vtable-method-info :name name
:return-type return-type
:args args
:callback-name callback-name
)))))
96 (defvar *vtables
* (make-hash-table :test
'equal
))
98 (defstruct vtable-description type-name cstruct-name methods
)
100 (defmacro define-vtable
((type-name cstruct-name
) &body items
)
102 (defcstruct ,cstruct-name
,@(mapcar #'vtable-item-
>cstruct-item items
))
103 (setf (gethash ,type-name
*vtables
*)
104 (make-vtable-description :type-name
,type-name
:cstruct-name
',cstruct-name
:methods
(list ,@(mapcar #'make-load-form
(vtable-methods items
)))))
105 ,@(iter (for method in
(vtable-methods items
))
106 (collect `(defgeneric ,(vtable-method-info-name method
) (,@(mapcar #'first
(vtable-method-info-args method
)))))
107 (collect `(glib-defcallback ,(vtable-method-info-callback-name method
) ,(vtable-method-info-return-type method
)
108 (,@(vtable-method-info-args method
))
110 (,(vtable-method-info-name method
) ,@(mapcar #'first
(vtable-method-info-args method
)))
111 (return-from-interface-method-implementation (v) :interactive
(lambda () (list (eval (read)))) v
)))))))
113 (defun interface-init (iface data
)
114 (destructuring-bind (class-name interface-name
) (prog1 (get-stable-pointer-value data
) (free-stable-pointer data
))
115 (declare (ignorable class-name
))
116 (let* ((vtable (gethash interface-name
*vtables
*))
117 (vtable-cstruct (vtable-description-cstruct-name vtable
)))
118 (log-for :subclass
"interface-init for class ~A and interface ~A~%" class-name interface-name
)
119 (iter (for method in
(vtable-description-methods vtable
))
120 (setf (foreign-slot-value iface vtable-cstruct
(vtable-method-info-name method
)) (get-callback (vtable-method-info-callback-name method
)))))))
122 (defcallback c-interface-init
:void
((iface :pointer
) (data :pointer
))
123 (interface-init iface data
))
125 (defun add-interface (name interface
)
126 (let* ((interface-info (list name interface
))
127 (interface-info-ptr (allocate-stable-pointer interface-info
)))
128 (with-foreign-object (info 'g-interface-info
)
129 (setf (foreign-slot-value info
'g-interface-info
:interface-init
) (callback c-interface-init
)
130 (foreign-slot-value info
'g-interface-info
:interface-data
) interface-info-ptr
)
131 (g-type-add-interface-static (g-type-from-name name
) (ensure-g-type interface
) info
))))
133 (defun add-interfaces (name)
134 (let* ((lisp-type-info (gethash name
*registered-types
*))
135 (interfaces (object-type-interfaces lisp-type-info
)))
136 (iter (for interface in interfaces
)
137 (add-interface name interface
))))
139 (defun class-init (class data
)
140 (declare (ignore data
))
141 (log-for :subclass
"class-init for ~A~%" (g-type-name (g-type-from-class class
)))
142 (setf (foreign-slot-value class
'g-object-class
:get-property
)
143 (callback c-object-property-get
)
144 (foreign-slot-value class
'g-object-class
:set-property
)
145 (callback c-object-property-set
))
147 (install-properties class
))
149 (defun object-property-get (object property-id g-value pspec
)
150 (declare (ignore property-id
))
151 (let* ((lisp-object (or (gethash (pointer-address object
) *foreign-gobjects-strong
*)
152 (gethash (pointer-address object
) *foreign-gobjects-weak
*)))
153 (property-name (foreign-slot-value pspec
'g-param-spec
:name
))
154 (property-type (foreign-slot-value pspec
'g-param-spec
:value-type
))
155 (type-name (g-type-name (foreign-slot-value pspec
'g-param-spec
:owner-type
)))
156 (lisp-type-info (gethash type-name
*registered-types
*))
157 (property-info (find property-name
(object-type-properties lisp-type-info
) :test
'string
= :key
'first
))
158 (property-get-fn (fourth property-info
)))
159 (log-for :subclass
"get(~A,'~A')~%" lisp-object property-name
)
160 (let ((value (restart-case
161 (funcall property-get-fn lisp-object
)
162 (return-from-property-getter (value) :interactive
(lambda () (format t
"Enter new value: ") (list (eval (read)))) value
))))
163 (set-g-value g-value value property-type
))))
165 (defcallback c-object-property-get
:void
((object :pointer
) (property-id :uint
) (value :pointer
) (pspec :pointer
))
166 (object-property-get object property-id value pspec
))
168 (defun object-property-set (object property-id value pspec
)
169 (declare (ignore property-id
))
170 (let* ((lisp-object (or (gethash (pointer-address object
) *foreign-gobjects-strong
*)
171 (gethash (pointer-address object
) *foreign-gobjects-weak
*)))
172 (property-name (foreign-slot-value pspec
'g-param-spec
:name
))
173 (type-name (g-type-name (foreign-slot-value pspec
'g-param-spec
:owner-type
)))
174 (lisp-type-info (gethash type-name
*registered-types
*))
175 (property-info (find property-name
(object-type-properties lisp-type-info
) :test
'string
= :key
'first
))
176 (property-set-fn (fifth property-info
))
177 (new-value (parse-g-value value
)))
178 (log-for :subclass
"set(~A,'~A',~A)~%" lisp-object property-name new-value
)
180 (funcall property-set-fn new-value lisp-object
)
181 (return-without-error-from-property-setter () nil
))))
183 (defcallback c-object-property-set
:void
((object :pointer
) (property-id :uint
) (value :pointer
) (pspec :pointer
))
184 (object-property-set object property-id value pspec
))
186 (defmacro register-object-type-implementation
(name class parent interfaces properties
)
187 (unless (stringp parent
)
188 (setf parent
(g-type-name (ensure-g-type parent
))))
190 (setf (gethash ,name
*registered-types
*) (make-object-type :name
,name
:class
',class
:parent
,parent
:interfaces
',interfaces
:properties
',properties
))
192 (log-for :subclass
"Registering GObject type implementation ~A for type ~A~%" ',class
,name
)
193 (with-foreign-object (query 'g-type-query
)
194 (g-type-query (g-type-from-name ,parent
) query
)
195 (g-type-register-static-simple (g-type-from-name ,parent
)
197 (foreign-slot-value query
'g-type-query
:class-size
)
198 (callback c-class-init
)
199 (foreign-slot-value query
'g-type-query
:instance-size
)
200 (callback c-instance-init
) nil
))
201 (add-interfaces ,name
))
202 (defmethod initialize-instance :before
((object ,class
) &key pointer
)
203 (log-for :subclass
"(initialize-instance ~A :pointer ~A) :before~%" object pointer
)
204 (unless (or pointer
(and (slot-boundp object
'gobject
::pointer
)
205 (gobject::pointer object
)))
206 (log-for :subclass
"calling g-object-constructor~%")
207 (setf (gobject::pointer object
) (gobject::g-object-call-constructor
,name nil nil
)
208 (gobject::g-object-has-reference object
) t
)))
210 ,@(iter (for (prop-name prop-type prop-accessor prop-reader prop-writer
) in properties
)
211 (declare (ignorable prop-type
))
213 (collect `(defun ,prop-accessor
(object) (g-object-call-get-property object
,prop-name
))))
215 (collect `(defun (setf ,prop-accessor
) (new-value object
) (g-object-call-set-property object
,prop-name new-value
))))))