Haskell wlroots bindings

Just to give a more general update on Tiny-wlhs.

I did some very basic analysis using cloc to count lines of code. In TinyWl.c there are around 800 lines of actual code, of these around 300 lines are function calls (or verbs ) and 500 lines are concerned with data structures and memory( or nouns).

So I think only the 300 lines of functions will be converted to Haskell and the remaining 500 left in C. Fortunately the conversation isnt too complex its follows the pattern of what has already been done.

My assumption is the C functions will provide everything needed to interact with the structs in the code and the act of converting the C functions into Haskell will expose everything we need via Haskell interface. Assuming we had all 300 lines converted to Haskell and all the getters and setters they need ser up, then in theory someone could come along and build a compositor using only the Haskell functions and types.

The reality likely is a lot complex and more will probably be needed but its not clear what this will be until we get to that point and try to build a compositor with what we have.

1 Like

Appreciate this! Iā€™ll add to what Shane said with suggesting https://wayland-book.com/ for reference material on Wayland itself. Section 1.1 is a really good primer, in particular, but the rest can be treated as reference.

2 Likes

Iā€™m toying around with adding keybindings from the Haskell side on my ā€œhs-keybindsā€ branch, and Iā€™m running into an issue. It seems that any call to handleKeybinding function results in a crash.

I get the sense that debugging this is going to be an ordeal, to say the least. Before I go through with that, though, Iā€™d like to consider alternatives to what Iā€™m doing; as far as I know, exporting Haskell functions to C comes with some overhead, and it definitely complicates things. Fortunately, the export would go away once more code is translated, but Iā€™m just trying to go step-by-step for now.

Anyways, maybe someone else could take a look and let me know if they have any suggestions?

