chiark / gitweb /
Ensure that BORDER is defined at compile time.
[clg] / gffi / interface.lisp
CommitLineData
beae6579 1;; Common Lisp bindings for GTK+ v2.x
2;; Copyright 1999-2006 Espen S. Johnsen <espen@users.sf.net>
3;;
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:
11;;
12;; The above copyright notice and this permission notice shall be
13;; included in all copies or substantial portions of the Software.
14;;
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.
22
050b602e 23;; $Id: interface.lisp,v 1.10 2008-12-10 02:40:18 espen Exp $
beae6579 24
25(in-package "GFFI")
26
27
28;;;; Foreign function call interface
29
30(defvar *package-prefix* nil)
31
32(defun set-package-prefix (prefix &optional (package *package*))
33 (let ((package (find-package package)))
34 (setq *package-prefix* (delete package *package-prefix* :key #'car))
35 (push (cons package prefix) *package-prefix*))
36 prefix)
37
38(defun package-prefix (&optional (package *package*))
39 (let ((package (find-package package)))
40 (or
41 (cdr (assoc package *package-prefix*))
42 (substitute #\_ #\- (string-downcase (package-name package))))))
43
44(defun find-prefix-package (prefix)
45 (or
46 (car (rassoc (string-downcase prefix) *package-prefix* :test #'string=))
47 (find-package (string-upcase prefix))))
48
49(defmacro use-prefix (prefix &optional (package *package*))
50 `(eval-when (:compile-toplevel :load-toplevel :execute)
51 (set-package-prefix ,prefix ,package)))
52
53
54(defun default-alien-fname (lisp-name)
55 (let* ((name (substitute #\_ #\- (string-downcase lisp-name)))
49ef0cdc 56 (start (position-if-not #'(lambda (char) (char= char #\%)) name))
57 (end (if (string= "_p" name :start2 (- (length name) 2))
58 (- (length name) 2)
59 (length name)))
60 (stripped-name (subseq name start end))
beae6579 61 (prefix (package-prefix *package*)))
62 (if (or (not prefix) (string= prefix ""))
63 stripped-name
64 (format nil "~A_~A" prefix stripped-name))))
65
66(defun default-alien-type-name (type-name)
67 (let ((prefix (package-prefix *package*)))
68 (apply
69 #'concatenate
70 'string
71 (mapcar
72 #'string-capitalize
73 (cons prefix (split-string (symbol-name type-name) :delimiter #\-))))))
74
6dfb2039
RS
75(defun split-alien-name (alien-name)
76 (let ((parts (split-string-if alien-name #'upper-case-p)))
77 (do ((prefix (first parts) (concatenate 'string prefix (first rest)))
78 (rest (rest parts) (cdr rest)))
79 ((null rest)
80 (error "Couldn't split alien name '~A' to find a registered prefix"
81 alien-name))
82 (when (find-prefix-package prefix)
83 (return (values (string-upcase (concatenate-strings rest #\-))
84 (find-prefix-package prefix)))))))
beae6579 85
6dfb2039
RS
86(defun default-type-name (alien-name)
87 (multiple-value-call #'intern (split-alien-name alien-name)))
beae6579 88
89(defun in-arg-p (style)
90 (find style '(:in :in/out :in/return :in-out :return)))
91
92(defun out-arg-p (style)
93 (find style '(:out :in/out :in-out)))
94
95(defun return-arg-p (style)
96 (find style '(:in/return :return)))
97
98(defmacro defbinding (name lambda-list return-type &rest args)
99 (multiple-value-bind (lisp-name c-name)
100 (if (atom name)
101 (values name (default-alien-fname name))
102 (values-list name))
103
104 (let* ((lambda-list-supplied-p lambda-list)
105 (lambda-list (unless (equal lambda-list '(nil)) lambda-list))
2c708568 106 (arg-types ())
107 (aux-bindings ())
beae6579 108 (doc-string (when (stringp (first args)) (pop args)))
109 (parsed-args
110 (mapcar
111 #'(lambda (arg)
112 (destructuring-bind
2c708568 113 (expr type &optional (style :in) (out-type type))
114 (if (atom arg)
115 (list arg arg)
116 arg)
beae6579 117 (cond
118 ((find style '(:in-out :return))
119 (warn "Deprecated argument style: ~S" style))
120 ((not (find style '(:in :out :in/out :in/return)))
121 (error "Bogus argument style: ~S" style)))
122 (when (and
123 (not lambda-list-supplied-p)
2c708568 124 (namep expr) (in-arg-p style)
125 (not (find expr lambda-list)))
126 (push expr lambda-list)
127 (push type arg-types))
beae6579 128 (let ((aux (unless (or (not (in-arg-p style)) (namep expr))
129 (gensym))))
130 (when aux
2c708568 131 (push (list aux expr) aux-bindings))
beae6579 132 (list
133 (cond
134 ((and (namep expr) (not (in-arg-p style))) expr)
02198d7d 135 ((namep expr)
136 #-clisp(make-symbol (string expr))
137 ;; The above used to work in CLISP, but I'm
138 ;; not sure exactly at which version it
139 ;; broke. The following could potentially
140 ;; cause variable capturing
141 #+clisp(intern (format nil "~A-~A" (string expr) (gensym))))
142 (#-clisp(gensym)
143 #+clisp(intern (string (gensym)))))
beae6579 144 (or aux expr) type style out-type))))
145 args)))
146
147 (%defbinding c-name lisp-name
148 (if lambda-list-supplied-p lambda-list (nreverse lambda-list))
2c708568 149 (not lambda-list-supplied-p) (nreverse arg-types)
150 aux-bindings return-type doc-string parsed-args))))
beae6579 151
152
153#+(or cmu sbcl)
154(defun foreign-funcall (cname args return-type)
155 (let ((fparams (loop
156 for (var expr type style out-type) in args
157 collect (if (out-arg-p style)
158 `(addr ,var)
159 var)))
160 (ftypes (loop
161 for (var expr type style out-type) in args
162 collect (if (out-arg-p style)
163 `(* ,(alien-type out-type))
164 (alien-type out-type))))
165 (fname (make-symbol cname)))
166 `(with-alien ((,fname (function ,(alien-type return-type) ,@ftypes) :extern ,cname))
167 (alien-funcall ,fname ,@fparams))))
168
169#+clisp
170(defun foreign-funcall (cname args return-type)
171 (let* ((fparams (loop
172 for (var expr type style out-type) in args
173 collect (if (out-arg-p style)
174 `(ffi:c-var-address ,var)
175 var)))
176 (fargs (loop
177 for (var expr type style out-type) in args
178 collect (list var (if (out-arg-p style)
179 'ffi:c-pointer
180 (alien-type out-type)))))
181 (c-function `(ffi:c-function
182 (:arguments ,@fargs)
183 (:return-type ,(alien-type return-type))
184 (:language :stdc))))
185 `(funcall
186 (load-time-value
4f2a8644 187 (ffi::foreign-library-function
188 ,cname (ffi::foreign-library :default) #?(clisp>= 2 40)nil
beae6579 189 nil (ffi:parse-c-type ',c-function)))
190 ,@fparams)))
191
192
193;; TODO: check if in and out types (if different) translates to same
194;; alien type
2c708568 195(defun %defbinding (cname lisp-name lambda-list declare-p arg-types aux-bindings return-type doc args)
beae6579 196 (let ((out (loop
197 for (var expr type style out-type) in args
198 when (or (out-arg-p style) (return-arg-p style))
199 collect (from-alien-form out-type var)))
200 (fcall (from-alien-form return-type
201 (foreign-funcall cname args return-type))))
202
203 (labels ((create-wrapper (args body)
204 (if args
205 (destructuring-bind (var expr type style out-type) (first args)
206 (declare (ignore out-type))
207 (alien-arg-wrapper type var expr style
208 (create-wrapper (rest args) body)))
209 body)))
2c708568 210 `(progn
211 ,(when declare-p
212 `(declaim
213 (ftype
214 (function
215 ,(mapcar #'argument-type arg-types)
216 (values
217 ,@(when return-type (list (return-type return-type)))
218 ,@(loop
219 for (var expr type style out-type) in args
220 when (out-arg-p style)
221 collect (return-type out-type)
222 when (return-arg-p style)
cab97f15 223 collect (return-type type))))
224 ,lisp-name)))
2c708568 225 (defun ,lisp-name ,lambda-list
beae6579 226 ,doc
2c708568 227 (let ,aux-bindings
beae6579 228 ,(if return-type
229 (create-wrapper args `(values ,fcall ,@out))
2c708568 230 (create-wrapper args `(progn ,fcall (values ,@out))))))))))
beae6579 231
232
233
234;;;; Dynamic (runtime) bindings
235
236(defun mkbinding (name return-type &rest arg-types)
237 #+cmu(declare (optimize (inhibit-warnings 3)))
238 #+sbcl(declare (muffle-conditions compiler-note))
239 (let* ((c-function
240 #+(or cmu sbcl)
241 `(function ,@(mapcar #'alien-type (cons return-type arg-types)))
242 #+clisp
243 `(ffi:c-function
244 (:arguments ,@(mapcar
245 #'(lambda (type)
246 (list (gensym) (alien-type type)))
247 arg-types))
248 (:return-type ,(alien-type return-type))
249 (:language :stdc)))
250 (foreign
251 #+(or cmu sbcl)
252 (handler-bind (#+sbcl(compiler-note #'(lambda (condition)
253 (declare (ignore condition))
254 (muffle-warning))))
255 (%heap-alien
256 (make-heap-alien-info
257 :type (parse-alien-type c-function #+sbcl nil)
258 :sap-form (let ((address (foreign-symbol-address name)))
259 (etypecase address
260 (integer (int-sap address))
261 (system-area-pointer address))))))
262 #+clisp
263 (ffi::foreign-library-function name
4f2a8644 264 (ffi::foreign-library :default) #?(clisp>= 2 40)nil
beae6579 265 nil (ffi:parse-c-type c-function)))
266 (return-value-translator (from-alien-function return-type)))
267 (multiple-value-bind (arg-translators cleanup-funcs)
268 (let ((translator/cleanup-pairs
269 (mapcar
270 #'(lambda (type)
271 (multiple-value-list (to-alien-function type)))
272 arg-types)))
273 (values
274 (mapcar #'first translator/cleanup-pairs)
275 (mapcar #'second translator/cleanup-pairs)))
276 #'(lambda (&rest args)
277 (let ((translated-args (mapcar #'funcall arg-translators args)))
278 (prog1
279 (funcall return-value-translator
280 #+(or cmu sbcl)(apply #'alien-funcall foreign translated-args)
281 #+clisp(apply foreign translated-args))
282 (mapc
283 #'(lambda (cleanup arg translated-arg)
284 (when cleanup
285 (funcall cleanup arg translated-arg)))
286 cleanup-funcs args translated-args)))))))
287
288
289
290;;;; C Callbacks
291
292(defun callback-body (args return-type body)
293 (labels ((create-wrappers (args body)
294 (if args
295 (destructuring-bind (var type) (first args)
296 (callback-wrapper type var var
297 (create-wrappers (rest args) body)))
298 body))
299 (create-body (args body)
300 (to-alien-form return-type
301 (create-wrappers args `(progn ,@body)))))
302 (if (and (consp (first body)) (eq (caar body) 'declare))
303 (let ((ignored (loop
304 for declaration in (cdar body)
305 when (eq (first declaration) 'ignore)
306 nconc (rest declaration))))
307 `(,(first body)
308 ,(create-body
309 (remove-if #'(lambda (arg)
310 (find (first arg) ignored))
311 args)
312 (rest body))))
313 (list (create-body args body)))))
314
315
316#+(or cmu sbcl)
317(defmacro define-callback (name return-type args &body body)
318 (let ((define-callback
319 #+cmu'alien:def-callback
320 #+(and sbcl alien-callbacks)'sb-alien::define-alien-callback
c52ab022 321 #+(and sbcl (not alien-callbacks))'sb-alien:define-alien-function)
322 (args (mapcar #'(lambda (arg)
323 (if (atom arg) (list arg arg) arg))
324 args)))
beae6579 325 `(progn
326 #+cmu(defparameter ,name nil)
327 (,define-callback ,name
328 #+(and sbcl alien-callbacks) ,(alien-type return-type)
329 (#+(or cmu (and sbcl (not alien-callbacks))),(alien-type return-type)
330 ,@(loop
331 for (name type) in args
332 collect `(,name ,(alien-type type))))
333 ,@(callback-body args return-type body)))))
334
335#+(or cmu sbcl)
336(defun callback-address (callback)
337 #+cmu(alien::callback-trampoline callback)
338 #+(and sbcl (not alien-callbacks))(sb-alien:alien-function-sap callback)
339 #+(and sbcl alien-callbacks)(sb-alien:alien-sap callback))
340
341#+sbcl
342(deftype callback ()
343 #-alien-callbacks'sb-alien:alien-function
344 #+alien-callbacks'sb-alien:alien)
345
346
347;;; The callback code for CLISP is based on code from CFFI
348;;; Copyright (C) 2005, James Bielman <jamesjb@jamesjb.com>
349;;; (C) 2005, Joerg Hoehle <hoehle@users.sourceforge.net>
350
351
352;;; *CALLBACKS* contains the callbacks defined by the %DEFCALLBACK
353;;; macro. The symbol naming the callback is the key, and the value
354;;; is a list containing a Lisp function, the parsed CLISP FFI type of
355;;; the callback, and a saved pointer that should not persist across
356;;; saved images.
357#+clisp
358(progn
359 (defvar *callbacks* (make-hash-table))
360
361 ;;; Return a CLISP FFI function type for a CFFI callback function
362 ;;; given a return type and list of argument names and types.
363 (eval-when (:compile-toplevel :load-toplevel :execute)
364 (defun callback-type (return-type arg-names arg-types)
365 (ffi:parse-c-type
366 `(ffi:c-function
367 (:arguments ,@(mapcar (lambda (sym type)
368 (list sym (alien-type type)))
369 arg-names arg-types))
370 (:return-type ,(alien-type return-type))
371 (:language :stdc)))))
372
373 ;;; Register and create a callback function.
374 (defun register-callback (name function parsed-type)
375 (setf (gethash name *callbacks*)
376 (list function parsed-type
377 (ffi:with-foreign-object (ptr 'ffi:c-pointer)
378 ;; Create callback by converting Lisp function to foreign
379 (setf (ffi:memory-as ptr parsed-type) function)
380 (ffi:foreign-value ptr)))))
381
382 ;;; Restore all saved callback pointers when restarting the Lisp
383 ;;; image. This is pushed onto CUSTOM:*INIT-HOOKS*.
384 ;;; Needs clisp > 2.35, bugfix 2005-09-29
385 (defun restore-callback-pointers ()
386 (maphash
387 (lambda (name list)
388 (register-callback name (first list) (second list)))
389 *callbacks*))
390
391 ;;; Add RESTORE-CALLBACK-POINTERS to the lists of functions to run
392 ;;; when an image is restarted.
393 (eval-when (:load-toplevel :execute)
394 (pushnew 'restore-callback-pointers custom:*init-hooks*))
395
396 ;;; Define a callback function NAME to run BODY with arguments
397 ;;; ARG-NAMES translated according to ARG-TYPES and the return type
398 ;;; translated according to RETTYPE. Obtain a pointer that can be
399 ;;; passed to C code for this callback by calling %CALLBACK.
400 (defmacro define-callback (name return-type args &body body)
c52ab022 401 (let* ((args (mapcar #'(lambda (arg)
402 (if (atom arg) (list arg arg) arg))
403 args))
404 (arg-names (mapcar #'first args))
405 (arg-types (mapcar #'second args)))
beae6579 406 `(progn
407 (defvar ,name ',name)
408 (register-callback ',name
409 (lambda ,arg-names ,@(callback-body args return-type body))
410 ,(callback-type return-type arg-names arg-types)))))
411
412 ;;; Look up the name of a callback and return a pointer that can be
413 ;;; passed to a C function. Signals an error if no callback is
414 ;;; defined called NAME.
415 (defun callback-address (name)
416 (multiple-value-bind (list winp) (gethash name *callbacks*)
417 (unless winp
418 (error "Undefined callback: ~S" name))
419 (third list)))
420
421 (deftype callback () 'symbol))
422
423
424
425;;;; Type expansion
426
f28952df
RS
427;; A hack to make the TYPE-EXPAND code for SBCL work.
428#?+(pkg-config:sbcl>= 1 0 35 15)
429(sb-ext:without-package-locks
430 (setf (symbol-function 'sb-kernel::type-expand)
431 (lambda (form) (typexpand form))))
432
beae6579 433(defun type-expand-1 (form)
434 #+(or cmu sbcl)
435 (let ((def (cond ((symbolp form)
436 #+cmu(kernel::info type expander form)
437 #+sbcl(sb-impl::info :type :expander form))
438 ((and (consp form) (symbolp (car form)))
439 #+cmu(kernel::info type expander (car form))
440 #+sbcl(sb-impl::info :type :expander (car form)))
441 (t nil))))
442 (if def
443 (values (funcall def (if (consp form) form (list form))) t)
444 (values form nil)))
445 #+clisp(ext:type-expand form t))
446
447(defun type-expand-to (type form)
448 (labels ((expand (form0)
449 (if (eq (first (mklist form0)) type)
450 form0
451 (multiple-value-bind (expanded-form expanded-p)
452 (type-expand-1 form0)
453 (if expanded-p
454 (expand expanded-form)
455 (error "~A can not be expanded to ~A" form type))))))
456 (expand form)))
457
050b602e 458(defun type-equal-p (type1 type2)
459 (and (subtypep type1 type2) (subtypep type2 type1)))
beae6579 460
461
462;;;; Type methods
463
2c708568 464(defun find-type-method (name type-spec &optional (error-p t))
465 (let ((type-methods (get name 'type-methods))
466 (specifier (if (atom type-spec)
467 type-spec
468 (first type-spec))))
469 (or
470 (gethash specifier type-methods)
471 (when error-p
472 (error
473 "No explicit type method for ~A when call width type specifier ~A found"
474 name type-spec)))))
475
beae6579 476(defun find-next-type-method (name type-spec &optional (error-p t))
477 (let ((type-methods (get name 'type-methods)))
478 (labels ((search-method-in-cpl-order (classes)
479 (when classes
480 (or
481 (gethash (class-name (first classes)) type-methods)
482 (search-method-in-cpl-order (rest classes)))))
483 (lookup-method (type-spec)
484 (if (and (symbolp type-spec) (find-class type-spec nil))
485 (let ((class (find-class type-spec)))
584285fb 486 #?(or (sbcl>= 0 9 15) (featurep :clisp))
beae6579 487 (unless (class-finalized-p class)
488 (finalize-inheritance class))
489 (search-method-in-cpl-order
490 (rest (class-precedence-list class))))
491 (multiple-value-bind (expanded-type expanded-p)
492 (type-expand-1 type-spec)
493 (when expanded-p
494 (or
495 (let ((specifier (etypecase expanded-type
496 (symbol expanded-type)
497 (list (first expanded-type)))))
498 (gethash specifier type-methods))
499 (lookup-method expanded-type))))))
500 (search-built-in-type-hierarchy (sub-tree)
501 (when (subtypep type-spec (first sub-tree))
502 (or
503 (search-nodes (cddr sub-tree))
504 (second sub-tree))))
505 (search-nodes (nodes)
506 (loop
507 for node in nodes
508 as method = (search-built-in-type-hierarchy node)
509 until method
510 finally (return method))))
511 (or
512 (lookup-method type-spec)
513 ;; This is to handle unexpandable types whichs doesn't name a
514 ;; class. It may cause infinite loops with illegal
515 ;; call-next-method calls
b673a77b 516 (unless (or
517 (null type-spec)
518 (and (symbolp type-spec) (find-class type-spec nil)))
beae6579 519 (search-nodes (get name 'built-in-type-hierarchy)))
520 (when error-p
521 (error "No next type method ~A for type specifier ~A"
522 name type-spec))))))
523
524(defun find-applicable-type-method (name type-spec &optional (error-p t))
2c708568 525 (or
526 (find-type-method name type-spec nil)
527 (find-next-type-method name type-spec nil)
528 (when error-p
529 (error
530 "No applicable type method for ~A when call width type specifier ~A"
531 name type-spec))))
532
beae6579 533
534(defun insert-type-in-hierarchy (specifier function nodes)
535 (cond
536 ((let ((node (find specifier nodes :key #'first)))
537 (when node
538 (setf (second node) function)
539 nodes)))
540 ((let ((node
541 (find-if
542 #'(lambda (node)
543 (subtypep specifier (first node)))
544 nodes)))
545 (when node
546 (setf (cddr node)
547 (insert-type-in-hierarchy specifier function (cddr node)))
548 nodes)))
549 ((let ((sub-nodes (remove-if-not
550 #'(lambda (node)
551 (subtypep (first node) specifier))
552 nodes)))
553 (cons
554 (list* specifier function sub-nodes)
555 (nset-difference nodes sub-nodes))))))
556
557(defun add-type-method (name specifier function)
558 (setf (gethash specifier (get name 'type-methods)) function)
559 (when (typep (find-class specifier nil) 'built-in-class)
560 (setf (get name 'built-in-type-hierarchy)
561 (insert-type-in-hierarchy specifier function
562 (get name 'built-in-type-hierarchy)))))
563
564
565(defmacro define-type-generic (name lambda-list &optional documentation)
566 (let ((type-spec (first lambda-list)))
567 (if (or
568 (not lambda-list)
569 (find type-spec '(&optional &key &rest &allow-other-keys)))
570 (error "A type generic needs at least one required argument")
571 `(progn
572 (unless (get ',name 'type-methods)
573 (setf (get ',name 'type-methods) (make-hash-table))
574 (setf (get ',name 'built-in-type-hierarchy) ()))
575 ,(if (intersection '(&optional &key &rest &allow-other-keys) lambda-list)
576 (let ((args (make-symbol "ARGS")))
577 `(defun ,name (,type-spec &rest ,args)
578 ,documentation
579 (apply
580 (find-applicable-type-method ',name ,type-spec)
581 ,type-spec ,args)))
582 `(defun ,name ,lambda-list
583 ,documentation
584 (funcall
585 (find-applicable-type-method ',name ,type-spec)
586 ,@lambda-list)))))))
587
588
589(defmacro define-type-method (name lambda-list &body body)
590 (let ((specifier (cadar lambda-list))
591 (args (make-symbol "ARGS")))
592 `(progn
593 (add-type-method ',name ',specifier
594 #'(lambda (&rest ,args)
595 (flet ((call-next-method (&rest args)
596 (let ((next-method (find-next-type-method ',name ',specifier)))
597 (apply next-method (or args ,args)))))
598 (destructuring-bind (,(caar lambda-list) ,@(rest lambda-list)) ,args
599 ,@body))))
600 ',name)))
601
602
603;;; Rules for auto-exporting symbols
604
605(defexport defbinding (name &rest args)
606 (declare (ignore args))
607 (if (symbolp name)
608 name
609 (first name)))
610
611(defexport define-type-generic (name &rest args)
612 (declare (ignore args))
613 name)