GTK4/Libadwaita/WebKit bindings for Common Lisp.
Before getting started, please ensure these libraries are available in your system:
- GTK4
- GObject Introspection
- WebkitGTK (optional)
- libadwaita (optional)
Theoretically, the application built with cl-gtk4
can run on most systems supported by GTK4 and most implementations that support CFFI callback (required by cl-gobject-introspection
).
The examples are tested to run on following implementations:
- SBCL
- Microsoft Windows
- MacOS
See: https://ibb.co/7KZz3r2 - GNU/Linux
See the screenshots in the Examples section.
- Microsoft Windows
- CCL
- ECL
- ABCL
- Currently,
cl-gtk4
is available on Ultralisp, so it can be downloaded via Quicklisp with Ultralisp installed as its distribution. To installcl-gtk4
manually, you can clone this repository along with the following dependencies into thelocal-projects
under your Quicklisp installation root: - Load the library with:
(ql:quickload :cl-gtk4)
(ql:quickload :cl-gtk4.adw)
(if you need libadwaita)(ql:quickload :cl-gtk4.webkit)
(if you need WebkitGTK)
- For GTK4 usage, please refer to GTK API reference and check out the conversion rules for these APIs.
Please note that GTK runs in a single thread and is NOT thread-safe, so all the UI-related operations must happen in GTK main event loop, which means you cannot write the code like this:
(let ((label (make-label :str "0"))
(count 0))
(bt:make-thread
(lambda ()
(loop :repeat 5
:do (setf (label-text label) (format nil "~A" (incf count)))
(sleep 1)))))
GLib provides idle_add
and timeout_add
to add a function to execute in the main event loop,
which is thread-safe so that it can be called in other threads.
cl-glib wraps idle_add
and timeout_add
, and cl-gtk4 create restarts for the handler passed for them to be invoked safely,
even when conditions are signaled.
It also provides the API for convenience:
gtk:run-in-main-event-loop
Execute the body in GTK main event loop, in which we can access the UI safely:(let ((label (make-label :str "0")) (count 0)) (bt:make-thread (lambda () (loop :repeat 5 :do (gtk:run-in-main-event-loop (setf (label-text label) (format nil "~A" (incf count)))) (sleep 1))))) ; Don't put this into `gtk:run-in-main-event-loop'
You can reload the application without closing the window constructed in define-main-window
by recompiling the define-application
macro in top-level (Simply stroke C-c C-c
if using Slime/Sly in Emacs):
The API in cl-gtk4
handles almost all possible recoverable errors by providing restarts, by which you can recover the program or safely exit the GTK application when encountering an error.
Please note that stack unwinding into FFI functions is unsafe and may cause you to be unable to run the GTK application in the current session again, or even freeze or crash the Lisp implementation.
Therefore, it is important to always use the restarts provided by cl-gtk4
or other restarts that do not unwind the stack into FFI functions to recover or exit the application.
Additionally, when you attempt to interrupt a GTK program through Slime/Sly, such as C-c C-c
in Emacs’ REPL,
remember to choose ABORT-APPLICATION
provided by cl-gtk4
instead of ABORT
provided by the Lisp implementation.
The highlighted restarts as follows, for example, are safe, while the rest perform stack unwinding and are therefore unsafe in GTK applications:
See the examples folder.
The examples are ready for being built into executable if the implementation supports :program-op
:
(asdf:operate :program-op :cl-gtk4/example)
Then you could find the executable file under the examples
folder.
Note that:
- On ECL, for unknown reason, the
:entry-point
of the ASDF system is ignored. This command should be used instead:(asdf:make-build :cl-gtk4/example :type :program :epilogue-code '(progn (uiop:symbol-call :gtk4.example :simple) (si:exit)))
- On Microsoft Windows, it’s recommended to launch your application via Dependency Walker, then the shared libraries used by your application would appear in it.
You should copy all these
.dll
files into the folder where you place the executable file. If you are using MSYS2, the folder structure might be like this:. ├── bin │ ├── gdbus.exe │ ├── libgio-2.0-0.dll │ ├── libgirepository-1.0-1.dll │ ├── libglib-2.0-0.dll │ ├── libgobject-2.0-0.dll │ ├── libgtk-4-1.dll │ ├── your_application.exe │ └── ... ├── lib │ ├── girepository-1.0 │ ├── gtk-4.0 │ └── ... └── share ├── icons └── ...
The folder
lib/girepository-1.0
is mandatory, without which your application won’t work as expected.