1 ;; Common Lisp bindings for GTK+ v2.x
2 ;; Copyright 2000-2005 Espen S. Johnsen <espen@users.sf.net>
4 ;; Permission is hereby granted, free of charge, to any person obtaining
5 ;; a copy of this software and associated documentation files (the
6 ;; "Software"), to deal in the Software without restriction, including
7 ;; without limitation the rights to use, copy, modify, merge, publish,
8 ;; distribute, sublicense, and/or sell copies of the Software, and to
9 ;; permit persons to whom the Software is furnished to do so, subject to
10 ;; the following conditions:
12 ;; The above copyright notice and this permission notice shall be
13 ;; included in all copies or substantial portions of the Software.
15 ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 ;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 ;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 ;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 ;; $Id: proxy.lisp,v 1.27 2006/02/06 12:48:40 espen Exp $
27 ;;;; Superclass for all metaclasses implementing some sort of virtual slots
29 (eval-when (:compile-toplevel :load-toplevel :execute)
30 (defclass virtual-slots-class (standard-class)
33 (defclass direct-virtual-slot-definition (standard-direct-slot-definition)
34 ((setter :reader slot-definition-setter :initarg :setter)
35 (getter :reader slot-definition-getter :initarg :getter)
36 (unbound :reader slot-definition-unbound :initarg :unbound)
37 (boundp :reader slot-definition-boundp :initarg :boundp)))
39 (defclass effective-virtual-slot-definition (standard-effective-slot-definition)
40 ((setter :reader slot-definition-setter :initarg :setter)
41 (getter :reader slot-definition-getter :initarg :getter)
42 (unbound :reader slot-definition-unbound :initarg :unbound)
43 (boundp :reader slot-definition-boundp :initarg :boundp)))
45 (defclass direct-special-slot-definition (standard-direct-slot-definition)
48 (defclass effective-special-slot-definition (standard-effective-slot-definition)
51 (defvar *unbound-marker* (gensym "UNBOUND-MARKER-"))
53 (defun most-specific-slot-value (instances slot &optional (default *unbound-marker*))
54 (let ((object (find-if
56 (and (slot-exists-p ob slot) (slot-boundp ob slot)))
59 (slot-value object slot)
62 (defmethod initialize-instance ((slotd effective-special-slot-definition) &rest initargs)
63 (declare (ignore initargs))
65 (setf (slot-value slotd 'allocation) :instance))
68 (defmethod direct-slot-definition-class ((class virtual-slots-class) &rest initargs)
69 (case (getf initargs :allocation)
70 (:virtual (find-class 'direct-virtual-slot-definition))
71 (:special (find-class 'direct-special-slot-definition))
72 (t (call-next-method))))
74 (defmethod effective-slot-definition-class ((class virtual-slots-class) &rest initargs)
75 (case (getf initargs :allocation)
76 (:virtual (find-class 'effective-virtual-slot-definition))
77 (:special (find-class 'effective-special-slot-definition))
78 (t (call-next-method))))
81 (defmethod initialize-internal-slot-functions ((slotd effective-virtual-slot-definition))
82 (if (not (slot-boundp slotd 'getter))
84 (slot-value slotd 'reader-function)
86 (declare (ignore object))
87 (error "Can't read slot: ~A" (slot-definition-name slotd)))
88 (slot-value slotd 'boundp-function)
89 #'(lambda (object) (declare (ignore object)) nil))
91 (let ((getter-function
92 (let ((getter (slot-value slotd 'getter)))
97 (funcall getter object)))
100 (setf (slot-value slotd 'reader-function)
105 (slot-definition-type slotd) 'pointer)))
106 (funcall reader (foreign-location object))))))))))
109 (slot-value slotd 'boundp-function)
111 ((slot-boundp slotd 'unbound)
112 (let ((unbound-value (slot-value slotd 'unbound)))
114 (not (eq (funcall getter-function object) unbound-value)))))
115 ((slot-boundp slotd 'boundp)
116 (let ((boundp (slot-value slotd 'boundp)))
119 (symbol #'(lambda (object)
120 (funcall boundp object)))
121 (string (let ((reader ()))
126 (slot-definition-type slotd) 'pointer)))
127 (funcall reader (foreign-location object))))))))
128 ((multiple-value-bind (unbound-p unbound-value)
129 (unbound-value (slot-definition-type slotd))
132 (not (eq (funcall getter-function object) unbound-value))))))
133 (#'(lambda (object) (declare (ignore object)) t))))
136 (slot-value slotd 'reader-function)
138 ((slot-boundp slotd 'unbound)
139 (let ((unbound (slot-value slotd 'unbound))
140 (slot-name (slot-definition-name slotd)))
142 (let ((value (funcall getter-function object)))
143 (if (eq value unbound)
144 (slot-unbound (class-of object) object slot-name)
146 ((slot-boundp slotd 'boundp)
147 (let ((boundp-function (slot-value slotd 'boundp-function)))
150 (funcall boundp-function object)
151 (funcall getter-function object)))))
152 ((multiple-value-bind (unbound-p unbound-value)
153 (unbound-value (slot-definition-type slotd))
154 (let ((slot-name (slot-definition-name slotd)))
157 (let ((value (funcall getter-function object)))
158 (if (eq value unbound-value)
159 (slot-unbound (class-of object) object slot-name)
161 (getter-function)))))
164 (slot-value slotd 'writer-function)
165 (if (not (slot-boundp slotd 'setter))
167 (declare (ignore object))
168 (error "Can't set slot: ~A" (slot-definition-name slotd)))
169 (with-slots (setter) slotd
173 #'(lambda (value object)
174 (funcall (fdefinition setter) value object)))
178 (slot-value slotd 'writer-function)
179 #'(lambda (value object)
182 (mkbinding setter 'nil 'pointer
183 (slot-definition-type slotd))))
184 (funcall writer (foreign-location object) value)))))))))
186 (initialize-internal-slot-gfs (slot-definition-name slotd)))
190 (defmethod compute-slot-accessor-info ((slotd effective-virtual-slot-definition) type gf)
193 (defmethod compute-effective-slot-definition-initargs ((class virtual-slots-class) direct-slotds)
194 (if (typep (first direct-slotds) 'direct-virtual-slot-definition)
196 (let ((getter (most-specific-slot-value direct-slotds 'getter)))
197 (unless (eq getter *unbound-marker*)
198 (setf (getf initargs :getter) getter)))
199 (let ((setter (most-specific-slot-value direct-slotds 'setter)))
200 (unless (eq setter *unbound-marker*)
201 (setf (getf initargs :setter) setter)))
202 (let ((unbound (most-specific-slot-value direct-slotds 'unbound)))
203 (unless (eq unbound *unbound-marker*)
204 (setf (getf initargs :unbound) unbound)))
205 (let ((boundp (most-specific-slot-value direct-slotds 'boundp)))
206 (unless (eq boundp *unbound-marker*)
207 (setf (getf initargs :boundp) boundp)))
208 (nconc initargs (call-next-method)))
212 (defmethod slot-value-using-class
213 ((class virtual-slots-class) (object standard-object)
214 (slotd effective-virtual-slot-definition))
215 (if (funcall (slot-value slotd 'boundp-function) object)
216 (funcall (slot-value slotd 'reader-function) object)
217 (slot-unbound class object (slot-definition-name slotd))))
219 (defmethod slot-boundp-using-class
220 ((class virtual-slots-class) (object standard-object)
221 (slotd effective-virtual-slot-definition))
222 (funcall (slot-value slotd 'boundp-function) object))
224 (defmethod (setf slot-value-using-class)
225 (value (class virtual-slots-class) (object standard-object)
226 (slotd effective-virtual-slot-definition))
227 (funcall (slot-value slotd 'writer-function) value object))
230 (defmethod validate-superclass
231 ((class virtual-slots-class) (super standard-class))
237 (internal *instance-cache*)
238 (defvar *instance-cache* (make-hash-table :test #'eql))
240 (defun cache-instance (instance &optional (weak-ref t))
242 (gethash (sap-int (foreign-location instance)) *instance-cache*)
244 (make-weak-pointer instance)
247 (defun find-cached-instance (location)
248 (let ((ref (gethash (sap-int location) *instance-cache*)))
250 (if (weak-pointer-p ref)
251 (weak-pointer-value ref)
254 (defun instance-cached-p (location)
255 (gethash (sap-int location) *instance-cache*))
257 (defun remove-cached-instance (location)
258 (remhash (sap-int location) *instance-cache*))
261 (defun list-cached-instances ()
262 (let ((instances ()))
263 (maphash #'(lambda (location ref)
264 (declare (ignore location))
265 (push ref instances))
271 ;;;; Proxy for alien instances
273 ;; TODO: add a ref-counted-proxy subclass
275 ((location :allocation :special :reader foreign-location :type pointer))
276 (:metaclass virtual-slots-class))
278 (defgeneric instance-finalizer (object))
279 (defgeneric reference-foreign (class location))
280 (defgeneric unreference-foreign (class location))
281 (defgeneric invalidate-instance (object))
283 (defmethod reference-foreign ((name symbol) location)
284 (reference-foreign (find-class name) location))
286 (defmethod unreference-foreign ((name symbol) location)
287 (unreference-foreign (find-class name) location))
289 (defmethod unreference-foreign :around ((class class) location)
290 (unless (null-pointer-p location)
293 (defmethod print-object ((instance proxy) stream)
294 (print-unreadable-object (instance stream :type t :identity nil)
295 (if (slot-boundp instance 'location)
296 (format stream "at 0x~X" (sap-int (foreign-location instance)))
297 (write-string "at \"unbound\"" stream))))
299 (defmethod initialize-instance :around ((instance proxy) &rest initargs)
300 (declare (ignore initargs))
303 (cache-instance instance)
304 (finalize instance (instance-finalizer instance))))
306 (defmethod instance-finalizer ((instance proxy))
307 (let ((location (foreign-location instance))
308 (class (class-of instance)))
309 ;; (unless (find-method #'unreference-foreign nil (list (class-of class) t) nil)
310 ;; (error "No matching method for UNREFERENCE-INSTANCE when called with class ~A" class))
312 (remove-cached-instance location)
313 (unreference-foreign class location))))
315 (defmethod invalidate-instance ((instance proxy))
316 (remove-cached-instance (foreign-location instance))
317 (slot-makunbound instance 'location))
320 ;;;; Metaclass used for subclasses of proxy
322 (defgeneric most-specific-proxy-superclass (class))
323 (defgeneric direct-proxy-superclass (class))
324 (defgeneric compute-foreign-size (class))
327 (eval-when (:compile-toplevel :load-toplevel :execute)
328 (defclass proxy-class (virtual-slots-class)
329 ((size :reader foreign-size)))
331 (defclass direct-alien-slot-definition (direct-virtual-slot-definition)
332 ((allocation :initform :alien)
333 (offset :reader slot-definition-offset :initarg :offset)))
335 (defclass effective-alien-slot-definition (effective-virtual-slot-definition)
336 ((offset :reader slot-definition-offset :initarg :offset)))
338 (defmethod most-specific-proxy-superclass ((class proxy-class))
341 (subtypep (class-name class) 'proxy))
342 (cdr (compute-class-precedence-list class))))
344 (defmethod direct-proxy-superclass ((class proxy-class))
347 (subtypep (class-name class) 'proxy))
348 (class-direct-superclasses class)))
350 (defmethod shared-initialize ((class proxy-class) names &key size)
353 (size (setf (slot-value class 'size) (first size)))
354 ((slot-boundp class 'size) (slot-makunbound class 'size))))
356 (defmethod direct-slot-definition-class ((class proxy-class) &rest initargs)
357 (case (getf initargs :allocation)
358 (:alien (find-class 'direct-alien-slot-definition))
359 (t (call-next-method))))
361 (defmethod effective-slot-definition-class ((class proxy-class) &rest initargs)
362 (case (getf initargs :allocation)
363 (:alien (find-class 'effective-alien-slot-definition))
364 (t (call-next-method))))
367 (defmethod compute-effective-slot-definition-initargs ((class proxy-class) direct-slotds)
368 (if (eq (most-specific-slot-value direct-slotds 'allocation) :alien)
370 (list :offset (most-specific-slot-value direct-slotds 'offset))
375 (defmethod initialize-internal-slot-functions ((slotd effective-alien-slot-definition))
376 (with-slots (offset) slotd
377 (let ((type (slot-definition-type slotd)))
378 (unless (slot-boundp slotd 'getter)
379 (let ((reader (reader-function type)))
381 (slot-value slotd 'getter)
383 (funcall reader (foreign-location object) offset)))))
385 (unless (slot-boundp slotd 'setter)
386 (let ((writer (writer-function type))
387 (destroy (destroy-function type)))
389 (slot-value slotd 'setter)
390 #'(lambda (value object)
391 (let ((location (foreign-location object)))
392 (funcall destroy location offset) ; destroy old value
393 (funcall writer value location offset))))))))
397 (defmethod compute-foreign-size ((class proxy-class))
400 ;; TODO: call some C code to detect this a compile time
401 (defconstant +struct-alignmen+ 4)
403 (defun align-offset (size)
404 (if (zerop (mod size +struct-alignmen+))
406 (+ size (- +struct-alignmen+ (mod size +struct-alignmen+)))))
408 (defmethod compute-slots ((class proxy-class))
412 (eq (slot-definition-allocation slotd) :alien))
413 (class-direct-slots class))))
416 as offset = (align-offset (foreign-size
417 (most-specific-proxy-superclass class)))
420 (slot-definition-offset slotd)
421 (size-of (slot-definition-type slotd))))
422 for slotd in alien-slots
423 unless (slot-boundp slotd 'offset)
424 do (setf (slot-value slotd 'offset) offset))))
427 (defmethod compute-slots :after ((class proxy-class))
428 (when (and (class-finalized-p class) (not (slot-boundp class 'size)))
429 (let ((size (compute-foreign-size class)))
431 (setf (slot-value class 'size) size)))))
433 (defmethod validate-superclass ((class proxy-class) (super standard-class))
434 (subtypep (class-name super) 'proxy))
436 (defmethod foreign-size ((class-name symbol))
437 (foreign-size (find-class class-name))))
439 (defmethod foreign-size ((object proxy))
440 (foreign-size (class-of object)))
443 (defmethod alien-type ((class proxy-class) &rest args)
444 (declare (ignore class args))
445 (alien-type 'pointer))
447 (defmethod size-of ((class proxy-class) &rest args)
448 (declare (ignore class args))
451 (defmethod from-alien-form (location (class proxy-class) &rest args)
452 (declare (ignore args))
453 `(ensure-proxy-instance ',(class-name class) ,location))
455 (defmethod from-alien-function ((class proxy-class) &rest args)
456 (declare (ignore args))
458 (ensure-proxy-instance class location)))
460 (defmethod to-alien-form (instance (class proxy-class) &rest args)
461 (declare (ignore class args))
462 `(foreign-location ,instance))
464 (defmethod to-alien-function ((class proxy-class) &rest args)
465 (declare (ignore class args))
468 (defmethod copy-from-alien-form (location (class proxy-class) &rest args)
469 (declare (ignore args))
470 (let ((class-name (class-name class)))
471 `(ensure-proxy-instance ',class-name
472 (reference-foreign ',class-name ,location))))
474 (defmethod copy-from-alien-function ((class proxy-class) &rest args)
475 (declare (ignore args))
477 (ensure-proxy-instance class (reference-foreign class location))))
479 (defmethod copy-to-alien-form (instance (class proxy-class) &rest args)
480 (declare (ignore args))
481 `(reference-foreign ',(class-name class) (foreign-location ,instance)))
483 (defmethod copy-to-alien-function ((class proxy-class) &rest args)
484 (declare (ignore args))
486 (reference-foreign class (foreign-location instance))))
488 (defmethod writer-function ((class proxy-class) &rest args)
489 (declare (ignore args))
490 #'(lambda (instance location &optional (offset 0))
491 (assert (null-pointer-p (sap-ref-sap location offset)))
493 (sap-ref-sap location offset)
494 (reference-foreign class (foreign-location instance)))))
496 (defmethod reader-function ((class proxy-class) &rest args)
497 (declare (ignore args))
498 #'(lambda (location &optional (offset 0))
499 (let ((instance (sap-ref-sap location offset)))
500 (unless (null-pointer-p instance)
501 (ensure-proxy-instance class (reference-foreign class instance))))))
503 (defmethod destroy-function ((class proxy-class) &rest args)
504 (declare (ignore args))
505 #'(lambda (location &optional (offset 0))
506 (unreference-foreign class (sap-ref-sap location offset))))
508 (defmethod unbound-value ((class proxy-class) &rest args)
509 (declare (ignore args))
512 (defun ensure-proxy-instance (class location &rest initargs)
513 "Returns a proxy object representing the foreign object at the give
514 location. If an existing object is not found in the cache
515 MAKE-PROXY-INSTANCE is called to create one."
516 (unless (null-pointer-p location)
518 #-debug-ref-counting(find-cached-instance location)
520 (let ((instance (find-cached-instance location)))
522 (format t "Object found in cache: ~A~%" instance)
524 (let ((instance (apply #'make-proxy-instance class location initargs)))
525 (cache-instance instance)
528 (defgeneric make-proxy-instance (class location &key weak)
529 (:documentation "Creates a new proxy object representing the foreign
530 object at the give location. If WEAK is non NIL the foreign memory
531 will not be released when the proxy is garbage collected."))
533 (defmethod make-proxy-instance ((class symbol) location &rest initargs)
534 (apply #'make-proxy-instance (find-class class) location initargs))
536 (defmethod make-proxy-instance ((class proxy-class) location &key weak)
537 (let ((instance (allocate-instance class)))
538 (setf (slot-value instance 'location) location)
540 (finalize instance (instance-finalizer instance)))
544 ;;;; Superclasses for wrapping of C structures
546 (defclass struct (proxy)
548 (:metaclass proxy-class)
551 (defmethod initialize-instance ((struct struct) &rest initargs)
552 (declare (ignore initargs))
553 (unless (slot-boundp struct 'location)
554 (let ((size (foreign-size (class-of struct))))
556 (error "~A has zero size" (class-of struct))
557 (setf (slot-value struct 'location) (allocate-memory size)))))
561 ;;;; Metaclasses used for subclasses of struct
563 (defclass struct-class (proxy-class)
566 (defmethod direct-slot-definition-class ((class struct-class) &rest initargs)
567 (if (not (getf initargs :allocation))
568 (find-class 'direct-alien-slot-definition)
571 (defmethod reference-foreign ((class struct-class) location)
572 (copy-memory location (foreign-size class)))
574 (defmethod unreference-foreign ((class struct-class) location)
575 (deallocate-memory location))
577 (defmethod compute-foreign-size ((class struct-class))
579 for slotd in (class-slots class)
580 when (eq (slot-definition-allocation slotd) :alien)
582 (slot-definition-offset slotd)
583 (size-of (slot-definition-type slotd))))))
584 (+ size (mod size +struct-alignmen+))))
586 (defmethod weak-reader-function ((class struct-class) &rest args)
587 (declare (ignore args))
588 #'(lambda (location &optional (offset 0))
589 (let ((instance (sap-ref-sap location offset)))
590 (unless (null-pointer-p instance)
591 (ensure-proxy-instance class instance :weak t)))))
594 (defclass static-struct-class (struct-class)
597 (defmethod reference-foreign ((class static-struct-class) location)
598 (declare (ignore class))
601 (defmethod unreference-foreign ((class static-struct-class) location)
602 (declare (ignore class location))
606 ;;; Pseudo type for structs which are inlined in other objects
608 (defmethod size-of ((type (eql 'inlined)) &rest args)
609 (declare (ignore type))
610 (foreign-size (first args)))
612 (defmethod reader-function ((type (eql 'inlined)) &rest args)
613 (declare (ignore type))
614 (destructuring-bind (class) args
615 #'(lambda (location &optional (offset 0))
616 (ensure-proxy-instance class
617 (reference-foreign class (sap+ location offset))))))
619 (defmethod writer-function ((type (eql 'inlined)) &rest args)
620 (declare (ignore type))
621 (destructuring-bind (class) args
622 #'(lambda (instance location &optional (offset 0))
623 (copy-memory (foreign-location instance) (foreign-size class) (sap+ location offset)))))
625 (defmethod destroy-function ((type (eql 'inlined)) &rest args)
626 (declare (ignore args))
627 #'(lambda (location &optional (offset 0))
628 (declare (ignore location offset))))