1 ;; Common Lisp bindings for GTK+ v2.0
2 ;; Copyright (C) 1999-2000 Espen S. Johnsen <espejohn@online.no>
4 ;; This library is free software; you can redistribute it and/or
5 ;; modify it under the terms of the GNU Lesser General Public
6 ;; License as published by the Free Software Foundation; either
7 ;; version 2 of the License, or (at your option) any later version.
9 ;; This library is distributed in the hope that it will be useful,
10 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ;; Lesser General Public License for more details.
14 ;; You should have received a copy of the GNU Lesser General Public
15 ;; License along with this library; if not, write to the Free Software
16 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ;; $Id: gforeign.lisp,v 1.2 2000-08-16 18:25:30 espen Exp $
24 (defvar *type-methods* (make-hash-table))
26 (defun ensure-type-method-fun (fname)
27 (unless (fboundp fname)
29 (symbol-function fname)
30 #'(lambda (type-spec &rest args)
32 (find-applicable-type-method type-spec fname) type-spec args)))))
34 (defmacro define-type-method-fun (fname lambda-list)
35 (declare (ignore lambda-list))
36 `(defun ,fname (type-spec &rest args)
38 (find-applicable-type-method type-spec ',fname) type-spec args)))
41 (defun ensure-type-name (type)
44 (pcl::class (class-name type))))
46 (defun add-type-method (type fname function)
49 (gethash (ensure-type-name type) *type-methods*)))
51 (defun find-type-method (type fname)
52 (cdr (assoc fname (gethash (ensure-type-name type) *type-methods*))))
54 (defun find-applicable-type-method (type-spec fname &optional (error t))
55 (flet ((find-superclass-method (class)
57 (dolist (super (cdr (pcl::class-precedence-list class)))
58 (return-if (find-type-method super fname)))))
59 (find-expanded-type-method (type-spec)
60 (multiple-value-bind (expanded-type-spec expanded-p)
61 (type-expand-1 type-spec)
64 (find-applicable-type-method expanded-type-spec fname nil))
66 (find-applicable-type-method t fname nil))))))
72 (find-type-method type-spec fname)
73 (find-superclass-method type-spec)))
76 (find-type-method type-spec fname)
77 (find-expanded-type-method type-spec)
78 (find-superclass-method (find-class type-spec nil))))
81 (find-type-method (first type-spec) fname)
82 (find-expanded-type-method type-spec)))
84 (error "Invalid type specifier ~A" type-spec)))
88 "No applicable method for ~A when called with type specifier ~A"
91 (defmacro deftype-method (fname type lambda-list &body body)
93 (ensure-type-method-fun ',fname)
94 (add-type-method ',type ',fname #'(lambda ,lambda-list ,@body))
97 (defmacro deftype (name parameters &body body)
98 (destructuring-bind (lisp-name &optional alien-name) (mklist name)
101 `(setf (alien-type-name ',lisp-name) ,alien-name))
102 (lisp:deftype ,lisp-name ,parameters ,@body))))
104 ;; To make the compiler shut up
105 (eval-when (:compile-toplevel :load-toplevel :execute)
106 (define-type-method-fun translate-type-spec (type-spec))
107 (define-type-method-fun translate-to-alien (type-spec expr &optional copy))
108 (define-type-method-fun translate-from-alien (type-spec expr &optional alloc))
109 (define-type-method-fun cleanup-alien (type-spec expr &optional copied)))
114 (defvar *type-function-cache* (make-hash-table :test #'equal))
116 (defun get-cached-function (type-spec fname)
117 (cdr (assoc fname (gethash type-spec *type-function-cache*))))
119 (defun set-cached-function (type-spec fname function)
120 (push (cons fname function) (gethash type-spec *type-function-cache*))
124 ;; Creates a function to translate an object of the specified type
125 ;; from lisp to alien representation.
126 (defun get-to-alien-function (type-spec)
128 (get-cached-function type-spec 'to-alien-function)
129 (set-cached-function type-spec 'to-alien-function
133 (declare (ignorable object))
134 ,(translate-to-alien type-spec 'object))))))
137 (defun get-from-alien-function (type-spec)
139 (get-cached-function type-spec 'from-alien-function)
140 (set-cached-function type-spec 'from-alien-function
144 (declare (ignorable alien))
145 ,(translate-from-alien type-spec 'alien))))))
147 ;; and for cleaning up
148 (defun get-cleanup-function (type-spec)
150 (get-cached-function type-spec 'cleanup-function)
151 (set-cached-function type-spec 'cleanup-function
155 (declare (ignorable alien))
156 ,(cleanup-alien type-spec 'alien))))))
160 ;; Creates a function to write an object of the specified type
161 ;; to the given memory location
162 (defun get-writer-function (type-spec)
164 (get-cached-function type-spec 'writer-function)
165 (set-cached-function type-spec 'writer-function
168 `(lambda (value sap offset)
169 (declare (ignorable value sap offset))
171 (,(sap-ref-fname type-spec) sap offset)
172 ,(translate-to-alien type-spec 'value :copy)))))))
174 ;; Creates a function to read an object of the specified type
175 ;; from the given memory location
176 (defun get-reader-function (type-spec)
178 (get-cached-function type-spec 'reader-function)
179 (set-cached-function type-spec 'reader-function
182 `(lambda (sap offset)
183 (declare (ignorable sap offset))
184 ,(translate-from-alien
185 type-spec `(,(sap-ref-fname type-spec) sap offset) :copy))))))
188 (defun get-destroy-function (type-spec)
190 (get-cached-function type-spec 'destroy-function)
191 (set-cached-function type-spec 'destroy-function
194 `(lambda (sap offset)
195 (declare (ignorable sap offset))
197 type-spec `(,(sap-ref-fname type-spec) sap offset) :copied))))))
203 (defconstant +size-of-int+ 4)
204 (defconstant +size-of-sap+ 4)
205 (defconstant +size-of-float+ 4)
206 (defconstant +size-of-double+ 8)
208 (defun sap-ref-unsigned (sap offset)
209 (sap-ref-32 sap offset))
211 (defun sap-ref-signed (sap offset)
212 (signed-sap-ref-32 sap offset))
214 (defun sap-ref-fname (type-spec)
215 (let ((alien-type-spec (mklist (translate-type-spec type-spec))))
216 (ecase (first alien-type-spec)
218 (ecase (second alien-type-spec)
224 (ecase (second alien-type-spec)
225 (8 'signed-sap-ref-8)
226 (16 'signed-sap-ref-16)
227 (32 'signed-sap-ref-32)
228 (64 'signed-sap-ref-64)))
229 (system-area-pointer 'sap-ref-sap)
230 (single-float 'sap-ref-single)
231 (double-float 'sap-ref-double))))
236 `(signed ,(* 8 +size-of-int+))
239 (defun unsigned (size)
241 `(unsigned ,(* 8 +size-of-int+))
244 (defun size-of (type-spec)
245 (let ((alien-type-spec (translate-type-spec type-spec)))
246 (ecase (first (mklist alien-type-spec))
247 ((signed unsigned) (/ (second alien-type-spec) 8))
248 ((system-area-pointer single-float) +size-of-sap+)
249 (single-float +size-of-float+)
250 (double-float +size-of-double+))))
253 ;;;; Foreign function call interface
255 (defvar *package-prefix* nil)
257 (defun set-package-prefix (prefix &optional (package *package*))
258 (let ((package (find-package package)))
259 (delete-if #'(lambda (assoc) (eq (car assoc) package)) *package-prefix*)
260 (push (cons package prefix) *package-prefix*))
263 (defun package-prefix (&optional (package *package*))
264 (let ((package (find-package package)))
266 (cdr (assoc package *package-prefix*))
267 (substitute #\_ #\- (string-downcase (package-name package))))))
269 (defmacro use-prefix (prefix &optional (package *package*))
270 `(eval-when (:compile-toplevel :load-toplevel :execute)
271 (set-package-prefix ,prefix ,package)))
274 (defun default-alien-func-name (lisp-name)
275 (let* ((lisp-name-string
276 (if (char= (char (the simple-string (string lisp-name)) 0) #\%)
277 (subseq (the simple-string (string lisp-name)) 1)
279 (prefix (package-prefix *package*))
280 (name (substitute #\_ #\- (string-downcase lisp-name-string))))
281 (if (or (not prefix) (string= prefix ""))
283 (format nil "~A_~A" prefix name))))
286 (defmacro define-foreign (name lambda-list return-type-spec &rest docs/args)
287 (multiple-value-bind (c-name lisp-name)
289 (values (default-alien-func-name name) name)
291 (let ((supplied-lambda-list lambda-list)
294 (dolist (doc/arg docs/args)
295 (if (stringp doc/arg)
298 (destructuring-bind (expr type &optional (style :in)) doc/arg
299 (unless (member style '(:in :out :in-out))
300 (error "Bogus argument style ~S in ~S." style doc/arg))
302 (not supplied-lambda-list)
303 (namep expr) (member style '(:in :in-out)))
304 (push expr lambda-list))
306 (list (if (namep expr) expr (gensym)) expr type style) args)))))
309 c-name lisp-name (or supplied-lambda-list (nreverse lambda-list))
310 return-type-spec (reverse docs) (reverse args)))))
314 (defun %define-foreign (foreign-name lisp-name lambda-list
315 return-type-spec docs args)
316 (ext:collect ((alien-types) (alien-bindings) (alien-parameters)
317 (alien-values) (alien-deallocatiors))
319 (destructuring-bind (var expr type-spec style) arg
320 (let ((declaration (translate-type-spec type-spec))
321 (deallocation (cleanup-alien type-spec expr)))
323 ((member style '(:out :in-out))
324 (alien-types `(* ,declaration))
325 (alien-parameters `(addr ,var))
328 ,@(when (eq style :in-out)
329 (list (translate-to-alien type-spec expr)))))
330 (alien-values (translate-from-alien type-spec var)))
332 (alien-types declaration)
334 `(,var ,declaration ,(translate-to-alien type-spec expr)))
335 (alien-parameters var)
336 (alien-deallocatiors deallocation))
338 (alien-types declaration)
339 (alien-parameters (translate-to-alien type-spec expr)))))))
341 (let ((alien-funcall `(alien-funcall ,lisp-name ,@(alien-parameters))))
342 `(defun ,lisp-name ,lambda-list
344 (with-alien ((,lisp-name
346 ,(translate-type-spec return-type-spec)
348 :extern ,foreign-name)
350 ,(if return-type-spec
352 ,(translate-from-alien return-type-spec alien-funcall)))
353 ,@(alien-deallocatiors)
354 (values result ,@(alien-values)))
357 ,@(alien-deallocatiors)
358 (values ,@(alien-values)))))))))
363 ;;;; Translations for fundamental types
365 (lisp:deftype long (&optional (min '*) (max '*)) `(integer ,min ,max))
366 (lisp:deftype unsigned-long (&optional (min '*) (max '*)) `(integer ,min ,max))
367 (lisp:deftype int (&optional (min '*) (max '*)) `(long ,min ,max))
368 (lisp:deftype unsigned-int (&optional (min '*) (max '*)) `(unsigned-long ,min ,max))
369 (lisp:deftype short (&optional (min '*) (max '*)) `(int ,min ,max))
370 (lisp:deftype unsigned-short (&optional (min '*) (max '*)) `(unsigned-int ,min ,max))
371 (lisp:deftype signed (&optional (size '*)) `(signed-byte ,size))
372 (lisp:deftype unsigned (&optional (size '*)) `(signed-byte ,size))
373 (lisp:deftype char () 'base-char)
374 (lisp:deftype pointer () 'system-area-pointer)
375 (lisp:deftype boolean (&optional (size '*))
376 (declare (ignore size))
378 (lisp:deftype static (type) type)
379 (lisp:deftype invalid () nil)
382 (deftype-method cleanup-alien t (type-spec alien &optional copied)
383 (declare (ignore type-spec alien copied))
387 (deftype-method translate-to-alien integer (type-spec number &optional copy)
388 (declare (ignore type-spec copy))
391 (deftype-method translate-from-alien integer (type-spec number &optional alloc)
392 (declare (ignore type-spec alloc))
396 (deftype-method translate-type-spec fixnum (type-spec)
397 (declare (ignore type-spec))
400 (deftype-method translate-to-alien fixnum (type-spec number &optional copy)
401 (declare (ignore type-spec copy))
404 (deftype-method translate-from-alien fixnum (type-spec number &optional alloc)
405 (declare (ignore type-spec alloc))
409 (deftype-method translate-type-spec long (type-spec)
410 (declare (ignore type-spec))
414 (deftype-method translate-type-spec unsigned-long (type-spec)
415 (declare (ignore type-spec))
419 (deftype-method translate-type-spec short (type-spec)
420 (declare (ignore type-spec))
424 (deftype-method translate-type-spec unsigned-short (type-spec)
425 (declare (ignore type-spec))
429 (deftype-method translate-type-spec signed-byte (type-spec)
430 (destructuring-bind (name &optional (size '*))
431 (type-expand-to 'signed-byte type-spec)
432 (declare (ignore name))
435 (deftype-method translate-to-alien signed-byte (type-spec number &optional copy)
436 (declare (ignore type-spec copy))
440 translate-from-alien signed-byte (type-spec number &optional alloc)
441 (declare (ignore type-spec alloc))
445 (deftype-method translate-type-spec unsigned-byte (type-spec)
446 (destructuring-bind (name &optional (size '*))
447 (type-expand-to 'unsigned-byte type-spec)
448 (declare (ignore name))
452 translate-to-alien unsigned-byte (type-spec number &optional copy)
453 (declare (ignore type-spec copy))
457 translate-from-alien unsigned-byte (type-spec number &optional alloc)
458 (declare (ignore type-spec alloc))
462 (deftype-method translate-type-spec single-float (type-spec)
463 (declare (ignore type-spec))
467 translate-to-alien single-float (type-spec number &optional copy)
468 (declare (ignore type-spec copy))
472 translate-from-alien single-float (type-spec number &optional alloc)
473 (declare (ignore type-spec alloc))
477 (deftype-method translate-type-spec double-float (type-spec)
478 (declare (ignore type-spec))
482 translate-to-alien double-float (type-spec number &optional copy)
483 (declare (ignore type-spec copy))
487 translate-from-alien double-float (type-spec number &optional alloc)
488 (declare (ignore type-spec alloc))
492 (deftype-method translate-type-spec base-char (type-spec)
493 (declare (ignore type-spec))
496 (deftype-method translate-to-alien base-char (type-spec char &optional copy)
497 (declare (ignore type-spec copy))
500 (deftype-method translate-from-alien base-char (type-spec code &optional alloc)
501 (declare (ignore type-spec alloc))
505 (deftype-method translate-type-spec string (type-spec)
506 (declare (ignore type-spec))
507 'system-area-pointer)
509 (deftype-method translate-to-alien string (type-spec string &optional copy)
510 (declare (ignore type-spec))
512 `(let ((string ,string))
514 (make-pointer (1+ (kernel:get-lisp-obj-address string)))
515 (1+ (length string))))
516 `(make-pointer (1+ (kernel:get-lisp-obj-address ,string)))))
519 translate-from-alien string (type-spec sap &optional (alloc :dynamic))
520 (declare (ignore type-spec))
522 (unless (null-pointer-p sap)
524 (c-call::%naturalize-c-string sap)
525 ,(when (eq alloc :dynamic) `(deallocate-memory ,sap))))))
527 (deftype-method cleanup-alien string (type-spec sap &optional copied)
528 (declare (ignore type-spec))
531 (unless (null-pointer-p sap)
532 (deallocate-memory sap)))))
535 (deftype-method translate-type-spec boolean (type-spec)
538 (destructuring-bind (name &optional (size '*))
539 (type-expand-to 'boolean type-spec)
540 (declare (ignore name))
543 (deftype-method translate-to-alien boolean (type-spec boolean &optional copy)
544 (declare (ignore type-spec copy))
547 (deftype-method translate-from-alien boolean (type-spec int &optional alloc)
548 (declare (ignore type-spec alloc))
552 (deftype-method translate-type-spec or (union-type-spec)
553 (destructuring-bind (name &rest type-specs)
554 (type-expand-to 'or union-type-spec)
555 (declare (ignore name))
556 (let ((type-spec-translations
557 (map 'list #'translate-type-spec type-specs)))
558 (unless (apply #'all-equal type-spec-translations)
560 "No common alien type specifier for union type: ~A" union-type-spec))
561 (first type-spec-translations))))
563 (deftype-method translate-to-alien or (union-type-spec expr &optional copy)
564 (destructuring-bind (name &rest type-specs)
565 (type-expand-to 'or union-type-spec)
566 (declare (ignore name))
567 `(let ((value ,expr))
571 #'(lambda (type-spec)
572 (list type-spec (translate-to-alien type-spec 'value copy)))
577 (deftype-method translate-type-spec system-area-pointer (type-spec)
578 (declare (ignore type-spec))
579 'system-area-pointer)
582 translate-to-alien system-area-pointer (type-spec sap &optional copy)
583 (declare (ignore type-spec copy))
587 translate-from-alien system-area-pointer (type-spec sap &optional alloc)
588 (declare (ignore type-spec alloc))
592 (deftype-method translate-type-spec null (type-spec)
593 (declare (ignore type-spec))
594 'system-area-pointer)
596 (deftype-method translate-to-alien null (type-spec expr &optional copy)
597 (declare (ignore type-spec copy))
601 (deftype-method translate-type-spec nil (type-spec)
602 (declare (ignore type-spec))
606 (deftype-method transalte-type-spec static (type-spec)
607 (translate-type-spec (second type-spec)))
609 (deftype-method translate-to-alien static (type-spec expr &optional copy)
610 (declare (ignore copy))
611 (translate-to-alien (second type-spec) expr nil))
613 (deftype-method translate-from-alien static (type-spec alien &optional alloc)
614 (declare (ignore alloc))
615 (translate-from-alien (second type-spec) alien nil))
617 (deftype-method cleanup-alien static (type-spec alien &optional copied)
618 (declare (ignore copied))
619 (cleanup-alien type-spec alien nil))
623 ;;;; Enum and flags type
625 (defun map-mappings (args op)
626 (let ((current-value 0))
630 (destructuring-bind (symbol &optional (value current-value))
632 (setf current-value (1+ value))
634 (:enum-int (list symbol value))
635 (:flags-int (list symbol (ash 1 value)))
636 (:int-enum (list value symbol))
637 (:int-flags (list (ash 1 value) symbol))
639 (if (integerp (first args))
643 (lisp:deftype enum (&rest args)
644 `(member ,@(map-mappings args :symbols)))
646 (deftype-method translate-type-spec enum (type-spec)
647 (destructuring-bind (name &rest args) (type-expand-to 'enum type-spec)
648 (declare (ignore name))
649 (if (integerp (first args))
650 `(signed ,(first args))
653 (deftype-method translate-to-alien enum (type-spec expr &optional copy)
654 (declare (ignore copy))
655 (destructuring-bind (name &rest args) (type-expand-to 'enum type-spec)
656 (declare (ignore name))
658 ,@(map-mappings args :enum-int))))
660 (deftype-method translate-from-alien enum (type-spec expr &optional alloc)
661 (declare (ignore alloc))
662 (destructuring-bind (name &rest args) (type-expand-to 'enum type-spec)
663 (declare (ignore name))
665 ,@(map-mappings args :int-enum))))
668 (lisp:deftype flags (&rest args)
672 (member ,@(map-mappings args :symbols))
675 (deftype-method translate-type-spec flags (type-spec)
676 (destructuring-bind (name &rest args) (type-expand-to 'flags type-spec)
677 (declare (ignore name))
678 (if (integerp (first args))
679 `(signed ,(first args))
682 (deftype-method translate-to-alien flags (type-spec expr &optional copy)
683 (declare (ignore copy))
684 (destructuring-bind (name &rest args) (type-expand-to 'flags type-spec)
685 (declare (ignore name))
686 (let ((mappings (map-mappings args :flags-int)))
688 (dolist (flag ,expr value)
689 (setq value (logior value (second (assoc flag ',mappings)))))))))
691 (deftype-method translate-from-alien flags (type-spec expr &optional alloc)
692 (declare (ignore alloc))
693 (destructuring-bind (name &rest args) (type-expand-to 'flags type-spec)
694 (declare (ignore name))
695 (let ((mappings (map-mappings args :int-flags)))
697 (dolist (mapping ',mappings result)
698 (unless (zerop (logand ,expr (first mapping)))
699 (push (second mapping) result)))))))