Pimp Your XMonad #4: Urgency Hooks

March 14, 2009

In a traditional desktop environment such as Gnome or KDE, one has a Windows-style taskbar and tray, with small icons for various apps. When an app wants your attention, it generally changes its tray icon, and might highlight or flash the taskbar entry. In a pure xmonad setup, one typically has neither a taskbar nor a tray, so how do you know when someone addresses you on IRC, or sends a Pidgin message?

Xmonad offers a solution, the XMonad.Hooks.UrgencyHook module. This module offers a variety of ways for xmonad to notify you that an app is requesting your attention, and some ways for you to respond.

XMonad Setup
When an app sets the X WM_URGENT option (see below on how to configure this in some common apps), UrgencyHook will notify you. Precisely how it does so is up to you. One uncommon choice is popping up a temporary dzen2 window, which I won’t be covering here (see the XMonad.Hooks.UrgencyHook docs). By far the most common technique is highlighting the workspace in your dzen or xmobar workspace list.

To start, import XMonad.Hooks.UrgencyHook. Then your xmonad defaultConfig { ... } line changes to: xmonad $ withUrgencyHook NoUrgencyHook defaultConfig { ... }. If you run the darcs version of xmonad and use the dzen function or dzenPP for your logHook you’re all set! Both of those functions set up dzen to show urgency by highlighting the workspace with the urgent window.

If you use the xmobar or xmobarPP functions, or want custom colours for your dzen, a little more work is required. You probably have a logHook defined, and it probably uses xmobarPP or dzenPP. If not, see the documentation for XMonad.Hooks.DynamicLog, that’s outside of the scope of this article.

You’ll have to add the field ppUrgent = xmobarColor "yellow" "red" . xmobarStrip to your xmobarPP for xmobar, or ppUrgent = dzenColor "yellow" "red" . dzenStrip for dzen. Of course, you can choose whatever colours you like.

N.B. The dzenStrip and xmobarStrip functions only exist in darcs. If you don’t run darcs xmonad, you can get the code from here and paste it into your xmonad.hs.

A Handy Keybinding: focusUrgent
XMonad.Hooks.UrgencyHook contains a useful keybinding function, focusUrgent, which takes no arguments and focuses the most recently urgent window.

Configuring Pidgin
The second part of setting up UrgencyHook is configuring apps to set WM_URGENT. To do this for Pidgin, you need to enable the Message Notificaton plugin (included with Pidgin), and in the plugin configuration tick the “Set window manager ‘URGENT’ hint” option.

Configuring urxvt
Another common thing to want highlighting for is when someone uses your name in IRC. To make urxvt set urgent on a bell, add the line URxvt.urgentOnBell: true to your ~/.Xdefaults.

Of course, you’ll also have to set your IRC client to trigger a bell on highlight (in irssi: /set bell_beeps ON and /set beep_msg_level MSGS NOTICES DCC DCCMSGS HILIGHT). If you run it in screen, make sure screen isn’t set to use visual bell either.

The XMonad.Hooks.UrgencyHook docs also have information on setting up these apps and others. If you use a different terminal or IRC client, Google knows how to set it up for urgency hints.

UrgencyHook in Action
Here’s a screenshot of me hard at work on xmonad, with UrgencyHook notifying me that I’ve received a Pidgin message on workspace 2: IM.

P.S. Twitter
I’m now on Twitter as bradenshep.


Pimp Your XMonad #3: Prompt

November 29, 2008

This is a big post that I’ve been putting off for a long time. There are a lot of awesome modules in the XMonad.Prompt.* family, and so there’s a lot to cover here.

XMonad.Prompt itself defines a library for displaying prompts to the user, with autocompletion and customizable look and feel. This module is a back-end library, not intended for use in configurations. What follows is a description and use cases for what I consider the most important modules in the Prompt family, a breakdown of how to customize the look and feel, and a quick rundown of the remaining Prompt modules as of 0.8.

Binding a Prompt action to a key
Nearly all Prompt actions are performed using key bindings. You can bind them to whichever keys you wish, and they all take the same basic form. First, you need to import the relevant module, for example

import XMonad.Prompt.Shell

Second, you need to create a keybinding to call a function from it

( modMask conf .|. xK_s, shellPrompt myXPConfig )

or using XMonad.Util.EZConfig (which will be the subject of a future PYXM)

