This library converts the elements from GObject Introspection into Lisp-style definitions, based on cl-gobject-introspection.
It’s very easy to create a binding to a new GObject-based library via cl-gobject-introspection-wrapper:
- Clone this repository into the folder
local-projectsunder your Quicklisp installation root, then load the library with(ql:quickload :cl-gobject-introspection-wrapper). - Create a package to store the auto-generated symbols from GIR. To avoid potential symbol conflicts,
we write the
defpackagewith an empty:useclause:(cl:defpackage gtk (:use))
- Use
gir-wrapper:define-gir-namespaceto generate all definitions from the GIR namespace:(gir-wrapper:define-gir-namespace "Gtk")
Note that all the definition symbols are exported automatically.
- If there exist some definitions whose name is not converted correctly or cause symbol conflicts according to Conversion Rules,
you can set up the special variable
gir-wrapper:*quoted-name-alist*to specify the corresponding symbol for a definition. This could also exclude some definitions by specifying the symbols tocl:nilso that you can define them by hand later:(cltl2:compiler-let (gir-wrapper:*quoted-name-alist* '((("TextBuffer" . "get_insert") . text-buffer-get-insert) ; Specify a symbol for class method ("CSET_a_2_z" . +cset-a-z-lower-case+) ; Specify a symbol for function, constant, enumeration, class, interface, struct, or function argument. (("Widget" . "is_sensitive") . cl:nil) ; Exclude a class method. ("String" . cl:nil))) ; Exclude a function, constant, enumeration, class, struct, or interface. (gir-wrapper:define-gir-namespace "Gtk" "4.0"))
For portability consideration, we set the variable before
gir-wrapper:define-gir-namespace, then reset it after that, to avoid using thecompiler-letin CLTL2:(cl:eval-when (:execute :compile-toplevel :load-toplevel) (cl:setf gir-wrapper:*quoted-name-alist* '((("TextBuffer" . "get_insert") . text-buffer-get-insert) ("CSET_a_2_z" . +cset-a-z-lower-case+) (("Widget" . "is_sensitive") . cl:nil) ("Widget")))) (gir-wrapper:define-gir-namespace "Gtk" "4.0") (cl:eval-when (:execute :compile-toplevel :load-toplevel) (cl:setf gir-wrapper:*quoted-name-alist* '()))
In our rule definitions:
*is a placeholder that stands for any type.[]denote an optional part of the name./splits multiple optional words.
| GIR definition | Lisp definition |
|---|---|
Foo | (progn (defun foop (instance)) (deftype foo ())) |
FOOBar | (progn (defun foo-bar-p (instance)) (deftype foo-bar ())) |
| GIR definition | Lisp definition |
|---|---|
Foo new/create[_from/for/with_bar](* arg1, * arg2, ...) | (defun make-foo (&key arg1 arg2 ...)) |
Foo new/create_from/for/with_bar(* arg) if arg is used by another constructor | (defun make-foo (&key bar)) |
Note that the constructors with identical name after conversion will be merged into a single Lisp function, and which constructor to be called is determined by the arguments provided for this function.
In class/interface/struct Foo:
| GIR definition | Lisp definition |
|---|---|
void set_bar(*) | (defun (setf foo-bar) (value instance)) |
* get_bar() | (defun foo-bar (instance))) |
* set_bar[_is_baz](boolean) | (defun (setf foo-bar[-baz]-p) (value instance)) |
boolean [bar_]is_baz() | (defun foo[-bar]-baz-p (instance)) |
boolean get[_bar_is]_baz() | (defun foo[-bar]-baz-p (instance)) |
boolean [bar_]has/should/can_baz() | (defun foo-[bar-]has/should/can-baz-p (instance)) |
* bar(* arg1, * arg2, ...) | (defun foo-bar (instance arg1 arg2 ...)) |
| GIR definition | Lisp definition |
|---|---|
FOO_BAR = 123 | (alexandria:define-constant +foo-bar+ 123 :test #'equal) |
| GIR definition | Lisp definition |
|---|---|
Foo { Bar, Baz, ... } | (progn (defconstant +foo-bar+ 0) (defconstant +foo-baz+ 1) ...) |