2 ;;; Copyright (C) 2013 Andy Wingo <wingo@pobox.com>
4 ;;; Figl is free software: you can redistribute it and/or modify it
5 ;;; under the terms of the GNU Lesser General Public License as
6 ;;; published by the Free Software Foundation, either version 3 of the
7 ;;; License, or (at your option) any later version.
9 ;;; Figl is distributed in the hope that it will be useful, but WITHOUT
10 ;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 ;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 ;;; Public License for more details.
14 ;;; You should have received a copy of the GNU Lesser General Public
15 ;;; License along with this program. If not, see
16 ;;; <http://www.gnu.org/licenses/>.
20 ;; figl is the Foreign Interface to GL.
24 (define-module (figl runtime)
25 #:use-module (system foreign)
26 #:export (current-resolver
27 define-foreign-procedure
28 define-foreign-procedures
30 define-simple-foreign-type))
33 ;; OpenGL and loading. What a mess. So, in the beginning, when
34 ;; Microsoft added support for OpenGL to Windows, they did so via a
35 ;; trampoline DLL. This DLL had a fixed number of entry points, and it
36 ;; was independent of the driver that the graphics card provided. It
37 ;; also provided an extension interface, wglGetProcAddress, which could
38 ;; return additional GL procedures by name. Microsoft was unwilling to
39 ;; extend their trampoline DLL for whatever reason, and so on Windows
40 ;; you always needed to wglGetProcAddress for almost any OpenGL
43 ;; Time passed and GLX and other GL implementations started to want
44 ;; extensions too. This let application vendors ship applications that
45 ;; could take advantage of the capabilities of users's graphics cards
46 ;; without requiring that they be present.
48 ;; There are a couple of differences between WGL and GLX, however.
49 ;; Chiefly, wglGetProcAddress can only be called once you have a
50 ;; context, and the resulting function can only be used in that context.
51 ;; In practice it seems that it can be used also in other contexts that
52 ;; end up referring to that same driver and GPU. GLX on the other hand
53 ;; is context-independent, but presence of a function does not mean that
54 ;; the corresponding functionality is actually available. In theory
55 ;; users have to check for the presence of the GL extension or check the
56 ;; core GL version, depending on whether the interface is an extension
59 ;; Because of this difference between the GLX and WGL semantics, there
60 ;; is no core "glGetProcAddress" function. It's terrible: each
61 ;; windowing system is responsible for providing their own
62 ;; function-loader interface.
64 ;; Finally, Guile needs to load up at least some interfaces using
65 ;; dynamic-link / dynamic-pointer in order to be able to talk to the
66 ;; library at all (and to open a context in the case of Windows), and it
67 ;; happens that these interfaces also work fine for getting some of the
70 ;; All of this mess really has very little place in the world of free
71 ;; software, where dynamic linking is entirely sufficient to deal with
72 ;; this issue, but it is how things have evolved.
74 ;; In light of all of this, we need to make some simplifications.
76 ;; One is that each low-level function will have just one foreign
77 ;; function wrapper. This means that a minority of Windows
78 ;; configurations won't work. Oh well.
80 ;; Another is that if dynamic-link returns a result, that it is assumed
81 ;; that glXGetProcAddress (or the equivalent) would return the same
82 ;; value. So we can try dynamic-link first, and only dispatch to e.g
83 ;; glXGetProcAddress if that fails.
85 ;; Finally, we assume that all GL symbols may be resolved by
86 ;; dynamic-pointer by looking in one library, regardless of whether they
87 ;; come from the lower GL level or from the window-system-specific
91 ;; FIXME: adapt implementation to match!
92 (define (default-resolver name)
93 (dynamic-pointer name (dynamic-link)))
95 (define current-resolver
96 (make-parameter default-resolver))
98 (define (resolve name)
99 ((current-resolver) name))
101 (define-syntax foreign-trampoline
103 (syntax-case stx (->)
105 name (pname ptype) ... -> type)
106 (with-syntax ((sname (symbol->string (syntax->datum #'name))))
107 #'(lambda (pname ...)
108 (let ((ptr (resolve sname)))
110 (pointer->procedure (type)
113 (trampoline pname ...))))))))
115 (define-syntax define-foreign-procedure
117 ((define-foreign-procedure (name (pname ptype) ... -> type)
121 (foreign-trampoline trampoline
122 name (pname ptype) ... -> type))
123 (name (lambda (pname ...)
125 (let ((pname (ptype #:unwrap pname))
127 (type #:wrap (trampoline pname ...))))))
130 (define-syntax define-foreign-procedures
132 ((define-foreign-procedures ((name prototype ...) ...)
135 (define-foreign-procedure (name prototype ...)
139 (define-syntax define-foreign-type
141 ((_ name ffi-type unwrap wrap)
145 ((_ #:wrap x) (wrap x))
146 ((_ #:unwrap x) (unwrap x)))))))
148 (define-syntax define-simple-foreign-type
151 ;; We could dispatch through to define-foreign-type via:
153 ;; (define-foreign-type name
158 ;; However the resulting wrap expression:
160 ;; ((lambda (x) x) (trampoline arg ...))
162 ;; would not be in tail position, as the optimizer doesn't know
163 ;; that the foreign function just returns one value. This hack
164 ;; just passes the wrapped expression through, allowing it to be in
170 ((_ #:unwrap x) x))))))