("M-s", shellPrompt myXPConfig)

The myXPConfig argument is how you customize the look and feel of the prompts. See the section called “Look and Feel Customization” below on how to do this.

Running Commands and Launching Applications: Shell, RunOrRaise, DirExec
One obvious application of Prompt is using it to run shell commands and launch applications. XMonad.Prompt.Shell.shellPrompt works similarly to dmenu, though it allows command-line arguments to be provided as well, and more complete look-and-feel customization.

XMonad.Prompt.RunOrRaise.runOrRaisePrompt uses the XMonad.Actions.RunOrRaise functionality to prompt for an application, launch it if it isn’t running, and summon it if it is.

XMonad.Prompt.DirExec.dirExecPrompt can be used when you want to run programs from a directory not necessarily in your path.

Navigating by Window Titles: Window
XMonad.Prompt.Window‘s windowPromptGoto and windowPromptBring functions present an autocompleting list of window titles for all the windows in your session, and either take you to the window you name, or bring it to your location. This can be very helpful both for tracking down windows you’ve misplaced, and for bringing remote windows here without going to find them.

Minimally Disruptive Note-Taking: AppendFile
XMonad.Prompt.AppendFile is my personal favourite find from my research for this article. That might not be fair to Shell and Window, I knew they were there so they weren’t “finds”. What AppendFile does is prompt you for one line of text, which it then appends to a file named in your xmonad.hs. The intended purpose (there are probably others) is to be able to jot down quick notes by hitting a key binding and typing one line, without disrupting whatever other task you were doing.

Arbitrary XMonad Actions: XMonad
XMonad.Prompt.XMonad can be viewed as an extension of your key bindings. It displays an autocompleting list of actions, each action bound to an arbitrary X (), like a key binding. You can use the provided default bindings using xmonadPrompt (which seem to correspond to roughly the same actions as the default key bindings) or create your own list of (name,action) pairs with xmonadPromptC.

Look and Feel Customization: XPConfig
The XPConfig data type is defined in XMonad.Prompt, and it defines the various colours and other parameters that determine the appearance, position, font, size and behaviour of the Prompt box. Following is a rundown of the parameters and what they control.

  • font — a String naming a font in the X format, eg. "-*-terminus-*-*-*-*-16-*-*-*-*-*-*-*" (default: "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*")
  • fgColor,bgColorStrings in "#rrggbb" format, defining the fore- and background colours. (defaults: "#333333" and "#FFFFFF")
  • fgHLight,bgHLightStrings as above, defining the highlight colour for a completion entry. (defaults: "#000000" and "#BBBBBB")
  • borderColor — a String as above, defining the border colour. (default: "#FFFFFF")
  • promptBorderWidth — the border width in pixels. (default: 1)
  • position — either Top or Bottom, the constructors of the XPPosition data type. (default: Bottom)
  • height — the height of the Prompt bar in pixels (make sure it’s large enough for your font!) (default: 18)
  • historySize — the number of history entries to save. (default: 256)
  • defaultText — a String defining the text initially in the Prompt box when it opens. (default: "")
  • autoComplete — a Maybe Int that controls the autocompletion behaviour. If Nothing, you must press Enter to select the entry. If Just x, it will select a unique entry after x microseconds. For example, Just 1000000 would wait 1 full second. (default: Nothing)

Remaining modules
These modules are well worth mentioning but are straightforward enough to just list quickly:

  • Workspace — takes a String -> X () function and gives it the name of the workspace you specify in the prompt. Could be used to focus the named workspace, bring all the windows on it to the current workspace, or any other action.
  • Ssh — given a host name in the prompt, creates an SSH session in a terminal.
  • Man — displays the man page for the subject you provide in the prompt.
  • AppLauncher — functionality similar to dmenu.

Conclusion
The XMonad.Prompt.* family is a prime example of the awesome functionality just waiting to be discovered. Depending on how they use XMonad, I have seen Ssh, Window and RunOrRaise all make an xmonad user’s day.


Pimp Your XMonad #2: SmartBorders

November 15, 2008

This is a quick one. The focused window border is sometimes unnecessary, particularly when using Full or Tabbed layouts, since only one window is visible. XMonad.Layout.NoBorders was built to fix this.

The simplest option is the noBorders modifier, which will simply never show the border on the layouts it is applied to. It is typically applied thus:

