chiark / gitweb /
Added :in-out style to define-foreign
[clg] / glib / gforeign.lisp
CommitLineData
560af5c5 1;; Common Lisp bindings for GTK+ v2.0
2;; Copyright (C) 1999-2000 Espen S. Johnsen <espejohn@online.no>
3;;
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.
8;;
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.
13;;
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
17
a27ed65c 18;; $Id: gforeign.lisp,v 1.2 2000-08-16 18:25:30 espen Exp $
560af5c5 19
20(in-package "GLIB")
21
22;;;; Type methods
23
24(defvar *type-methods* (make-hash-table))
25
26(defun ensure-type-method-fun (fname)
27 (unless (fboundp fname)
28 (setf
29 (symbol-function fname)
30 #'(lambda (type-spec &rest args)
31 (apply
32 (find-applicable-type-method type-spec fname) type-spec args)))))
33
34(defmacro define-type-method-fun (fname lambda-list)
35 (declare (ignore lambda-list))
36 `(defun ,fname (type-spec &rest args)
37 (apply
38 (find-applicable-type-method type-spec ',fname) type-spec args)))
39
40
41(defun ensure-type-name (type)
42 (etypecase type
43 (symbol type)
44 (pcl::class (class-name type))))
45
46(defun add-type-method (type fname function)
47 (push
48 (cons fname function)
49 (gethash (ensure-type-name type) *type-methods*)))
50
51(defun find-type-method (type fname)
52 (cdr (assoc fname (gethash (ensure-type-name type) *type-methods*))))
53
54(defun find-applicable-type-method (type-spec fname &optional (error t))
55 (flet ((find-superclass-method (class)
56 (when 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)
62 (cond
63 (expanded-p
64 (find-applicable-type-method expanded-type-spec fname nil))
65 ((neq type-spec t)
66 (find-applicable-type-method t fname nil))))))
67
68 (or
69 (typecase type-spec
70 (pcl::class
71 (or
72 (find-type-method type-spec fname)
73 (find-superclass-method type-spec)))
74 (symbol
75 (or
76 (find-type-method type-spec fname)
77 (find-expanded-type-method type-spec)
78 (find-superclass-method (find-class type-spec nil))))
79 (cons
80 (or
81 (find-type-method (first type-spec) fname)
82 (find-expanded-type-method type-spec)))
83 (t
84 (error "Invalid type specifier ~A" type-spec)))
85 (and
86 error
87 (error
88 "No applicable method for ~A when called with type specifier ~A"
89 fname type-spec)))))
90
91(defmacro deftype-method (fname type lambda-list &body body)
92 `(progn
93 (ensure-type-method-fun ',fname)
94 (add-type-method ',type ',fname #'(lambda ,lambda-list ,@body))
95 ',fname))
96
97(defmacro deftype (name parameters &body body)
98 (destructuring-bind (lisp-name &optional alien-name) (mklist name)
99 `(progn
100 ,(when alien-name
101 `(setf (alien-type-name ',lisp-name) ,alien-name))
102 (lisp:deftype ,lisp-name ,parameters ,@body))))
103
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)))
110
111
112;;;;
113
114(defvar *type-function-cache* (make-hash-table :test #'equal))
115
116(defun get-cached-function (type-spec fname)
117 (cdr (assoc fname (gethash type-spec *type-function-cache*))))
118
119(defun set-cached-function (type-spec fname function)
120 (push (cons fname function) (gethash type-spec *type-function-cache*))
121 function)
122
123
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)
127 (or
128 (get-cached-function type-spec 'to-alien-function)
129 (set-cached-function type-spec 'to-alien-function
130 (compile
131 nil
132 `(lambda (object)
133 (declare (ignorable object))
134 ,(translate-to-alien type-spec 'object))))))
135
136;; and the opposite
137(defun get-from-alien-function (type-spec)
138 (or
139 (get-cached-function type-spec 'from-alien-function)
140 (set-cached-function type-spec 'from-alien-function
141 (compile
142 nil
143 `(lambda (alien)
144 (declare (ignorable alien))
145 ,(translate-from-alien type-spec 'alien))))))
146
147;; and for cleaning up
148(defun get-cleanup-function (type-spec)
149 (or
150 (get-cached-function type-spec 'cleanup-function)
151 (set-cached-function type-spec 'cleanup-function
152 (compile
153 nil
154 `(lambda (alien)
155 (declare (ignorable alien))
156 ,(cleanup-alien type-spec 'alien))))))
157
158
159
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)
163 (or
164 (get-cached-function type-spec 'writer-function)
165 (set-cached-function type-spec 'writer-function
166 (compile
167 nil
168 `(lambda (value sap offset)
169 (declare (ignorable value sap offset))
170 (setf
171 (,(sap-ref-fname type-spec) sap offset)
172 ,(translate-to-alien type-spec 'value :copy)))))))
173
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)
177 (or
178 (get-cached-function type-spec 'reader-function)
179 (set-cached-function type-spec 'reader-function
180 (compile
181 nil
182 `(lambda (sap offset)
183 (declare (ignorable sap offset))
184 ,(translate-from-alien
185 type-spec `(,(sap-ref-fname type-spec) sap offset) :copy))))))
186
187
188(defun get-destroy-function (type-spec)
189 (or
190 (get-cached-function type-spec 'destroy-function)
191 (set-cached-function type-spec 'destroy-function
192 (compile
193 nil
194 `(lambda (sap offset)
195 (declare (ignorable sap offset))
196 ,(cleanup-alien
197 type-spec `(,(sap-ref-fname type-spec) sap offset) :copied))))))
198
199
200
201;;;;
202
203(defconstant +size-of-int+ 4)
204(defconstant +size-of-sap+ 4)
205(defconstant +size-of-float+ 4)
206(defconstant +size-of-double+ 8)
207
208(defun sap-ref-unsigned (sap offset)
209 (sap-ref-32 sap offset))
210
211(defun sap-ref-signed (sap offset)
212 (signed-sap-ref-32 sap offset))
213
214(defun sap-ref-fname (type-spec)
215 (let ((alien-type-spec (mklist (translate-type-spec type-spec))))
216 (ecase (first alien-type-spec)
217 (unsigned
218 (ecase (second alien-type-spec)
219 (8 'sap-ref-8)
220 (16 'sap-ref-16)
221 (32 'sap-ref-32)
222 (64 'sap-ref-64)))
223 (signed
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))))
232
233
234(defun signed (size)
235 (if (eq size '*)
236 `(signed ,(* 8 +size-of-int+))
237 `(signed ,size)))
238
239(defun unsigned (size)
240 (if (eq size '*)
241 `(unsigned ,(* 8 +size-of-int+))
242 `(unsigned ,size)))
243
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+))))
251
252
253;;;; Foreign function call interface
254
255(defvar *package-prefix* nil)
256
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*))
261 prefix)
262
263(defun package-prefix (&optional (package *package*))
264 (let ((package (find-package package)))
265 (or
266 (cdr (assoc package *package-prefix*))
267 (substitute #\_ #\- (string-downcase (package-name package))))))
268
269(defmacro use-prefix (prefix &optional (package *package*))
270 `(eval-when (:compile-toplevel :load-toplevel :execute)
271 (set-package-prefix ,prefix ,package)))
272
273
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)
278 (string lisp-name)))
279 (prefix (package-prefix *package*))
280 (name (substitute #\_ #\- (string-downcase lisp-name-string))))
281 (if (or (not prefix) (string= prefix ""))
282 name
283 (format nil "~A_~A" prefix name))))
284
285
286(defmacro define-foreign (name lambda-list return-type-spec &rest docs/args)
287 (multiple-value-bind (c-name lisp-name)
288 (if (atom name)
289 (values (default-alien-func-name name) name)
290 (values-list name))
291 (let ((supplied-lambda-list lambda-list)
292 (docs nil)
293 (args nil))
294 (dolist (doc/arg docs/args)
295 (if (stringp doc/arg)
296 (push doc/arg docs)
297 (progn
298 (destructuring-bind (expr type &optional (style :in)) doc/arg
a27ed65c 299 (unless (member style '(:in :out :in-out))
560af5c5 300 (error "Bogus argument style ~S in ~S." style doc/arg))
a27ed65c 301 (when (and
302 (not supplied-lambda-list)
303 (namep expr) (member style '(:in :in-out)))
560af5c5 304 (push expr lambda-list))
305 (push
306 (list (if (namep expr) expr (gensym)) expr type style) args)))))
307
308 (%define-foreign
309 c-name lisp-name (or supplied-lambda-list (nreverse lambda-list))
310 return-type-spec (reverse docs) (reverse args)))))
311
312
313#+cmu
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))
318 (dolist (arg args)
319 (destructuring-bind (var expr type-spec style) arg
320 (let ((declaration (translate-type-spec type-spec))
321 (deallocation (cleanup-alien type-spec expr)))
322 (cond
a27ed65c 323 ((member style '(:out :in-out))
560af5c5 324 (alien-types `(* ,declaration))
325 (alien-parameters `(addr ,var))
a27ed65c 326 (alien-bindings
327 `(,var ,declaration
328 ,@(when (eq style :in-out)
329 (list (translate-to-alien type-spec expr)))))
560af5c5 330 (alien-values (translate-from-alien type-spec var)))
331 (deallocation
332 (alien-types declaration)
333 (alien-bindings
334 `(,var ,declaration ,(translate-to-alien type-spec expr)))
335 (alien-parameters var)
336 (alien-deallocatiors deallocation))
337 (t
338 (alien-types declaration)
339 (alien-parameters (translate-to-alien type-spec expr)))))))
340
341 (let ((alien-funcall `(alien-funcall ,lisp-name ,@(alien-parameters))))
342 `(defun ,lisp-name ,lambda-list
343 ,@docs
344 (with-alien ((,lisp-name
345 (function
346 ,(translate-type-spec return-type-spec)
347 ,@(alien-types))
348 :extern ,foreign-name)
349 ,@(alien-bindings))
350 ,(if return-type-spec
351 `(let ((result
352 ,(translate-from-alien return-type-spec alien-funcall)))
353 ,@(alien-deallocatiors)
354 (values result ,@(alien-values)))
355 `(progn
356 ,alien-funcall
357 ,@(alien-deallocatiors)
358 (values ,@(alien-values)))))))))
359
360
361
362
363;;;; Translations for fundamental types
364
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))
377 `(member t nil))
378(lisp:deftype static (type) type)
379(lisp:deftype invalid () nil)
380
381
382(deftype-method cleanup-alien t (type-spec alien &optional copied)
383 (declare (ignore type-spec alien copied))
384 nil)
385
386
387(deftype-method translate-to-alien integer (type-spec number &optional copy)
388 (declare (ignore type-spec copy))
389 number)
390
391(deftype-method translate-from-alien integer (type-spec number &optional alloc)
392 (declare (ignore type-spec alloc))
393 number)
394
395
396(deftype-method translate-type-spec fixnum (type-spec)
397 (declare (ignore type-spec))
398 (signed '*))
399
400(deftype-method translate-to-alien fixnum (type-spec number &optional copy)
401 (declare (ignore type-spec copy))
402 number)
403
404(deftype-method translate-from-alien fixnum (type-spec number &optional alloc)
405 (declare (ignore type-spec alloc))
406 number)
407
408
409(deftype-method translate-type-spec long (type-spec)
410 (declare (ignore type-spec))
411 (signed '*))
412
413
414(deftype-method translate-type-spec unsigned-long (type-spec)
415 (declare (ignore type-spec))
416 (unsigned '*))
417
418
419(deftype-method translate-type-spec short (type-spec)
420 (declare (ignore type-spec))
421 '(signed 16))
422
423
424(deftype-method translate-type-spec unsigned-short (type-spec)
425 (declare (ignore type-spec))
426 '(unsigned 16))
427
428
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))
433 (signed size)))
434
435(deftype-method translate-to-alien signed-byte (type-spec number &optional copy)
436 (declare (ignore type-spec copy))
437 number)
438
439(deftype-method
440 translate-from-alien signed-byte (type-spec number &optional alloc)
441 (declare (ignore type-spec alloc))
442 number)
443
444
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))
449 (unsigned size)))
450
451(deftype-method
452 translate-to-alien unsigned-byte (type-spec number &optional copy)
453 (declare (ignore type-spec copy))
454 number)
455
456(deftype-method
457 translate-from-alien unsigned-byte (type-spec number &optional alloc)
458 (declare (ignore type-spec alloc))
459 number)
460
461
462(deftype-method translate-type-spec single-float (type-spec)
463 (declare (ignore type-spec))
464 'single-float)
465
466(deftype-method
467 translate-to-alien single-float (type-spec number &optional copy)
468 (declare (ignore type-spec copy))
469 number)
470
471(deftype-method
472 translate-from-alien single-float (type-spec number &optional alloc)
473 (declare (ignore type-spec alloc))
474 number)
475
476
477(deftype-method translate-type-spec double-float (type-spec)
478 (declare (ignore type-spec))
479 'double-float)
480
481(deftype-method
482 translate-to-alien double-float (type-spec number &optional copy)
483 (declare (ignore type-spec copy))
484 number)
485
486(deftype-method
487 translate-from-alien double-float (type-spec number &optional alloc)
488 (declare (ignore type-spec alloc))
489 number)
490
491
492(deftype-method translate-type-spec base-char (type-spec)
493 (declare (ignore type-spec))
494 '(unsigned 8))
495
496(deftype-method translate-to-alien base-char (type-spec char &optional copy)
497 (declare (ignore type-spec copy))
498 `(char-code ,char))
499
500(deftype-method translate-from-alien base-char (type-spec code &optional alloc)
501 (declare (ignore type-spec alloc))
502 `(code-char ,code))
503
504
505(deftype-method translate-type-spec string (type-spec)
506 (declare (ignore type-spec))
507 'system-area-pointer)
508
509(deftype-method translate-to-alien string (type-spec string &optional copy)
510 (declare (ignore type-spec))
511 (if copy
512 `(let ((string ,string))
513 (copy-memory
514 (make-pointer (1+ (kernel:get-lisp-obj-address string)))
515 (1+ (length string))))
516 `(make-pointer (1+ (kernel:get-lisp-obj-address ,string)))))
517
518(deftype-method
519 translate-from-alien string (type-spec sap &optional (alloc :dynamic))
520 (declare (ignore type-spec))
521 `(let ((sap ,sap))
522 (unless (null-pointer-p sap)
523 (prog1
524 (c-call::%naturalize-c-string sap)
525 ,(when (eq alloc :dynamic) `(deallocate-memory ,sap))))))
526
527(deftype-method cleanup-alien string (type-spec sap &optional copied)
528 (declare (ignore type-spec))
529 (when copied
530 `(let ((sap ,sap))
531 (unless (null-pointer-p sap)
532 (deallocate-memory sap)))))
533
534
535(deftype-method translate-type-spec boolean (type-spec)
536 (if (atom type-spec)
537 (unsigned '*)
538 (destructuring-bind (name &optional (size '*))
539 (type-expand-to 'boolean type-spec)
540 (declare (ignore name))
541 (unsigned size))))
542
543(deftype-method translate-to-alien boolean (type-spec boolean &optional copy)
544 (declare (ignore type-spec copy))
545 `(if ,boolean 1 0))
546
547(deftype-method translate-from-alien boolean (type-spec int &optional alloc)
548 (declare (ignore type-spec alloc))
549 `(not (zerop ,int)))
550
551
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)
559 (error
560 "No common alien type specifier for union type: ~A" union-type-spec))
561 (first type-spec-translations))))
562
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))
568 (etypecase value
569 ,@(map
570 'list
571 #'(lambda (type-spec)
572 (list type-spec (translate-to-alien type-spec 'value copy)))
573 type-specs)))))
574
575
576
577(deftype-method translate-type-spec system-area-pointer (type-spec)
578 (declare (ignore type-spec))
579 'system-area-pointer)
580
581(deftype-method
582 translate-to-alien system-area-pointer (type-spec sap &optional copy)
583 (declare (ignore type-spec copy))
584 sap)
585
586(deftype-method
587 translate-from-alien system-area-pointer (type-spec sap &optional alloc)
588 (declare (ignore type-spec alloc))
589 sap)
590
591
592(deftype-method translate-type-spec null (type-spec)
593 (declare (ignore type-spec))
594 'system-area-pointer)
595
596(deftype-method translate-to-alien null (type-spec expr &optional copy)
597 (declare (ignore type-spec copy))
598 `(make-pointer 0))
599
600
601(deftype-method translate-type-spec nil (type-spec)
602 (declare (ignore type-spec))
603 'void)
604
605
606(deftype-method transalte-type-spec static (type-spec)
607 (translate-type-spec (second type-spec)))
608
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))
612
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))
616
617(deftype-method cleanup-alien static (type-spec alien &optional copied)
618 (declare (ignore copied))
619 (cleanup-alien type-spec alien nil))
620
621
622
623;;;; Enum and flags type
624
625(defun map-mappings (args op)
626 (let ((current-value 0))
627 (map
628 'list
629 #'(lambda (mapping)
630 (destructuring-bind (symbol &optional (value current-value))
631 (mklist mapping)
632 (setf current-value (1+ value))
633 (case op
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))
638 (:symbols symbol))))
639 (if (integerp (first args))
640 (rest args)
641 args))))
642
643(lisp:deftype enum (&rest args)
644 `(member ,@(map-mappings args :symbols)))
645
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))
651 '(signed 32))))
652
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))
657 `(ecase ,expr
658 ,@(map-mappings args :enum-int))))
659
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))
664 `(ecase ,expr
665 ,@(map-mappings args :int-enum))))
666
667
668(lisp:deftype flags (&rest args)
669 `(or
670 null
671 (cons
672 (member ,@(map-mappings args :symbols))
673 list)))
674
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))
680 '(signed 32))))
681
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)))
687 `(let ((value 0))
688 (dolist (flag ,expr value)
689 (setq value (logior value (second (assoc flag ',mappings)))))))))
690
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)))
696 `(let ((result nil))
697 (dolist (mapping ',mappings result)
698 (unless (zerop (logand ,expr (first mapping)))
699 (push (second mapping) result)))))))