Need to export the extension loader
[cl-glfw.git] / lib / scaffolding.lisp
blob2bd47fa2ff25c22d1829411b71003082a1e40002
1 (defpackage #:cl-glfw-scaffolding
2 (:use #:cl #:cffi #:cl-glfw-types)
3 (:shadowing-import-from #:cl-glfw-types #:boolean #:byte #:float
4 #:char #:pointer)
5 (:export #:defglfun #:defglextfun #:*type-map* #:make-extension-loader))
7 (in-package #:cl-glfw-scaffolding)
9 (defparameter *type-map* nil)
11 (defun c-name-of (func-spec) (first func-spec))
12 (defun lisp-name-of (func-spec) (second func-spec))
13 (defun freturn-of (func-spec) (getf (cddr func-spec) :return))
14 (defun args-of (func-spec) (getf (cddr func-spec) :args))
15 (defun category-of (func-spec) (getf (cddr func-spec) :category))
17 (defun deconstant (symbol)
18 (if (not (constantp symbol))
19 symbol
20 (deconstant (intern (concatenate 'cl:string "_" (symbol-name symbol))))))
22 (defun final-arg-name (arg)
23 (deconstant (intern (string-upcase (symbol-name (getf arg :name))))))
25 (defun get-type (sym)
26 (let ((type-string (cl:string (getf *type-map* sym))))
27 ;; (format t "Type string ~S " type-string)
28 (if (string-equal "string" type-string)
29 (intern type-string :keyword)
30 (intern type-string :cl-glfw-types))))
32 (defun final-arg-type (arg)
33 (let ((type (get-type (getf arg :type))))
34 (cond
35 ((equal "VOID" (symbol-name type)) 'cl-glfw-types:pointer)
36 ((getf arg :array) (if (equal (symbol-name type) "CHAR") :string 'cl-glfw-types:pointer))
37 (t type))))
39 (defun arg-element-type (arg)
40 (get-type (getf arg :type)))
42 (defun conc-symbols (&rest symbols)
43 (intern (apply #'concatenate (cons 'cl:string (mapcar #'symbol-name symbols)))))
45 (defun array-wrappable-p (arg #|args|#)
46 (let ((resolved-type (get-type (getf arg :type))))
47 (and (getf arg :array)
48 ;; we must have a type, ie. not a void* pointer
49 (not (equal "VOID" (symbol-name resolved-type)))
50 ;; opengl cannot retain this pointer, as we would destroy it after passing it
51 (not (getf arg :retained))
52 ;; can we guarantee a size? - used to do this, but the app programmer must get it right himself for OpenGL anyway
53 ;; so doing it this way is consistent with the C-interface, though more dangerous
55 (or (integerp (getf arg :size))
56 (and (symbolp (getf arg :size))
57 (find-if #'(lambda (other-arg)
58 (eql (getf arg :size) (final-arg-name other-arg)))
59 args)))|#
60 ;; our own hook
61 (not (getf arg :wrapped)))))
63 (defun gl-function-definition (func-spec &optional (c-prefix "gl") (lisp-prefix '#:||))
64 `(defcfun (,(concatenate 'cl:string c-prefix (c-name-of func-spec))
65 ,(conc-symbols lisp-prefix (lisp-name-of func-spec)))
66 ,(get-type (intern (freturn-of func-spec)))
67 ,@(mapcar #'(lambda (arg) (list (final-arg-name arg) (final-arg-type arg)))
68 (args-of func-spec))))
70 (defun gl-funcall-definition (func-spec fpointer)
71 `(cffi:foreign-funcall-pointer
72 ,fpointer
73 ,@(mapcan #'(lambda (arg) (list (final-arg-type arg) (final-arg-name arg)))
74 (args-of func-spec))
75 ,(get-type (intern (freturn-of func-spec)))))
77 (defun expand-a-wrapping (func-spec final-content)
78 (let* ((func-spec (copy-tree func-spec)) ; duplicate because we're not supposed to modify macro params
79 (args (args-of func-spec))
80 (first-wrappable (position-if #'array-wrappable-p args)))
81 (if first-wrappable
82 (let* ((arg (elt (args-of func-spec) first-wrappable))
83 (original-array-name (gensym (symbol-name (final-arg-name arg))))
84 (array-name (final-arg-name arg)))
85 ;; set it wrapped by non-consingly attaching a wrapped property on the end
86 (nconc arg (list :wrapped t))
87 `(if (and (typep ,array-name 'sequence) (not (stringp ,array-name)))
88 ;; the actual allocation
89 (let* ((,original-array-name ,array-name)
90 (,array-name (foreign-alloc ',(arg-element-type arg)
91 ;; we used to base it on the count of whatever the spec said
92 #|:count ,(getf arg :size)|#
93 ;; but now, we'll use the user's sequence size, or just their content
94 ,@(if (eql (getf arg :direction) :in)
95 `(:initial-contents ,original-array-name)
96 `(:count (length ,original-array-name))))))
97 ;; (format t "Copying ~a elements of ~a: ~a into ~a~%"
98 ;; ,(getf arg :size) ',array-name ,original-array-name ,array-name)
99 (unwind-protect
100 (prog1
101 ;; recurse in case there are more
102 ,(expand-a-wrapping func-spec final-content)
103 ;; custom coersion of output values, after call
104 ,(when (eql (getf arg :direction) :out)
105 `(cond
106 ((listp ,original-array-name)
107 (loop for i from 0
108 for cel = ,original-array-name then (cdr cel)
109 while cel
110 do (setf (car cel) (mem-aref ,array-name ',(arg-element-type arg) i))))
111 ((vectorp ,original-array-name)
112 (loop for i below (length ,original-array-name) do
113 (setf (aref ,original-array-name i)
114 (mem-aref ,array-name ',(arg-element-type arg) i)))))))
115 (foreign-free ,array-name)))
116 ;; in the case the arg wasn't a sequence, pass it straight through
117 ,(expand-a-wrapping func-spec final-content)))
118 ;; in the case that there is no more wrapping to be done, emit the final content to start unwinding
119 final-content)))
121 (defun proc-parameter-name-of (func-spec)
122 (intern (format nil "*PROC-~A*" (lisp-name-of func-spec))))
124 (defun gl-function-name-of (func-spec)
125 (concatenate 'cl:string "gl" (c-name-of func-spec)))
127 (defun wrapped-win32-gl-function-definition (func-spec)
128 (let ((parameter-name (proc-parameter-name-of func-spec)))
129 `(progn
130 (defparameter ,parameter-name nil)
131 (defun ,(lisp-name-of func-spec)
132 ,(mapcar #'(lambda (arg) (final-arg-name arg))
133 (args-of func-spec))
134 ,(let ((args (args-of func-spec))
135 (funcall-definition (gl-funcall-definition func-spec parameter-name)))
136 ;; if there is more than 0 wrappable arrays
137 (if (some #'array-wrappable-p args)
138 (expand-a-wrapping func-spec funcall-definition)
139 funcall-definition))))))
141 (defmacro make-extension-loader (extension-name (&rest function-specs))
142 #-win32 (declare (ignore function-specs))
143 `(defun ,(intern (format nil "LOAD-~A" extension-name)) ()
144 #+win32
145 (setf
146 ;;Won't refer to gl:get-proc-address symbol directly here, as it's loaded after the scaffolding
147 ,@(let ((get-proc-address-func (find-symbol "GET-PROC-ADDRESS" (find-package '#:cl-glfw-opengl))))
148 (loop for function-spec in function-specs nconcing
149 (list (proc-parameter-name-of function-spec)
150 (list get-proc-address-func (gl-function-name-of function-spec))))))))
152 (defun wrapped-gl-function-definition (func-spec)
153 (let ((args (args-of func-spec)))
154 ;; if there is more than 0 wrappable arrays
155 (if (some #'array-wrappable-p args)
156 `(progn
157 ;; make an inlined function prefixed with %
158 (declaim (inline ,(conc-symbols '#:% (lisp-name-of func-spec))))
159 ,(gl-function-definition func-spec "gl" '#:%)
160 ;; the exposed function with wrappings
161 (defun ,(lisp-name-of func-spec) ,(mapcar #'final-arg-name (args-of func-spec))
162 ,(expand-a-wrapping func-spec
163 `(,(conc-symbols '#:% (lisp-name-of func-spec))
164 ,@(mapcar #'final-arg-name (args-of func-spec))))))
165 (gl-function-definition func-spec))))
167 (defmacro defglfun (&rest func-spec)
168 (wrapped-gl-function-definition func-spec))
170 (defmacro defglextfun (&rest func-spec)
171 #+win32 (wrapped-win32-gl-function-definition func-spec)
172 #-win32 (wrapped-gl-function-definition func-spec))