layoutHook = ... ||| noBorders Full ||| ... ||| noBorders Tabbed ||| ...

Using it on a layout that shows multiple windows, such as Tall, would be a bad idea. But what about Tall with only one window? There is also the smartBorders modifier that will hide the borders under either of two conditions:

  • There is only one screen and only one window. (Note that on a Xinerama setup, this will never be true.)
  • A floating window covers the entire screen (great for mplayer, probably the largest use case)

Since smartBorders handles all layouts, even those that can have multiple windows, it is best applied to your whole layoutHook:

layoutHook = avoidStruts $ ... $ smartBorders $ ... ||| tiled ||| ...

Adding to core
It has been argued that SmartBorders should be in core, and even be the default behaviour. The main argument against this is that it could be confusing, and that it is rather straightforward to set up. For now, setting up smartBorders is easy, and improves the fullscreen video experience.


Pimp Your XMonad #1: Status bars

November 2, 2008

I’ve been hanging around in #xmonad for a long time, and two things have become clear. First, we answer a certain subset of questions many times. Most of the answers are in the FAQ, but the reality is that #xmonad tends to be more efficient, and it gives the answerers a means of procrastination more useful to society than reading whatever has hit reddit this minute.

Second, some relatively new user whose config is as he wants it will be hanging around in the channel when another new user asks if some feature exists. The first user says “I never even thought of something like this!” So I conceived the idea which eventually turned into this series of posts, a place for new users to discover features they never knew they were lacking.

Status bars

This arguably doesn’t belong, since it’s unlikely any user is going to reel in their jaw and say “I can get it to show me a list of workspaces?!” Don’t get me wrong, it’s an awesome feature, and I would get lost all the time without it, especially on a dual-head setup. But it might not belong since it’s a relatively easy-to-conceive feature.

Why should you use a status bar? Well, it will list all your currently populated workspaces (meaning those with at least one window on it, as well as those currently visible whether or not they have windows on), which can help with the “Oh, I forgot I sent this to workspace 8 six hours ago” problem. Using one of the pretty-printers (available for xmobar and dzen, with customizable behaviour) It highlights the currently focused workspace, as well as the other visible workspaces under a Xinerama setup. Also shown are the current layout, handy to avoid the “I forgot about these other windows because I was in Full” problem; and the window title, handy for the subtle jokes of some web comics, and… well I actually turned that one off.

Which status bar app?

xmonad users generally pick one of two: dzen2 and xmobar. The former is a general-purpose Do-It-Yourself status bar program, with sophisticated color and formatting directives that can be inlined in the input. It also supports XFT fonts and XPM icons. However, it is only an engine for displaying the input; you must write a program to generate the necessary input yourself. If that program is xmonad, the logHook in xmonad.hs, and XMonad.Log.DynamicLog‘s dynamicLogWithPP and dzenPP functions make that easy. If you want time, CPU usage, memory usage, etc., you’ll need to write your own script, with help from the dzen wiki.

xmobar, as the name hints, was designed for use with xmonad. Since xmonad doesn’t have any parts that run periodically, it can’t output a reliable time, or fresh CPU/memory/disk/network data to any status bar app. xmobar handles this itself, allowing the user to specify in a config file what information they want, and in what format it should appear.

Setting it up

With future articles, I intend to describe in more detail how to set up and get full use out of the features I’m discussing. In this case, though, the question is so frequently asked and answered in #xmonad that I’m going to punt. Either see John Goerzen’s excellent guide to setting up xmonad for its xmobar sections, or see that and replace xmobarPP with dzenPP, and remove the sTitle line from the example xmonad.hs seen there.


xmonad-light 0.8 Released

September 17, 2008

Following the recent release of xmonad 0.8, xmonad-light has a 0.8 version out. This version of xmonad-light is built against the xmonad 0.8 release version. The binary (i686) distribution can be downloaded here.

New Features

As I said in my previous post, I have implemented ManageDocks. Also implemented is defaultKeys, which can be set to False to ignore the default key bindings and define them all yourself.

The --to-haskell command-line option will output Haskell code for an xmonad.hs equivalent to your xmonad.conf.

Binary Distributions

The binary distributions do not require GHC or xmonad to already be installed.

They include a README describing usage and xmonad.conf syntax, an INSTALL file, and a TODO with future improvements. I duplicate these below.

Distro Packages

