3 (define-foreign-type g-boxed-foreign-type ()
5 :accessor g-boxed-foreign-info
6 :initform (error "info must be specified")))
7 (:actual-type :pointer))
9 (eval-when (:compile-toplevel :load-toplevel :execute)
10 (defstruct g-boxed-info
14 (eval-when (:compile-toplevel :load-toplevel :execute)
15 (defun get-g-boxed-foreign-info (name)
16 (get name 'g-boxed-foreign-info)))
18 (defvar *g-type-name->g-boxed-foreign-info* (make-hash-table :test 'equal))
20 (defun get-g-boxed-foreign-info-for-gtype (g-type-designator)
21 (or (gethash (g-type-string g-type-designator) *g-type-name->g-boxed-foreign-info*)
22 (error "Unknown GBoxed type '~A'" (g-type-string g-type-designator))))
24 (define-parse-method g-boxed-foreign (name)
25 (let ((info (get-g-boxed-foreign-info name)))
26 (assert info nil "Unknown foreign GBoxed type ~A" name)
27 (make-instance 'g-boxed-foreign-type :info info)))
29 (defgeneric boxed-proxy-to-native (type-info proxy))
31 (defgeneric boxed-read-values-from-native (type-info proxy native))
33 (defgeneric boxed-native-to-proxy (type-info native))
35 (defgeneric boxed-write-values-to-native-and-free (type-info proxy native))
37 (defmethod translate-to-foreign (proxy (type g-boxed-foreign-type))
39 (let ((boxed-type-info (g-boxed-foreign-info type)))
40 (values (boxed-proxy-to-native boxed-type-info proxy) proxy))
43 (defmethod free-translated-object (native-structure (type g-boxed-foreign-type) proxy)
45 (let ((boxed-type-info (g-boxed-foreign-info type)))
46 (boxed-read-values-from-native boxed-type-info proxy native-structure)
47 (g-boxed-free (g-boxed-info-g-type boxed-type-info) native-structure))))
49 (defmethod translate-from-foreign (native-structure (type g-boxed-foreign-type))
50 (unless (null-pointer-p native-structure)
51 (let ((info (g-boxed-foreign-info type)))
52 (boxed-native-to-proxy info native-structure))))
54 (defmethod cleanup-translated-object-for-callback ((type g-boxed-foreign-type) proxy native-structure)
55 (unless (null-pointer-p native-structure)
56 (let ((info (g-boxed-foreign-info type)))
57 (boxed-write-values-to-native-and-free info proxy native-structure))))
59 (defmethod has-callback-cleanup ((type g-boxed-foreign-type))
62 (eval-when (:load-toplevel :compile-toplevel :execute)
63 (defstruct (g-boxed-cstruct-wrapper-info (:include g-boxed-info))
67 (defmacro define-g-boxed-cstruct (name g-type-name &body slots)
70 ,@(iter (for (name type &key initarg) in slots)
71 (collect (list name initarg))))
72 (defcstruct ,(generated-cstruct-name name)
73 ,@(iter (for (name type &key initarg) in slots)
74 (collect `(,name ,type))))
75 (eval-when (:compile-toplevel :load-toplevel :execute)
76 (setf (get ',name 'g-boxed-foreign-info)
77 (make-g-boxed-cstruct-wrapper-info :name ',name
79 :cstruct ',(generated-cstruct-name name)
80 :slots ',(iter (for (name type &key initarg) in slots)
82 (gethash ,g-type-name *g-type-name->g-boxed-foreign-info*)
83 (get ',name 'g-boxed-foreign-info)))))
85 (defmethod boxed-proxy-to-native ((type g-boxed-cstruct-wrapper-info) proxy)
86 (let* ((native-structure-type (g-boxed-cstruct-wrapper-info-cstruct type))
87 (native-structure (foreign-alloc native-structure-type)))
88 (iter (for slot in (g-boxed-cstruct-wrapper-info-slots type))
89 (setf (foreign-slot-value native-structure native-structure-type slot)
90 (slot-value proxy slot)))
91 (prog1 (g-boxed-copy (g-boxed-info-g-type type) native-structure)
92 (foreign-free native-structure))))
94 (defmethod boxed-native-to-proxy ((type g-boxed-cstruct-wrapper-info) native-structure)
95 (let* ((native-structure-type (g-boxed-cstruct-wrapper-info-cstruct type))
96 (proxy-structure-type (g-boxed-info-name type))
97 (proxy (make-instance proxy-structure-type)))
98 (iter (for slot in (g-boxed-cstruct-wrapper-info-slots type))
99 (setf (slot-value proxy slot)
100 (foreign-slot-value native-structure native-structure-type slot)))
103 (defmethod boxed-read-values-from-native ((type g-boxed-cstruct-wrapper-info) proxy native-structure)
104 (let ((native-structure-type (g-boxed-cstruct-wrapper-info-cstruct type)))
105 (iter (for slot in (g-boxed-cstruct-wrapper-info-slots type))
106 (setf (slot-value proxy slot)
107 (foreign-slot-value native-structure native-structure-type slot)))))
109 (defmethod boxed-write-values-to-native-and-free ((type g-boxed-cstruct-wrapper-info) proxy native-structure)
110 (let ((native-structure-type (g-boxed-cstruct-wrapper-info-cstruct type)))
111 (iter (for slot in (g-boxed-cstruct-wrapper-info-slots type))
112 (setf (foreign-slot-value native-structure native-structure-type slot)
113 (slot-value proxy slot)))))
115 (eval-when (:compile-toplevel :load-toplevel :execute)
116 (defstruct (g-boxed-opaque-wrapper-info (:include g-boxed-info))
119 (defclass g-boxed-opaque ()
120 ((pointer :initarg :pointer
122 :accessor g-boxed-opaque-pointer)))
124 (defmethod boxed-proxy-to-native ((type g-boxed-opaque-wrapper-info) proxy)
125 (g-boxed-copy (g-boxed-info-g-type type) (g-boxed-opaque-pointer proxy)))
127 (defmethod boxed-native-to-proxy ((type g-boxed-opaque-wrapper-info) native)
128 (let ((g-type (g-boxed-info-g-type type)))
129 (flet ((finalizer () (g-boxed-free g-type native)))
130 (let ((proxy (make-instance (g-boxed-info-name type) :pointer native)))
131 (tg:finalize proxy #'finalizer)))))
133 (defmethod boxed-read-values-from-native ((type g-boxed-opaque-wrapper-info) proxy native)
134 (declare (ignore type proxy native)))
136 (defmethod boxed-write-values-to-native-and-free ((type g-boxed-opaque-wrapper-info) proxy native)
137 (declare (ignore type native))
138 (tg:cancel-finalization proxy))
140 (defmacro define-g-boxed-opaque (name g-type-name &key
141 (alloc (error "Alloc must be specified")))
142 (let ((native-copy (gensym "NATIVE-COPY-"))
143 (instance (gensym "INSTANCE-"))
144 (finalizer (gensym "FINALIZER-")))
145 `(progn (defclass ,name (g-boxed-opaque) ())
146 (defmethod initialize-instance :after ((,instance ,name) &key &allow-other-keys)
147 (unless (g-boxed-opaque-pointer ,instance)
148 (let ((,native-copy ,alloc))
149 (flet ((,finalizer () (g-boxed-free ,g-type-name ,native-copy)))
150 (setf (g-boxed-opaque-pointer ,instance) ,native-copy)
151 (finalize ,instance #',finalizer)))))
152 (eval-when (:compile-toplevel :load-toplevel :execute)
153 (setf (get ',name 'g-boxed-foreign-info)
154 (make-g-boxed-opaque-wrapper-info :name ',name
155 :g-type ,g-type-name)
156 (gethash ,g-type-name *g-type-name->g-boxed-foreign-info*)
157 (get ',name 'g-boxed-foreign-info))))))
159 (defstruct var-structure
166 (defstruct var-structure-variant
167 discriminating-values
170 (defstruct var-structure-slot
176 (defmethod make-load-form ((object var-structure) &optional env)
177 (make-load-form-saving-slots object :environment env))
179 (defmethod make-load-form ((object var-structure-slot) &optional env)
180 (make-load-form-saving-slots object :environment env))
182 (defmethod make-load-form ((object var-structure-variant) &optional env)
183 (make-load-form-saving-slots object :environment env))
185 (defun var-struct-all-slots (struct)
187 (append (var-struct-all-slots (var-structure-parent struct))
188 (var-structure-slots struct))))
190 (defun all-structures (structure)
191 (append (iter (for variant in (var-structure-variants structure))
192 (appending (all-structures (var-structure-variant-structure variant))))
195 (defun parse-variant-structure-definition (name slots &optional parent)
196 (iter (with result = (make-var-structure :name name
199 :discriminator-slot nil
202 (if (eq :variant (first slot))
204 (when (var-structure-discriminator-slot result)
205 (error "Structure has more than one discriminator slot"))
206 (setf (var-structure-discriminator-slot result) (second slot)
207 (var-structure-variants result) (parse-variants result (nthcdr 2 slot))))
208 (push (parse-slot slot) (var-structure-slots result)))
209 (finally (setf (var-structure-slots result)
210 (reverse (var-structure-slots result)))
213 (defun parse-slot (slot)
214 (destructuring-bind (name type &key count initform) slot
215 (make-var-structure-slot :name name :type type :count count :initform initform)))
217 (defun ensure-list (thing)
222 (defun parse-variants (parent variants)
223 (iter (for var-descr in variants)
224 (for (options variant-name . slots) in variants)
226 (make-var-structure-variant
227 :discriminating-values (ensure-list options)
228 :structure (parse-variant-structure-definition variant-name slots parent)))
231 (defun generated-cstruct-name (symbol)
232 (or (get symbol 'generated-cstruct-name)
233 (setf (get symbol 'generated-cstruct-name) (gensym (format nil "GEN-~A-CSTRUCT-" (symbol-name symbol))))))
235 (defun generated-cunion-name (symbol)
236 (or (get symbol 'generated-cunion-name)
237 (setf (get symbol 'generated-cunion-name) (gensym (format nil "GEN-~A-CUNION-" (symbol-name symbol))))))
239 (defun generate-cstruct-1 (struct)
240 `(defcstruct ,(generated-cstruct-name (var-structure-name struct))
241 ,@(iter (for slot in (var-struct-all-slots struct))
242 (collect `(,(var-structure-slot-name slot) ,(var-structure-slot-type slot)
243 ,@(when (var-structure-slot-count slot)
244 (list `(:count ,(var-structure-slot-count slot)))))))))
246 (defun generate-c-structures (structure)
247 (iter (for str in (all-structures structure))
248 (collect (generate-cstruct-1 str))))
250 (defun generate-union-1 (struct)
251 `(defcunion ,(generated-cunion-name (var-structure-name struct))
252 ,@(iter (for variant in (all-structures struct))
253 (unless (eq struct variant)
254 (collect `(,(var-structure-name variant)
255 ,(generated-cunion-name (var-structure-name variant))))))))
257 (defun generate-unions (struct)
258 (iter (for str in (all-structures struct))
259 (collect (generate-union-1 str))))
261 (defun generate-structure-1 (str)
262 `(defstruct ,(if (var-structure-parent str)
263 `(,(var-structure-name str) (:include ,(var-structure-name (var-structure-parent str))
264 (,(var-structure-discriminator-slot (var-structure-parent str))
265 ,(first (var-structure-variant-discriminating-values
267 (var-structure-variants
268 (var-structure-parent str))
269 :key #'var-structure-variant-structure))))))
270 `,(var-structure-name str))
271 ,@(iter (for slot in (var-structure-slots str))
272 (collect `(,(var-structure-slot-name slot)
273 ,(var-structure-slot-initform slot))))))
275 (defun generate-structures (str)
276 (iter (for variant in (reverse (all-structures str)))
277 (collect (generate-structure-1 variant))))
279 (defun generate-native-type-decision-procedure-1 (str proxy-var)
280 (if (null (var-structure-discriminator-slot str))
281 `(values ',(generated-cstruct-name (var-structure-name str))
282 ',(mapcar #'var-structure-slot-name (var-struct-all-slots str)))
283 `(typecase ,proxy-var
284 ,@(iter (for variant in (var-structure-variants str))
285 (for v-str = (var-structure-variant-structure variant))
286 (collect `(,(var-structure-name v-str)
287 ,(generate-native-type-decision-procedure-1 v-str proxy-var))))
288 (,(var-structure-name str)
289 (values ',(generated-cstruct-name (var-structure-name str))
290 ',(mapcar #'var-structure-slot-name (var-struct-all-slots str)))))))
292 (defun generate-proxy-type-decision-procedure-1 (str native-var)
293 (if (null (var-structure-discriminator-slot str))
294 `(values ',(var-structure-name str)
295 ',(mapcar #'var-structure-slot-name (var-struct-all-slots str))
296 ',(generated-cstruct-name (var-structure-name str)))
297 `(case (foreign-slot-value ,native-var
298 ',(generated-cstruct-name (var-structure-name str))
299 ',(var-structure-discriminator-slot str))
300 ,@(iter (for variant in (var-structure-variants str))
301 (for v-str = (var-structure-variant-structure variant))
302 (collect `(,(var-structure-variant-discriminating-values variant)
303 ,(generate-proxy-type-decision-procedure-1
306 (t (values ',(var-structure-name str)
307 ',(mapcar #'var-structure-slot-name (var-struct-all-slots str))
308 ',(generated-cstruct-name (var-structure-name str)))))))
310 (defun generate-proxy-type-decision-procedure (str)
311 (let ((native (gensym "NATIVE-")))
313 ,(generate-proxy-type-decision-procedure-1 str native))))
315 (defun generate-native-type-decision-procedure (str)
316 (let ((proxy (gensym "PROXY-")))
318 ,(generate-native-type-decision-procedure-1 str proxy))))
320 (defun compile-proxy-type-decision-procedure (str)
321 (compile nil (generate-proxy-type-decision-procedure str)))
323 (defun compile-native-type-decision-procedure (str)
324 (compile nil (generate-native-type-decision-procedure str)))
326 (defstruct (g-boxed-variant-cstruct-info (:include g-boxed-info))
328 native-type-decision-procedure
329 proxy-type-decision-procedure)
331 (defmethod make-load-form ((object g-boxed-variant-cstruct-info) &optional env)
332 (make-load-form-saving-slots object :environment env))
334 (defmacro define-g-boxed-variant-cstruct (name g-type-name &body slots)
335 (let* ((structure (parse-variant-structure-definition name slots)))
336 `(progn ,@(generate-c-structures structure)
337 ,@(generate-unions structure)
338 ,@(generate-structures structure)
339 (eval-when (:compile-toplevel :load-toplevel :execute)
340 (setf (get ',name 'g-boxed-foreign-info)
341 (make-g-boxed-variant-cstruct-info :name ',name
344 :native-type-decision-procedure
345 ,(generate-native-type-decision-procedure structure)
346 :proxy-type-decision-procedure
347 ,(generate-proxy-type-decision-procedure structure))
348 (gethash ,g-type-name *g-type-name->g-boxed-foreign-info*)
349 (get ',name 'g-boxed-foreign-info))))))
351 (defun decide-native-type (info proxy)
352 (funcall (g-boxed-variant-cstruct-info-native-type-decision-procedure info) proxy))
354 (defmethod boxed-proxy-to-native ((type g-boxed-variant-cstruct-info) proxy)
355 (multiple-value-bind (actual-cstruct slots) (decide-native-type type proxy)
356 (let ((native-structure (foreign-alloc
357 (generated-cstruct-name
359 (g-boxed-variant-cstruct-info-root type))))))
360 (iter (for slot in slots)
361 (setf (foreign-slot-value native-structure actual-cstruct slot)
362 (slot-value proxy slot)))
363 (prog1 (g-boxed-copy (g-boxed-info-g-type type) native-structure)
364 (foreign-free native-structure)))))
366 (defun decide-proxy-type (info native-structure)
367 (funcall (g-boxed-variant-cstruct-info-proxy-type-decision-procedure info) native-structure))
369 (defmethod boxed-write-values-to-native-and-free ((type g-boxed-variant-cstruct-info) proxy native-ptr)
370 (multiple-value-bind (actual-struct slots actual-cstruct) (decide-proxy-type type native-ptr)
371 (unless (eq (type-of proxy) actual-struct)
373 (error "Expected type of boxed variant structure ~A and actual type ~A do not match"
374 (type-of proxy) actual-struct)
375 (skip-parsing-values () (return-from boxed-write-values-to-native-and-free))))
376 (iter (for slot in slots)
377 (setf (slot-value proxy slot)
378 (foreign-slot-value native-ptr actual-cstruct slot)))))
380 (defmethod boxed-native-to-proxy ((type g-boxed-variant-cstruct-info) native-ptr)
381 (multiple-value-bind (actual-struct slots actual-cstruct) (decide-proxy-type type native-ptr)
382 (let ((proxy (make-instance actual-struct)))
383 (iter (for slot in slots)
384 (setf (slot-value proxy slot)
385 (foreign-slot-value native-ptr actual-cstruct slot)))
388 (defgeneric boxed-native-to-proxy-needs-copy-for-gvalue-get (type))
390 (defmethod boxed-native-to-proxy-needs-copy-for-gvalue-get ((type g-boxed-cstruct-wrapper-info))
393 (defmethod boxed-native-to-proxy-needs-copy-for-gvalue-get ((type g-boxed-variant-cstruct-info))
396 (defmethod boxed-native-to-proxy-needs-copy-for-gvalue-get ((type g-boxed-opaque-wrapper-info))
399 (defmethod parse-g-value-for-type (gvalue-ptr (type-numeric (eql +g-type-boxed+)) parse-kind)
400 (declare (ignore parse-kind))
401 (if (g-type= (g-value-type gvalue-ptr) (g-strv-get-type))
402 (convert-from-foreign (g-value-get-boxed gvalue-ptr) '(glib:gstrv :free-from-foreign nil))
403 (let* ((boxed-type (get-g-boxed-foreign-info-for-gtype type-numeric))
404 (native (if (boxed-native-to-proxy-needs-copy-for-gvalue-get boxed-type)
405 (g-boxed-copy type-numeric (g-value-get-boxed gvalue-ptr))
406 (g-value-get-boxed gvalue-ptr))))
407 (create-proxy-for-native boxed-type native))))
409 (defmethod set-gvalue-for-type (gvalue-ptr (type-numeric (eql +g-type-boxed+)) value)
410 (if (g-type= (g-value-type gvalue-ptr) (g-strv-get-type))
411 (g-value-set-boxed gvalue-ptr (convert-to-foreign value '(glib:gstrv :free-from-foreign nil)))
412 (let* ((boxed-type (get-g-boxed-foreign-info-for-gtype type-numeric))
413 (native (boxed-proxy-to-native boxed-type value)))
414 (g-value-take-boxed gvalue-ptr native))))