As my first post related to SerenityOS development, I’m going to talk about a topic which I’m really passionate about – Python programming!
SerenityOS has a pretty decent port of the Python language, mainly due to the work done by Linus (and other contributors!). Almost every standard library module works as intended, with many of its optional dependencies also ported, like sqlite, zlib, ncurses. Fun fact: webbrowser module has official support for launching the Serenity’s Ladybird Browser.
One of such optional dependencies is libffi, which is a foreign-function interface library. Having this dependency makes the ctypes module available.
With ctypes, we can easily call C compatible functions exported by a shared library, which means
it should be possible to interface with the Serenity C++ classes, providing that we write wrapper
functions in the style of C, and use the
extern "C" declaration.
Let’s build a small “hello world” C-reni-Py (😅) program!
Creating a shared library
To create a shared library the easiest way possible, I’m going to use the same structure
as the other libraries, by creating a new directory in the
Let’s call it LibExample:
We’re going to build a really simple one-function library that sends (push-like) desktop notifications!
Nice! Now, to make the build system compile this code, it’s necessary to edit
I’m not sure if this declaration is entirely correct
(as I don’t fully understand the build system), but I found that
must be present. Otherwise, the library won’t be placed in the OS filesystem
(I assume it’s because it’s not a dependency of any application).
Userland/Libraries/CMakeLists.txt and add an
declaration. Build the system and run it. Check that there is a
Using the library from Python
Assuming that you already have the Python port installed, let’s use the library. Open a Python interpreter and type:
It crashed! (At least the error is pretty cool)
Apparently, before we can create notifications, we need to have an event loop
instantiated, which in Serenity it’s done by initializing a
I’m not entirely sure why, I think it’s related to the way GUI applications communicate
After declaring a global variable
static RefPtr<GUI::Application> app,
I’m going to initialize it in the first invocation of
notify(). Something like this:
And there we go! Displaying a notification on the SerenityOS desktop, via Python:
(I also introduced an icon file as a third parameter, so I could find a reason to show the wholesome catdog mascot in this post.)
With this approach, it’s theoretically possible to build Python bindings for LibGUI (and other components), but it’s really hard work to do that, I think. Another way to build this kind of integration is to use the CPython C API.
That’s all for today. I hope you find this post interesting in any way. x)