The whole point of xmonad-light is to lower the bar to trying out xmonad by removing the GHC dependency and making it much more lightweight. Anyone looking to package xmonad-light for their distro is invited and encouraged to do so. Comment on this post or email me if you need my support with any part of it.

Future Features

In no particular order:

  • An xmonad-like restart system that keeps running with the current configuration on a restart that fails to compile the xmonad.hs or parse the xmonad.conf.
  • xmessage error popups and use of the ~/.xmonad/xmonad.errors file.
  • logHook support, for output to dzen and xmobar.
  • more layouts and other contrib modules

I will be working on all of these, but patches are always welcome.

Bugs

There are almost certainly bugs in xmonad-light. I have been testing it, of course, but I haven’t really used it. Real use by actual users will, I’m sure, expose some bugs. Emails to the xmonad mailing list with bugs and feature requests will come to my attention, and I will endeavour to fix them or implement them.

Comments

Any comments, suggestions, gripes, “it would be nice if”, or other such are definitely welcome.

Contributing

Throughout this post I say “me”. I’m the sole developer of the xmonad-light project only by circumstance and not by desire. I would welcome any patches, and if you want to hack away on xmonad-light you’re most welcome. If you think I’m doing it all wrong, come to #xmonad or the xmonad mailing list and say so!


Announcing xmonad-light

August 28, 2008

After further work on PlainConfig, and in the light of the impending 0.8 release, it was decided that PlainConfig really doesn’t belong in xmonad-contrib. No one is going to use PlainConfig from their xmonad.hs, that completely misses the point. Cabal doesn’t provide an easy parallel to having make for ordinary contrib and make plainconfig for a PlainConfig-ready binary.

Considering all this, it would be far simpler to have the PlainConfig functionality as a separate Cabalized project with the binary target, that can be configured using ~/.xmonad/xmonad.conf and doesn’t depend on GHC for configuration.

With that, xmonad-light was born. It bears only some resemblance to the XMonad.Config.PlainConfig module currently in xmonad-contrib. I had modified it heavily in my local repository before splitting it into xmonad-light, and never pushed it due to the 0.8 release freeze.

Misconceptions

To be perfectly clear, let me explain a few things xmonad-light is, and is not.

Most importantly, xmonad-light is not a fork of xmonad! It depends on xmonad and xmonad-contrib for building. Rather it is an alternative configuration framework for xmonad, and will be distributed as a binary which has been statically linked to xmonad and xmonad-contrib.

xmonad-light is not a sign that xmonad has crippled its configuration, or has moved away from Haskell configurations. The goal of xmonad-light is not to replace Haskell configurations, but to provide an alternative with a much more gradual learning curve. The xmonad.conf to xmonad.hs compiler (see below) makes the transition to a more powerful config easy. This is almost inevitable given the often-seen ramp-up in users’ desire for customization.

xmonad-light is a standalone binary, that does not depend on GHC, xmonad or xmonad-contrib to run on a user’s machine. It has been statically linked against a version of xmonad and xmonad-contrib, namely whatever version was registered with ghc-pkg when xmonad-light was built.

When xmonad-light is executed, it will look for ~/.xmonad/xmonad.hs. Yes, the Haskell file. If it exists, xmonad-light will try to execute it as xmonad normally does. If this fails (GHC error, GHC doesn’t exist, xmonad not installed/registered, etc.) or the xmonad.hs doesn’t exist, it then looks for ~/.xmonad/xmonad.conf and uses that. If neither file exists, it will run the default xmonad config.

When moving to Haskell configuration files, users will need to install xmonad, xmonad-contrib, and GHC normally, as they do now to install and configure xmonad. It will be possible (due to the sequence described above) to mod+q from xmonad-light to normal xmonad.

Current Features

xmonad-light currently supports everything PlainConfig did, which is to say basic configuration of most of the parts of an xmonad.hs. It’s easier to list the omissions and caveats, which are:

  • Layouts are selected from a predefined list, which currently includes only the standard three.
  • Any key sequence, including submaps, can be bound using EZConfig syntax, but the actions are selected from a list. This list includes all the actions required to reproduce the defaults, and can spawn anything.
  • ManageHook conditions are fully expressive, but the actions are selected only from doIgnore, doFloat and doShift "workspace".
  • No support for logHooks.
  • No support for ManageDocks.

