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
(gtype-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 (gtype-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 (gtype 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 ((gtype +g-type-invalid
+) (error "GValue is of invalid type ~A (~A)" property-g-type
(gtype-name property-g-type
)))
45 ((gtype +g-type-void
+) nil
)
46 ((gtype +g-type-char
+) (g-param-spec-char property-name property-name property-name
(minimum-foreign-integer :char
) (maximum-foreign-integer :char
) 0 flags
))
47 ((gtype +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 ((gtype +g-type-boolean
+) (g-param-spec-boolean property-name property-name property-name nil flags
))
49 ((gtype +g-type-int
+) (g-param-spec-int property-name property-name property-name
(minimum-foreign-integer :int
) (maximum-foreign-integer :int
) 0 flags
))
50 ((gtype +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 ((gtype +g-type-long
+) (g-param-spec-long property-name property-name property-name
(minimum-foreign-integer :long
) (maximum-foreign-integer :long
) 0 flags
))
52 ((gtype +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 ((gtype +g-type-int64
+) (g-param-spec-int64 property-name property-name property-name
(minimum-foreign-integer :int64
) (maximum-foreign-integer :int64
) 0 flags
))
54 ((gtype +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 ((gtype +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 ((gtype +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 ((gtype +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 ((gtype +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 ((gtype +g-type-string
+) (g-param-spec-string property-name property-name property-name
"" flags
))
60 ((gtype +g-type-pointer
+) (g-param-spec-pointer property-name property-name property-name flags
))
61 ((gtype +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 ((gtype +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
(gtype-name property-g-type
)))))))
67 (defun install-properties (class)
68 (let* ((name (gtype-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 slot-name name return-type args callback-name impl-call
)
83 (defmethod make-load-form ((object vtable-method-info
) &optional environment
)
84 (declare (ignore environment
))
85 `(make-vtable-method-info :slot-name
',(vtable-method-info-slot-name object
)
86 :name
',(vtable-method-info-name object
)
87 :return-type
',(vtable-method-info-return-type object
)
88 :args
',(vtable-method-info-args object
)
89 :callback-name
',(vtable-method-info-callback-name object
)))
91 (defun vtable-methods (iface-name items
)
92 (iter (for item in items
)
93 (when (eq :skip
(first item
)) (next-iteration))
94 (destructuring-bind (name (return-type &rest args
) &key impl-call
) item
95 (for method-name
= (intern (format nil
"~A-~A-IMPL" (symbol-name iface-name
) (symbol-name name
))))
96 (for callback-name
= (intern (format nil
"~A-~A-CALLBACK" (symbol-name iface-name
) (symbol-name name
))))
97 (collect (make-vtable-method-info :slot-name name
99 :return-type return-type
101 :callback-name callback-name
102 :impl-call impl-call
)))))
104 (defvar *vtables
* (make-hash-table :test
'equal
))
106 (defstruct vtable-description type-name cstruct-name methods
)
108 (defmacro define-vtable
((type-name name
) &body items
)
109 (let ((cstruct-name (intern (format nil
"~A-VTABLE" (symbol-name name
))))
110 (methods (vtable-methods name items
)))
112 (defcstruct ,cstruct-name
,@(mapcar #'vtable-item-
>cstruct-item items
))
113 (setf (gethash ,type-name
*vtables
*)
114 (make-vtable-description :type-name
,type-name
115 :cstruct-name
',cstruct-name
116 :methods
(list ,@(mapcar #'make-load-form methods
))))
117 ,@(iter (for method in methods
)
119 (if (vtable-method-info-impl-call method
)
120 (first (vtable-method-info-impl-call method
))
121 (mapcar #'first
(vtable-method-info-args method
))))
122 (collect `(defgeneric ,(vtable-method-info-name method
) (,@args
)))
123 (collect `(glib-defcallback ,(vtable-method-info-callback-name method
)
124 ,(vtable-method-info-return-type method
)
125 (,@(vtable-method-info-args method
))
127 ,(if (vtable-method-info-impl-call method
)
128 `(progn ,@(rest (vtable-method-info-impl-call method
)))
129 `(,(vtable-method-info-name method
)
130 ,@(mapcar #'first
(vtable-method-info-args method
))))
131 (return-from-interface-method-implementation (v)
132 :interactive
(lambda () (list (eval (read)))) v
))))))))
134 (defun interface-init (iface data
)
135 (destructuring-bind (class-name interface-name
) (prog1 (get-stable-pointer-value data
) (free-stable-pointer data
))
136 (declare (ignorable class-name
))
137 (let* ((vtable (gethash interface-name
*vtables
*))
138 (vtable-cstruct (vtable-description-cstruct-name vtable
)))
139 (log-for :subclass
"interface-init for class ~A and interface ~A~%" class-name interface-name
)
140 (iter (for method in
(vtable-description-methods vtable
))
141 (for cb
= (get-callback (vtable-method-info-callback-name method
)))
142 (for slot-name
= (vtable-method-info-slot-name method
))
143 (log-for :subclass
"->setting method ~A to ~A~%" method cb
)
144 (setf (foreign-slot-value iface vtable-cstruct slot-name
) cb
)))))
146 (defcallback c-interface-init
:void
((iface :pointer
) (data :pointer
))
147 (interface-init iface data
))
149 (defun add-interface (name interface
)
150 (let* ((interface-info (list name interface
))
151 (interface-info-ptr (allocate-stable-pointer interface-info
)))
152 (with-foreign-object (info 'g-interface-info
)
153 (setf (foreign-slot-value info
'g-interface-info
:interface-init
) (callback c-interface-init
)
154 (foreign-slot-value info
'g-interface-info
:interface-data
) interface-info-ptr
)
155 (g-type-add-interface-static (gtype name
) (gtype interface
) info
))))
157 (defun add-interfaces (name)
158 (let* ((lisp-type-info (gethash name
*registered-types
*))
159 (interfaces (object-type-interfaces lisp-type-info
)))
160 (iter (for interface in interfaces
)
161 (add-interface name interface
))))
163 (defun class-init (class data
)
164 (declare (ignore data
))
165 (log-for :subclass
"class-init for ~A~%" (gtype-name (g-type-from-class class
)))
166 (setf (foreign-slot-value class
'g-object-class
:get-property
)
167 (callback c-object-property-get
)
168 (foreign-slot-value class
'g-object-class
:set-property
)
169 (callback c-object-property-set
))
171 (install-properties class
))
173 (defun object-property-get (object property-id g-value pspec
)
174 (declare (ignore property-id
))
175 (let* ((lisp-object (or (gethash (pointer-address object
) *foreign-gobjects-strong
*)
176 (gethash (pointer-address object
) *foreign-gobjects-weak
*)))
177 (property-name (foreign-slot-value pspec
'g-param-spec
:name
))
178 (property-type (foreign-slot-value pspec
'g-param-spec
:value-type
))
179 (type-name (gtype-name (foreign-slot-value pspec
'g-param-spec
:owner-type
)))
180 (lisp-type-info (gethash type-name
*registered-types
*))
181 (property-info (find property-name
(object-type-properties lisp-type-info
) :test
'string
= :key
'first
))
182 (property-get-fn (fourth property-info
)))
183 (log-for :subclass
"get(~A,'~A')~%" lisp-object property-name
)
184 (let ((value (restart-case
185 (funcall property-get-fn lisp-object
)
186 (return-from-property-getter (value) :interactive
(lambda () (format t
"Enter new value: ") (list (eval (read)))) value
))))
187 (set-g-value g-value value property-type
))))
189 (defcallback c-object-property-get
:void
((object :pointer
) (property-id :uint
) (value :pointer
) (pspec :pointer
))
190 (object-property-get object property-id value pspec
))
192 (defun object-property-set (object property-id value pspec
)
193 (declare (ignore property-id
))
194 (let* ((lisp-object (or (gethash (pointer-address object
) *foreign-gobjects-strong
*)
195 (gethash (pointer-address object
) *foreign-gobjects-weak
*)))
196 (property-name (foreign-slot-value pspec
'g-param-spec
:name
))
197 (type-name (gtype-name (foreign-slot-value pspec
'g-param-spec
:owner-type
)))
198 (lisp-type-info (gethash type-name
*registered-types
*))
199 (property-info (find property-name
(object-type-properties lisp-type-info
) :test
'string
= :key
'first
))
200 (property-set-fn (fifth property-info
))
201 (new-value (parse-g-value value
)))
202 (log-for :subclass
"set(~A,'~A',~A)~%" lisp-object property-name new-value
)
204 (funcall property-set-fn new-value lisp-object
)
205 (return-without-error-from-property-setter () nil
))))
207 (defcallback c-object-property-set
:void
((object :pointer
) (property-id :uint
) (value :pointer
) (pspec :pointer
))
208 (object-property-set object property-id value pspec
))
210 (defmacro register-object-type-implementation
(name class parent interfaces properties
)
211 (unless (stringp parent
)
212 (setf parent
(gtype-name (gtype parent
))))
214 (setf (gethash ,name
*registered-types
*) (make-object-type :name
,name
:class
',class
:parent
,parent
:interfaces
',interfaces
:properties
',properties
))
216 (log-for :subclass
"Registering GObject type implementation ~A for type ~A~%" ',class
,name
)
217 (with-foreign-object (query 'g-type-query
)
218 (g-type-query (gtype ,parent
) query
)
219 (g-type-register-static-simple (gtype ,parent
)
221 (foreign-slot-value query
'g-type-query
:class-size
)
222 (callback c-class-init
)
223 (foreign-slot-value query
'g-type-query
:instance-size
)
224 (callback c-instance-init
) nil
))
225 (add-interfaces ,name
))
227 ,@(iter (for (prop-name prop-type prop-accessor prop-reader prop-writer
) in properties
)
228 (declare (ignorable prop-type
))
230 (collect `(defun ,prop-accessor
(object) (g-object-call-get-property object
,prop-name
))))
232 (collect `(defun (setf ,prop-accessor
) (new-value object
) (g-object-call-set-property object
,prop-name new-value
))))))