Haskell wlroots bindings

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.

2 Likes

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

I’ve got something basic working, but it took me a while to figure out how to get everything to link properly. I’m not familiar with any of the intricacies of cabal, so I ended up modifying the Makefile. Even then, I’ve got little idea of what GHC is doing behind the scenes, so some of what I had to do to get this working is still spooky to me. I did spend a fair bit of time trying to get this working with cabal, but I’ve given up at this point; help would be appreciated.

As an aside, this is pretty clearly off-topic at this point. @l-Shane-l, I could start a new thread next time, if you don’t mind. If you’d rather write the OP, would you mind starting it soon?

1 Like

I created a thread here Tiny-wlhs, a hybrid haskell and C Wayland compositor

We probably should set up a discord or IRC or something, somewhere to ask questions and help each other.

2 Likes

@bradrn Hey Brad. Do you think I could become the maintainer of wlhs? I want to continue writing the bindings as we were, and I’m not opposed to just using the already-existing wayland scanner, even if it means checking in XML files and having an extra build step.

What do you need me to do?

We have two outstanding PRs, and it says " Only those with write access to this repository can merge pull requests."

I know my PR wasn’t merged because the project was likely to go in another direction, but if I’ve continued adding things in on my fork here and there.

I wasn’t sure if I should take up the WLHS repo, or if I should hard fork it and continue working.

I’d need write access at least.

I’ve thought about this once or twice but I’m not sure. What does everyone else think?

(I’d be happy to give you write access, but if other people want a fork that might be easier.)

I’m not sure what the point of a fork would be unless you plan on updating your version of the repo separately. I think that it’s fine to just give write access.

I logged in to just cheer this effort on. I’m currently stuck using gnome because x11 won’t start and I already miss XMonad so much. I want it to have a bright future, and I have to admit that wayland is a little bit smoother in certain aspects, it doesn’t have any tearing issues for example. Thank you so much to everyone that is putting in effort to making xmonad work on wayland. I can’t wait for this to be work, I’ve been periodically checking in to see if there is progress I’m happy I found this thread. Please continue to work hard!

4 Likes

I should say that on this thread but I’ve ditch a few weeks ago my 10years old XMonad config for gnome PaperWM - GNOME Shell Extensions it is pretty good. It is not quite a tiling WM but solves lots of the same problem in an interesting way (basically by sticking all window next to each other).
I’m sure this model could be added to XMonad as a new layout.

@l-Shane-l Are there any library functions from WLRoots that you’d like to have done sooner? I’m just looking at Sway code and then picking wlroots modules that look important and implementing those.

I can’t find a definitive “this is the public API” of wlroots, but I think I should focus on the include/wlr/types/* directory.

2 Likes

My wish list looks something like

  1. A haskell Wayland Scanner
  2. Bindings to wlroots so all the functions starting in wlr_
  3. An up to date set of bindings to xkbcommon

But there is nothing I’m waiting on or holding me up if that is what your asking? its actually the opposite, it would be good to see bindings to functions I already used so they could be tested quickly.

So far only the logging is actually being used from wlhs and that is something i implemented in my own fork and I think is still sitting in a PR.

One thing you could do is open the src directory in tiny-wlhs and do a rip grep for all the functions starting with wlr_

I think these would be the best to start with, and would be very easy to test.

It might also be worth taking a look at hsroots there is a lot of interesting information in it. Like for example it uses the old haskell-xkbcommon bindings and I think I saw some code for a wayland scanner in it.

1 Like