The former three are easily expanded to include more layouts and actions as necessary. The latter two are both on my to-do list before releasing a first version of xmonad-light.

The other notable feature is that the compiler from xmonad.conf to xmonad.hs is complete, and supports all the same features as the main part of xmonad-light. Testing (calling it thorough would be to describe having an umbrella as being prepared for a hurricane) has shown that using the generated xmonad.hs works (ie. ghc accepts it) and that the resulting configuration appears to match the xmonad.conf. This functionality will be accessed with a command-line option to the xmonad-light binary.

Usability and Stability

xmonad-light is usabile in the sense that it works, and one could actually use it to configure xmonad without GHC. It is not usable in the sense that there’s no easy way to get and install it yet. There are no known bugs in the code.

xmonad-light is stable in the sense that the API (the xmonad.conf format) is likely to change only by addition of new features. It is unstable in the sense that code and API changes are coming (see below for features I plan to add imminently and in the longer term).

To-Do

In roughly descending order of priority:

  • Support for ManageDocks.
  • Support for logHooks (probably just basic dzenPP, xmobarPP).
  • Actually add the --to-haskell command-line option.
  • Release a version guaranteed to work against the 0.8 release of xmonad.
  • Add more layouts, key actions and ManageHook actions as they are desired by users.

Contributing

This has so far been my baby, but I would welcome input (in the form of comments or code) on the design or implementation. The code is available as a darcs repository:

darcs get http://code.haskell.org/~shepheb/xmonad-light

The one part of the code that bothers me is the seemingly large amount of duplication, with often nearly identical functions for the XConfig output and the Haskell code output. However, I can’t see a way to do it more generally.

Conclusion

I’m off to add ManageDocks support.


XMonad for the Masses?

July 26, 2008

I’ve been an xmonad user since November, and a contrib developer since December. I love it, and really miss it when I have to use Windows or a friend’s Linux machine. I’ve introduced several classmates to it, and the ones who use Linux regularly use xmonad as well.

One of the best features of xmonad is that the configuration file is a Haskell source file. This allows for great extensibility, flexibility and control of one’s setup.

One of the great weaknesses of xmonad is that the configuration file is a Haskell source file. This means that a user must have a working GHC 6.8 (does 6.6 still work?) installed in order to change their xmonad configuration.

For someone already involved in Haskell, this is not a problem, of course. For someone looking for a good tiling window manager, GHC is a dependency of the heaviest sort. It totals some 150MB of disk on Ubuntu Hardy, for example.

To most people that’s nothing as far as disk space itself goes; gigabytes of storage are cheap and plentiful, even on laptops. Except for the new breed of EEE PCs and similar! The ritziest models of these have 8GB, most have 4GB. Now GHC is starting to be actually expensive.

Above and beyond the actual disk space cost is the psychological cost of a big dependency. It hardly fits the presentation of xmonad as a minimalist window manager to require the installation a complete compiler toolchain.

There are blog posts and mailing list posts about the problem, and a few days ago a user on #xmonad balked at the problem, though the combined weight of Don Stewart, Spencer Janssen and I persuaded him to try it anyway and he seems well pleased with the investment of space.

That last one really drives home the point: GHC is an initial hurdle only; once a user has discovered how awesome xmonad is, they will gladly sacrifice the space for it.

Strictly speaking, xmonad doesn’t require GHC, you can run GHC’s binaries without the compiler present. For xmonad, however, this is extremely limiting, as a user cannot even change the mod key without recompiling their xmonad.hs and therefore requiring GHC.

And so we are left with a conundrum. Certainly we do not wish to sacrifice the flexibility and power of using Haskell as a configuration language. So the solution, if any, lies in improving the situation for the user who does not have GHC. Since he current has no options at all, any alternatives are an improvement. I have three ideas for consideration. All are discussed in detail below.

  1. An archive of precompiled configuration files alongside the config archive on the wiki.
  2. A web service that could receive an xmonad.hs, compile it, and return the binary for download.
  3. A supplementary plain-text config file, that a precompiled xmonad.hs could parse, generating an XConfig and passing that to the xmonad function.

1. Config Archive
This solution requires the least effort, just a bit of web hosting space. It’s also the most limiting, since users can’t mix and match, just select from the existing models. Better than nothing, but not by much. I don’t have much more to say about it, I support the below ideas more.

