So this weekend I spent more time on tiny-wlhs, there is no great success to talk about unfortunately but I will give an update because I think will provide some insight on the project.
So currently in the Main.hs we have this line
initSuccess <- FFI.c_server_init server
Which calls this FFI
foreign import ccall "server_init" c_server_init :: Ptr TinyWLServer -> IO Bool
which calls this c function in the tinywl shared lib
bool server_init(struct tinywl_server *server) {
wlr_log_init(WLR_DEBUG, NULL);
if (!initialize_wl_display(server)) return false;
if (!initialize_backend(server)) return false;
if (!initialize_renderer(server)) return false;
if (!initialize_allocator(server)) return false;
initialize_compositor(server);
initialize_output_layout(server);
initialize_scene(server);
initialize_xdg_shell(server);
initialize_cursor(server);
initialize_seat(server);
return true;
}
so focusing on initialize_compositor
void initialize_compositor(struct tinywl_server *server) {
/* wlr_compositor_create(server->wl_display, 5, server->renderer); */
wlr_subcompositor_create(server->wl_display);
wlr_data_device_manager_create(server->wl_display);
}
the intention is to comment out wlr_compositor_create and instead call it from wlhs
Unfortunately wlr_compositor_create does not exist in wlhs it should be in WLR/Types/Compositor.hsc
I will go over this later but first a slight detour on the topic of opaque pointers. In order to keep tiny-wlhs as simple as possible I have left all structs on the C side of the equation with opaque pointers being used to manage them on the Haskell side. Then to interact I created getters and setters in hsc files.
So that required creating a header file for tinywl in the tinywl share library. Then this code
#include "tinywl.h"
type TinyWLServerPtr = Ptr TinyWLServer
-- Getter for wl_display
getWlDisplay :: TinyWLServerPtr -> IO (Ptr ())
getWlDisplay ptr = #{peek struct tinywl_server, wl_display} ptr
-- Getter for renderer
getRenderer :: TinyWLServerPtr -> IO (Ptr ())
getRenderer ptr = #{peek struct tinywl_server, renderer} ptr
-- Getter for backend
getBackend :: TinyWLServerPtr -> IO (Ptr ())
getBackend ptr = #{peek struct tinywl_server, backend} ptr
-- Setter for cursor_mode
setCursorMode :: TinyWLServerPtr -> CInt -> IO ()
setCursorMode ptr mode = #{poke struct tinywl_server, cursor_mode} ptr mode
-- Getter for cursor_mode
getCursorMode :: TinyWLServerPtr -> IO CInt
getCursorMode ptr = #{peek struct tinywl_server, cursor_mode} ptr
which i crudely tested in Main.hs like this
initSuccess <- FFI.c_server_init server
if initSuccess
then do
wlr_log WLR_INFO "Server initialized successfully"
wlDisplay <- Server.getWlDisplay server
renderer <- Server.getRenderer server
backend <- Server.getBackend server
putStrLn $ "wl_display pointer: " ++ show wlDisplay
putStrLn $ "renderer pointer: " ++ show renderer
putStrLn $ "backend pointer: " ++ show backend
So originally I was creating Haskell implementations of the server struct but this led to an unending recursive process of header definitions that lead to the Wayland protocols. These protocols are designed to create headers at compile time using Wayland scanner.
So to take this path we would need a Haskell xml scanner for the Wayland protocols to be run at compile time, or to simply draw the line on the nested Haskell implementation of structs and use opaque at some arbitrary level(likely the Wayland protocol headers).
The is why I decided to leave the struct on the C side as much as possible.
So now returning to the need for wlr_compositor_create in wlhs this is what the c function looks like
struct wlr_compositor *wlr_compositor_create(struct wl_display *display,
uint32_t version, struct wlr_renderer *renderer) {
assert(version <= COMPOSITOR_VERSION);
struct wlr_compositor *compositor = calloc(1, sizeof(*compositor));
if (!compositor) {
return NULL;
}
compositor->global = wl_global_create(display, &wl_compositor_interface,
version, compositor, compositor_bind);
if (!compositor->global) {
free(compositor);
return NULL;
}
wl_signal_init(&compositor->events.new_surface);
wl_signal_init(&compositor->events.destroy);
wl_list_init(&compositor->renderer_destroy.link);
compositor->display_destroy.notify = compositor_handle_display_destroy;
wl_display_add_destroy_listener(display, &compositor->display_destroy);
wlr_compositor_set_renderer(compositor, renderer);
return compositor;
}
So I think for
struct wlr_compositor *compositor = calloc(1, sizeof(*compositor));
if (!compositor) {
return NULL;
}
It makes the sense to create a function in hsc to just create this and return a pointer.
For the rest of the function I believe all the wl_* functions exist in wlhs and are also pointers like this
foreign import capi "wayland-server-core.h wl_signal_init"
wl_signal_init :: Ptr WL_signal -> IO ()
So to try to wrap this long message up I think there is some valuable information here.
1 - wlhs does need to have Wayland headers, this can be from wayland-scanner run at compile time, the files manually uploaded or a Haskell wml parser but they are needed.
2 - I think there would be a lot of value in writing down design decisions somewhere, like what is the default approach for structs, or opaque pointers. It might be worth having a discussion on what to leave on the C side and what to implement in Haskell. There are clear benefits for the Haskell implementation (the whole reason we are doing this) but it comes at a cost, both in time to implement and likely in run time performance.
3 - wlr_compositor_create needs to be implemented in wlhs. Further on this, with the Wayland protocols there are signals and events etc its not clear if just creating a pointer to these is the best approach. We may want to implement these in Haskell.
4 - This is subjective and my opinion but when I try to use the bindings I really see the limitations in the automatic generators. I really get the appeal but I think even if we had something that could magically convert every header there would still be questions about what should be bound and what should be implemented in Haskell.
Anyway Ill probably try to create a wlhs implementation for wlr_compositor_create next week, hopefully someone else will beat me to it