1 | Here is an example for a simple program that uses CHICKEN's |
---|
2 | foreign function interface. It is the Gtk 2.0 Hello, world |
---|
3 | program as presented in the GTK tutorial, which is available |
---|
4 | [[http://www.gtk.org/tutorial/ch-gettingstarted.html#SEC-HELLOWORLD|here]]. |
---|
5 | |
---|
6 | This example is intended to show various features of the FFI |
---|
7 | and to demonstrate that one can go relatively far without writing |
---|
8 | tons of glue code. The comments are only to show the CHICKEN specific |
---|
9 | things, see the original tutorial for more information. |
---|
10 | |
---|
11 | <enscript highlight=scheme> |
---|
12 | ;;;; gtkhello.scm |
---|
13 | |
---|
14 | (require-extension easyffi) |
---|
15 | |
---|
16 | ;; Include this code directly into generated .c file: |
---|
17 | |
---|
18 | (foreign-declare "#include <gtk/gtk.h>") |
---|
19 | |
---|
20 | ;; Parse declarations and generate Scheme bindings: |
---|
21 | |
---|
22 | (foreign-parse " |
---|
23 | const int GTK_WINDOW_TOPLEVEL; |
---|
24 | |
---|
25 | void gtk_main_quit(); |
---|
26 | void gtk_init(int *, char ***); |
---|
27 | GtkWidget *gtk_window_new(int); |
---|
28 | int g_signal_connect(void *, char *, void *, void *); |
---|
29 | int g_signal_connect_swapped(void *, char *, void *, void *); |
---|
30 | void gtk_container_set_border_width(GtkContainer *, int); |
---|
31 | GtkWidget *gtk_button_new_with_label(char *); |
---|
32 | void gtk_widget_destroy(GtkWidget *); |
---|
33 | void gtk_container_add(GtkContainer *, GtkWidget *); |
---|
34 | void gtk_widget_show(GtkWidget *); |
---|
35 | |
---|
36 | /* the ___safe marker is needed, because gtk_main() may |
---|
37 | (and will) call Scheme callbacks. */ |
---|
38 | ___safe void gtk_main(); |
---|
39 | ") |
---|
40 | |
---|
41 | |
---|
42 | ;; Define a few callback function: |
---|
43 | |
---|
44 | (define-external (hello ((pointer "GtkWidget") widget) |
---|
45 | (c-pointer data)) |
---|
46 | void |
---|
47 | (print "Hello, world") ) |
---|
48 | |
---|
49 | (define-external (delete_event ((pointer "GtkWidget") widget) |
---|
50 | ((pointer "GdkEvent") event) |
---|
51 | (c-pointer data) ) |
---|
52 | bool |
---|
53 | (print "Delete event occured") |
---|
54 | #t) |
---|
55 | |
---|
56 | (define-external (destroy ((pointer "GtkWidget") widget) |
---|
57 | (c-pointer data) ) |
---|
58 | void |
---|
59 | (gtk_main_quit) ) |
---|
60 | |
---|
61 | ;; This is a bit ugly, but there is no elegant way to get |
---|
62 | ;; at argc/argv at the Scheme level and pass pointers to it |
---|
63 | ;; to foreign code: |
---|
64 | |
---|
65 | (foreign-code "gtk_init(&C_main_argc, &C_main_argv);") |
---|
66 | |
---|
67 | (define window (gtk_window_new GTK_WINDOW_TOPLEVEL)) |
---|
68 | |
---|
69 | ;; #$... with a callback-name returns the function pointer: |
---|
70 | |
---|
71 | (g_signal_connect window "delete_event" #$delete_event #f) |
---|
72 | (g_signal_connect window "destroy" #$destroy #f) |
---|
73 | |
---|
74 | (gtk_container_set_border_width window 10) |
---|
75 | |
---|
76 | (define button (gtk_button_new_with_label "Hello World")) |
---|
77 | |
---|
78 | (g_signal_connect button "clicked" #$hello #f) |
---|
79 | |
---|
80 | ;; Here we use foreign-value to get the function pointer. |
---|
81 | ;; #$gtk_widget_destroy wouldn't work: the function is not |
---|
82 | ;; a callback: |
---|
83 | |
---|
84 | (g_signal_connect_swapped button "clicked" |
---|
85 | (foreign-value gtk_widget_destroy c-pointer) window) |
---|
86 | |
---|
87 | (gtk_container_add window button) |
---|
88 | (gtk_widget_show button) |
---|
89 | (gtk_widget_show window) |
---|
90 | (gtk_main) |
---|
91 | </enscript> |
---|
92 | |
---|
93 | To compile it, we have to ensure the proper C compiler and linker |
---|
94 | flags are passed: |
---|
95 | |
---|
96 | $ csc -vk gtkhello.scm -C "`pkg-config --cflags gtk+-2.0`" -L "`pkg-config --libs gtk+-2.0`" -X easyffi |
---|
97 | |
---|
98 | (the [[/eggref/4/easyffi|easyffi]] egg is required) |
---|