;;; ;;; hgdiobj.lisp ;;; ;;; GDI objects and GDI object handles. ;;; (cl:in-package :lh-winapi) ;;;; Storage representation. ;;; We represent GDI objects as a set of wrapper classes and an ;;; underlying integer object handle. This is supposedly a closed ;;; class hierarchy (there is no way to add "user" GDI objects). (defclass hgdiobj () ((hgdiobj :accessor hgdiobj-hgdiobj :initarg hgdiobj))) (defclass hpen (hgdiobj) ()) (defclass hbrush (hgdiobj) ()) (defclass hdc (hgdiobj) ()) (defclass hpalette (hgdiobj) ()) (defclass hfont (hgdiobj) ()) (defclass hbitmap (hgdiobj) ()) (defclass hrgn (hgdiobj) ()) (defclass hmetafile (hgdiobj) ()) (defclass henhmetafile (hgdiobj) ()) ;;;; GDI object type mapping. ;;; When we are working with an under-specified object type ;;; (specifically HGDIOBJ) we query GDI as to exactly what type of ;;; handle we are given and use the result of the query to determine ;;; the correct wrapper class. The enum and class below support this. (define-alien-type gdi-object-type-id (enum gdi-object-type-id (obj_pen 1) obj_brush obj_dc obj_metadc obj_pal obj_font obj_bitmap obj_region obj_metafile obj_memdc obj_extpen obj_enhmetadc obj_enhmetafile)) (defun class-for-gdi-object-type (type-id) (or (getf '(obj_pen hpen obj_brush hbrush obj_dc hdc obj_metadc hdc obj_pal hpalette obj_font hfont obj_bitmap hbitmap obj_region hrgn obj_metafile hmetafile obj_memdc hdc obj_extpen hpen obj_enhmetadc hdc obj_enhmetafile henhmetafile) type-id) (error "Unable to find wrapper class for GDI object type ~A" type-id))) ;;;; Alien type definition. ;;; This is based on the original work done for translated alien type ;;; definition in lh-guid.lisp. As a departure from previous work ;;; such as the hwnd.lisp type definition, this is a parameterized ;;; type with several non-parameterized type translators for the ;;; various GDI object handle types. Additionally, naturalizing a GDI ;;; object handle declared as an HGDIOBJ (the root class of the ;;; hierarchy) invokes a lookup to determine exactly which type the ;;; object really is. Also unlike the previous alien type definitions ;;; that I have done, there is no "interning". A fresh wrapper can be ;;; constructed several times for the same handle. ;;; First, the basics. Class definition, translator definition and ;;; supporting methods for type tranlsation. We still include a type ;;; so that we don't have to worry about arg-tn or result-tn methods. (define-alien-type-class (gdi-object :include integer :include-args (signed)) (gdi-handle-type nil :type symbol)) (define-alien-type-translator hgdiobj () (make-alien-gdi-object-type :bits 32 :signed nil)) (macrolet ((translate (class) `(define-alien-type-translator ,class () (make-alien-gdi-object-type :bits 32 :signed nil :gdi-handle-type ',class)))) (translate hpen) (translate hbrush) (translate hdc) (translate hpalette) (translate hfont) (translate hbitmap) (translate hrgn) (translate hmetafile) (translate henhmetafile)) (define-alien-type-method (gdi-object :unparse) (type) (or (alien-gdi-object-type-gdi-handle-type type) 'hgdiobj)) (define-alien-type-method (gdi-object :lisp-rep) (type) `(or ,(or (alien-gdi-object-type-gdi-handle-type type) 'hgdiobj) null)) ;;; Because we're a parameterized type we actually need type= and ;;; subtypep methods (okay, we might not need a subtypep method, as ;;; they're only defined for array and pointer types in SBCL proper, ;;; but we may as well, since conceptually this -is- a pointer type). ;;; Note that if there is a defined lisp-rep type for an alien then it ;;; will be used to determine subtype relationships in some but not ;;; all circumstances instead of the subtypep method, so they must be ;;; consistent. ;;; Two gdi-objects are type= if they have the same gdi-handle-type. (define-alien-type-method (gdi-object :type=) (type1 type2) (eq (alien-gdi-object-type-gdi-handle-type type1) (alien-gdi-object-type-gdi-handle-type type2))) ;;; Given two gdi-objects type1 and type2, type2 is subtypep type1 if ;;; type1 is an hgdiobj or if type1 and type2 are type=. (define-alien-type-method (gdi-object :subtypep) (type1 type2) (and (alien-gdi-object-type-p type2) (or (not (alien-gdi-object-type-gdi-handle-type type1)) (alien-type-= type1 type2)))) ;;; There is a cleverness here: If TYPE is unspecialized (that is, it ;;; is HGDIOBJ) then we determine the actual type to use at runtime. (define-alien-type-method (gdi-object :naturalize-gen) (type alien) (let ((handle-type (alien-gdi-object-type-gdi-handle-type type)) (getobjecttype (gensym)) (object-type-id (gensym))) `(if (not (zerop ,alien)) ,(if handle-type `(make-instance ',handle-type 'hgdiobj ,alien) `(with-alien ((,getobjecttype (function gdi-object-type-id unsigned-long) :extern "GetObjectType")) ;; This definition of GetObjectType is defined to take ;; the underlying (alien) integer handle rather than a ;; proper hgdiobj, mostly to avoid either having a pile ;; of eval-when goop in this file or splitting off a ;; separate file just for the :naturalize-gen method. (let ((,object-type-id (alien-funcall ,getobjecttype ,alien))) (make-instance (class-for-gdi-object-type ,object-type-id) 'hgdiobj ,alien)))) nil))) (define-alien-type-method (gdi-object :deport-gen) (type object) (declare (ignore type)) `(if ,object (the (unsigned-byte 32) (hgdiobj-hgdiobj ,object)) 0)) ;;; EOF