Search code examples
haskellconfigurationtransparencyx11xmonad

Making every window transparent with xmonad


I'd like every window to be transparent, I'm using xmonad and X11. I am not very familiar with any of Haskell, xmonad or X11.

How can I configure xmonad for that? I don't even know how to even get started.


Solution

  • To do that, you need an event hook that sets the opacity property on creation of new windows. This is what I'm using (requires xprop to be on the path):

    import XMonad
    import Graphics.X11.Xlib
    import Graphics.X11.Xlib.Extras
    import Data.Monoid
    import Data.Word
    
    setTransparentHook :: Event -> X All
    setTransparentHook ConfigureEvent{ev_event_type = createNotify, ev_window = id} = do
      setOpacity id opacity
      return (All True) where
        opacityFloat = 0.9
        opacity = floor $ fromIntegral (maxBound :: Word32) * opacityFloat
        setOpacity id op = spawn $ "xprop -id " ++ show id ++ " -f _NET_WM_WINDOW_OPACITY 32c -set _NET_WM_WINDOW_OPACITY " ++ show op
    setTransparentHook _ = return (All True)
    
    main = xmonad $ def
      { handleEventHook = setTransparentHook <+> handleEventHook def }
    

    Process

    Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime.

    In the spirit of this quote I'm describing my process of coming up with this. Note that I have very limited experience with all of Haskell, xmonad and X11.

    The initial mindset was that I needed a script that sets the opacity on all windows (which turned out not to be exactly what I needed). I know that composite managers on linux do this kind of stuff (visual effects), so I check out the man page of compton (which I'm using) via man compton.

    Searching for "opacity", I see the --opacity-rule flag, which mentions that compton-trans is recommended for this instead, so I look at its source. Trying out the script in the command line to discover that I need the window ID to set its opacity, so I look up how to do that.

    A solution was to use xwininfo. You can list all the windows using xwininfo -tree -root. I thought I needed to parse the output of that and was already looking up awk tutorials.

    Then I get the idea: "Wait, what if I just set the opacity on window creation? Xmonad is a window manager, it should definitely have the window ID!". So I look through xmonad's config options where I find the handleEventHook property. By clicking on the types Event and All I find out which imports I need (Graphics.X11.Xlib.Extras and Data.Monoid).

    Creating a skeleton for my event hook by coping the default:

    import XMonad
    import Graphics.X11.Xlib.Extras
    import Data.Monoid
    
    myEventHook :: Event -> X All
    myEventHook _ = return (All True)
    

    Now some Haskell knowledge is needed, I want to do something when Event is a window creation event. Looking through the Event docs and a bit of probing, I found out I need the ConfigureEvent where ev_event_type is createNotify while ev_window is the created window ID. To use createNotify I also import Graphics.X11.Xlib:

    import XMonad
    import Graphics.X11.Xlib
    import Graphics.X11.Xlib.Extras
    import Data.Monoid
    
    myEventHook :: Event -> X All
    myEventHook ConfigureEvent{ ev_event_type = createNotify, ev_window = id } = do
      return (All True)
    myEventHook _ = return (All True)
    

    Now what do we actually want to do? We want to spawn a shell process with the compton-trans command. There is the simple function spawn used throughout xmonad:

    myEventHook :: Event -> X All
    myEventHook ConfigureEvent{ ev_event_type = createNotify, ev_window = id } = do
      spawn $ "compton-trans -w " ++ show id ++ " 50"
      return (All True)
    myEventHook _ = return (All True)
    

    It worked! That's great but I noticed it being a bit slow and it still has the compton dependency which isn't really needed. So I look at the compton-trans source again, and see the last line that actually does the work which just uses xprop! Now this is great because I don't need all the stuff of compton-trans which does some checks and is optimized for user convenience. Using some number conversion I came up with the final version as shown above. It may even be faster to use the direct path to the executable.

    I sometimes also used the ghci ~/.xmonad/xmonad.hs to check some types and man xprop/man whatever to check the documentation.

    I hope this helps fellow xmonads to getting started! If someone has some improvements, please let me know.