There is a small amount of overhead on the first call to a foreign export function within a given thread, but the result is cached unless you explicitly discard the cache so subsequent calls are fast (see 6.17. Foreign function interface (FFI) ā€” Glasgow Haskell Compiler 9.10.1 User's Guide). If this makes things easier for you, Iā€™d say go for it.

1 Like

Thanks! I clicked on your link, and I see now that I canā€™t just declare the extern myself and link against the library.

I think @bradrn mentioned that we was willing to hand over the repo to somebody. My injury is manageable now and Iā€™d like to contribute again.

I think weā€™d need to decide some things, like checking in the XML files for the protocol that we need to use to generate C headers to link with to build wlroots. I still have this outstanding PR (outstanding as in not handled), and I might have a little bit more work on top of it on my fork.

And I didnā€™t notice that we had a PR, we should get that merged too.

Wayland adoption seems to be picking up, which is exciting. (I still need to get rid of this Nvidia card and switch to AMD)

1 Like

I had a brief look at the code but nothing jumped out at me. For debugging I use gdb on the binary created by cabal. However I can attest debugging is not pleasant.

Its interesting you choose to work on the keybindings this is something I have been thinking about.

Originally I created the repo to test wlroots bindings then this morphed into simply taking the current running C from a tinywl shared library and controlling it with Haskell.

So currently we have the following


main :: IO ()
main = do
    -- Initialize logging
    wlr_log_init WLR_DEBUG nullFunPtr

    -- Add a test log message
    wlr_log WLR_INFO "Initializing TinyWL with wlhs bindings"
    

    args <- getArgs
    server <- FFI.c_server_create
    wlr_log WLR_DEBUG "Server created"

    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
            _ <- Compositor.initialize_compositor wlDisplay 5 renderer
            socket <- FFI.c_server_start server
            if socket /= nullPtr
                then do
                    socketStr <- peekCString socket
                    setEnv "WAYLAND_DISPLAY" socketStr
                    wlr_log WLR_INFO $ "WAYLAND_DISPLAY set to " ++ socketStr
                    case args of
                        ("-s":cmd:_) -> do
                            withCString cmd FFI.c_server_set_startup_command
                            wlr_log WLR_DEBUG $ "Startup command set: " ++ cmd
                        _ -> return ()
                    putStrLn $ "Running Wayland compositor on WAYLAND_DISPLAY=" ++ socketStr
                    FFI.c_server_run server
                else wlr_log WLR_ERROR "Failed to start server"
        else wlr_log WLR_ERROR "Failed to initialize server"
    FFI.c_server_destroy server
    wlr_log WLR_INFO "Server destroyed, shutting down"

I tried breaking down things like server_init, but that felt like a dead end. Manually sequencing C calls from Haskell isnā€™t very useful. Creating tons of getters/setters for C structs and then calling wlroots functions via FFI is a lot of work for not much gain.

So, what should tiny-wlhs be? The C shared library approach simplifies some things (Wayland scanner, memory management), but it limits how ā€œHaskell-yā€ the compositor can be, especially if weā€™re aiming for something like Waymonad. Itā€™s a C compositor at its core, with a Haskell control layer. That has limitations. The Haskell/C interface and debugging are also major pain points.

The alternative it to create a compositor from scratch Haskell. This to me at least is just not viable atm. It needs wlroots bindings and perhaps a bigger roadblock it needs a Haskell version of the wayland scanner. So a Haskell xml scanner to be run on each build with the latest wayland protocols, converting the protocol which is very C focused into Haskell.

So with all that in mind I think tiny-wlhs can be three things.

First, a way to test any wlroots bindings or xml scanners if created. This can help provide some peace of mind and checks for anyone working on these, and I would be more than happy to do the integrations to test anything, it might also provide a target to work towards.

Second, an educational tool. If you look back over this long thread you will see a lot of confusion about the wayland protocol and how to actually use it. This repo can provide a basic working example that can be played around with to become more familiar with the concepts. Besides that it will offer anyone who works on it directly a lot of insight of what is required from a bindings library, and about the limitations and possibilities of a c based compositor and what a Haskell implementation might look like.

And Thirdly, while support for a wayland implementation of xmonad is being worked on it can serve as a stand-in for anyone who wants to mess around with compositors in Haskell.

To the final point about getting some minimal app people can control and configure in Haskell I think the following features(for lack of a better term) are needed in a basic implementation and should be fully controllable in idiomatic Haskell.

1. Core Functionality & Lifecycle:

  • Start/Stop Server: Essential for basic control.

  • Server Configuration: Logging levels are important for debugging.

2. Output Management:

  • List Outputs: Useful for information and debugging.

  • Basic Output Configuration: Setting resolution is important for usability, but advanced multi-monitor setups can be deferred.

3. Window Management:

  • Window Placement (Tiled): Focus on basic tiling layouts (e.g., tall, wide).

  • Window Resizing/Moving (Basic): Within the constraints of the tiling layout.

  • Window Focus: Essential for interacting with windows.

  • Window Closing: A fundamental window management operation.

  • Fullscreen: A common use case.

4. Input Handling:

  • Keyboard Bindings: Crucial for controlling the compositor. Focus on keybindings for window management actions.

  • Mouse Bindings: Basic mouse clicks for window focus and interaction.

5. Application Launching & Management:

  • Application Spawning: Allowing users to launch applications is important for daily use. Defer startup applications for now.

6. Compositor Features :

  • Basic Decorations: Simple borders and titles are helpful for distinguishing windows. Advanced customization can come later.

7. Inter-Client Communication (Deferred):

  • Wayland Protocols (Advanced): This is not a priority for the initial experimental phase. Clipboard management, drag-and-drop, and other advanced protocol interactions can be explored later.

So if we think about these features instead of comprehensive bindings its actually easier to just go feature by feature and leave as much of the complexity in the C side as possible (as this is already written, or can be borrowed from projects like sway) and only pulling into a Haskell interface what we actually need for what ever feature we are trying to support.

But I would like to get peoples thoughts, its unfortunately not going to address the need for bindings or a xml scanner but I think having something configurable and controllable in Haskell should provide dividends by simply providing a way to play around with a compositor in Haskell.

So with all that being said, this will likely be my last update post on this thread, tiny-wlhs has changed its scope so is no longer relevant in this thread unless directly talking about testing any wlroots bindings. Ill likely post updates on tiny-wlhs in its own thread.

1 Like

Tiny-wlhs will be useful too I think but most people in this thread concurred that a very basic binding from Haskell to wlroots that tried to create 0 abstractions would be best starting point. Because it then could become a component of other projects more easily than it could be if wlhs tried to also provide a nicer interface than calling C functions.

For example, as far as I can tell in the Haskell world there are two major ways to architecture applications, one is building your own monad onion (which has performance implications apparently) and two is using ReaderT over IO. Iā€™m trying the latter for a web application that I am building and I like it.

The point is that compositor authors are likely to have opinions about how the compositor state should be managed, and we imagined that wlroots would be as ā€˜dumbā€™ as possible so that it could be used by any kind of project.

I think youā€™re idea about making something that actually works & runs by itself is wonderful, Iā€™m just explaining the thought process behind WLHS.

1 Like

I probably should have posted an update. Donā€™t even bother right now; itā€™s not even linking. I forgot that the make clean part of the nix shell hook wasnā€™t working and didnā€™t realize that my changes werenā€™t being reflected every time I restarted the shell :man_facepalming:.

Anyways, Iā€™ve since figured out a lot more and hope to finish up what I was doing and post about it in the next few days.

2 Likes

Yup, Iā€™m fully on board with this. If I can daily-drive it, then Iā€™ll be much happier working on it, and Iā€™m sure this goes for most people.

I think ā€œlimitationsā€ is a good way to put it. I found this idea rather unsavory, at first, but the ā€œworse is betterā€ approach may just be necessary.

I no longer agree. This idea sounded good to me on paper, but I think that Shaneā€™s effort on tiny-wlhs made me confront reality. As he said:

Wlroots is a great library, but at the end of the day itā€™s a C library designed for C programs. Iā€™m not sure what a good C library designed for Haskell programs looks like, but I think that we can build on the steps that Shane outlined in order to figure that out.

2 Likes