2. Web Service
This solution is the most flexible, allowing users to have their cake and eat it too: the full power of Haskell configurations without the cost of GHC. This would be the approach I would support, except that there are serious challenges here. Template Haskell allows arbitrary execution at compile-time, there’s an infinite loop in the typechecker. There might be other bugs to allow arbitrary compile-time execution even if we could disable TH. Finally, we need a box to run this on, and no one volunteered.

3. PlainConfig
This option is actually implemented already, at least in proof-of-concept form. I wrote XMonad.Config.PlainConfig in April to demonstrate that this idea could work, and could really run with a precompiled xmonad.hs and without GHC. It works, though of course it is far more limiting than a Haskell configuration. Key bindings are done using EZConfig, and drawing from a list of predefined actions. Layouts are selected from a predefined list as well. Workspaces have arbitrary labels, and as many as you like (though no list comprehensions for the new key bindings!). Mod and NumLock keys can be set freely, as can the colours, border width, terminal and focus-follows-mouse. ManageHooks use a parser from Roman Cheplyaka to handle arbitrary resource, className, and title queries, and the two standard actions doFloat and doIgnore. doShift wasn’t in core when I wrote PlainConfig, but it is very easy to add.

Similarly, it would be easy to add more layouts and keybinding actions, they are just entries in a Data.Map String Whatever. The two most common configuration elements that are missing are logHooks for status bars and layoutHook modifiers like avoidStruts. The latter is straightforward to add (there are only a few of them, after all), and the former is only slightly more difficult, though it doesn’t allow a lot of flexibility.

The Kicker

From the relative volume of those three possiblities, it should be fairly clear which I think is the most practical approach to lightweight xmonad configuration. PlainConfig works, and was designed to be easily extended to add more contrib modules (ManageDocks, EwmhDesktops, layouts), more keybinding actions (CopyWindow, Prompt).

The next key piece of the puzzle is the idea of nightly builds. It’s been a long time since the 0.7 release, and contrib has been zipping ahead as ever. Not everyone wants to install darcs, check out xmonad, and build xmonad from the darcs source just to get at the new modules and changes. A system of nightly builds would allow these users to stay up-to-date and take advantage of the active development, without needing darcs or indeed, GHC.

It would be straightforward to provide two distinct builds every night. The first would be xmonad as packaged presently for Debian, Arch et al., using the stock config and with no xmonad.hs. Alongside this, however, could be a version which includes the prebuilt xmonad.hs that parses the user’s xmonad.conf using PlainConfig.

And the Gateway Drug

Inevitably, many PlainConfig users are going to want something it can’t provide, and can’t easily be modified to provide. To avoid making this a dilemma between improved configuration power and having to rewrite their existing config into an xmonad.hs, I plan to write a “cross-compiler” from xmonad.conf files to an equivalent xmonad.hs which can be a drop-in replacement.

Concerning Evangelism

During discussion of the web compilation idea on the mailing list, there was a rather emphatic post cursing the drive in others for evangelising open source projects, since it just invites a load of clueless newbies who were “attracted to the *marketing* instead of the functionality”. I certainly try to avoid being a zealot, and readily suggest to users taking exception to fundamental aspects of xmonad that they consider one of the alternative WMs. On the other hand, more users for xmonad means more potential contributors, more people checking bugs, new sources of ideas (whether they can code them personally or not) and more buzz for Haskell. I consider all of these worthy goals, and so if adding a feature like GHC-less configuration to xmonad makes me an evangelist, I will bear that label gladly.

In the spirit of “patches welcome”, I plan to work on the “cross-compiler” and nightly builds, both normal and PlainConfig-ready, in the next week. I will donate my own desktop to the cause as a build box until an alternative can be found. I hope that once this system is in place, we can manage to put out the word that xmonad doesn’t require GHC anymore loudly enough to overcome the inertia. Perhaps more importantly, to kill the untruth that one must learn Haskell to use xmonad! There are unfortunately numerous blog posts out there that compare tiling WMs and cite one or both as a disadvantage of xmonad. The latter is particularly insidious, and though xmonad users have said in many places that this was simply not true, that far more users have zero Haskell knowledge than have some, it persists.

Of course, the reverse rumour, that xmonad has crippled their configuration, that Haskell isn’t a good language for configuration, will need a strong pre-emptive strike. It needs to be blindingly obvious that both options for configuration are still supported!


Follow

Get every new post delivered to your Inbox.