Dmenu is a Godsend

  • Home
  • Back
  • Because dwm is customized through editing its source code, it’s pointless to make binary packages of it. This keeps its userbase small and elitist. No novices asking stupid questions.

    https://dwm.suckless.org

    Simple Software

    In a previous article I talked a bit about software simplicity, or perhaps maybe the lack of it that exists in the world. One of my favorite parts of UNIX-like systems is the composability of command-line tools. Instead of having giant monolithic programs that do everything, you have various small domain-specific tools that all use a common interface — standard-input and -output. The following is a good example of this:

    yq -0 '.mangoes.[].quote.content' <quotes.yml \
    | shuf -zn1 \
    | tr '\0' '\n'

    I have a YAML file on my system with various fun quotes, and I like to have a quote-of-the-day that’s displayed when I open a terminal. I can make use of the yq program which is specialized in querying YAML files to extract the list of quotes from the file. I can then use the shuf program to shuffle the quotes and pick one at random, and finally I can use the tr program to translate null-bytes to newlines.

    Notice how all three of these tools are very specialized to a specific domain; they don’t try to reinvent the wheel, instead offloading the tasks they aren’t specialized to do to other tools. yq doesn’t waste its time with functionality to shuffle lists of items because you can do it with shuf instead. This mindset of composability allows you the user to create tools that are more powerful than they could have been otherwise. Instead of hoping that all your tools offer support for shuffling items and hoping that they can shuffle things in the specific way you like, you can just use the one single shuffling tool shuf everywhere.

    In my example I used shuf to pick a random quote, but I could similarly use it to pick a random song from a playlist, or to pick a random image to use as a desktop wallpaper.

    So with the composability of the UNIX environment being such a great thing, it blows my mind that people are still writing software such as kitty that try to do absolutely everything in one giant monolithic program instead of keeping focused to the task at hand and allowing the user to extend their tools to add in the functionality they so desire.

    Kitty’s unicode input screen
    Kitty’s Unicode Input

    Some of you have surely already figured out what my issue is with what Kitty has done here, and why I think this is shitty software design. For those that haven’t, allow me to ask you a question: what would be better than unicode input in your terminal?

    The answer is simple: unicode input everywhere.

    Why should you be limited to unicode input in your terminal? What if you’re texting your friend or sending an email, and want to include a unicode symbol such as ‘™’, or want to properly refer to a site you visited such as Ta’ Ħaġrat? Perhaps you even have a friend whose surname is Mäkelä. Or maybe — you just want to send your friends a middle-finger emoji sometimes.

    Wouldn’t it be nice if you had a global menu for unicode input? Well turns out it’s actually really damn easy to do yourself, and renders all that Kitty code absolutely useless.

    Writing the Script

    The first thing you’re probably going to need if you want to have your own unicode-input tool is a list of all the unicode characters out there. Luckily for you, I already went through the hell that is the Unicode website to find it for you.

    $ wget 'https://www.unicode.org/Public/UNIDATA/UnicodeData.txt'

    You can check out the file for yourself if you want. It’s got a bunch of information in it — a lot of which is really useless for our purposes. There are also a bunch of control characters and other things in there that I personally don’t care for, so those can probably be removed too. If it tickles your fancy, I’ve written a sed script to clean up the input into something a bit nicer:

    s/;[^;]*//2g
    s/\<(.)([A-Z]*)/\1\L\2/2g
    /^[^;]*;</d
    /Compatibility/d
    /Variation Selector/d
    s/[^;]*/\\\\u&/

    After processing, your unicode data file should look something like this:

    \\u0020;Space
    \\u0021;Exclamation Mark
    \\u0022;Quotation Mark
    \\u0023;Number Sign
    \\u0024;Dollar Sign
    \\u0025;Percent Sign
    \\u0026;Ampersand
    \\u0027;Apostrophe
    \\u0028;Left Parenthesis
    \\u0029;Right Parenthesis

    Now that you have your data file, we can begin scripting. The first thing we want is to get all of the names of the unicode characters. Turns out that is a very easy task thanks to the cut utility. We can split each line on a semicolon and extract the second field with a simple command, and then we can compose it together with dmenu or your preferred clone of it to present the user with a graphical list of items they can pick from:

    name="$(cut -d';' -f2 UnicodeData.txt | dmenu)"

    Notice how we can simply compose cut and dmenu together, and just assign the result to a variable. We can do this because these are sane programs that read from standard-input, perform some simple, basic task, and then print a result to standard-output. One tool to parse a file, and another tool to let the user pick a selection.

    We have the users selection now, the $name variable holds the name of the unicode character that the user selected. All that we need now is to actually get the unicode character of our choosing. Luckily this is also incredibly easy thanks to composition:

    #!/bin/sh
    
    name="$(cut -d';' -f2 UnicodeData.txt | dmenu)"
    awk -vn="$name" -F';' '$2 == n { print $1 }' UnicodeData.txt \
    | xargs printf \
    | wl-copy

    Congratulations! That entire script, which can be easily condensed down into only 2 lines of code is all you need to create a graphical interface that allows you to pick a unicode character, and then copies your selection to your clipboard — and it was all done by taking simple tools and combining them to make a greater application.

    So next time you’re developing software and want to add a new feature, just pause and think for a second. Do you need that feature? Can it be done in a better way? It is possible to generalize your feature to where it is useful outside of just your specific application? Don’t make the same mistake Kitty did. Allow me to leave you with this quote:

    The talk reviews reasons for UNIX’s popularity and shows, using UCB cat as a primary example, how UNIX has grown fat. cat isn’t for printing files with line numbers, it isn’t for compressing multiple blank lines, it’s not for looking at non-printing ASCII characters, it’s for concatenating files.

    We are reminded that ls isn’t the place for code to break a single column into multiple ones, and that mailnews shouldn’t have its own more processing or joke encryption code.

    USENIX Summer Conference Proceedings, 1983

    The Good, The Bad, and The Ugly

    I would like to take a moment to point out some examples of composability done right, and some examples of features that are actively harmful in the pursuit of the UNIX ideal.

    Ls — Bad

    The ls program is one of the most useful ones found in the UNIX environment. It has a simple job to perform and it does it well. It lists files in a directory. So what’s the issue then? Well let’s see what happens when you combine ls and cat:

    $ ls | cat
    code
    doc
    down
    mail
    media

    Ok… that looks about right. We invoke ls and we get a listing of the current directory, newline-separated. Well look at what happens when we don’t compose it with another command:

    $ ls
    code  doc  down  mail  media

    Yeah… it decided to ‘prettify’ the output by putting everything on one line. If the input contains enough items it columnates them really nicely for you. Here’s an example of what it looks like when I run ls in my screenshots folder:

    $ ls
    
    2023-08-03_18:07:58.png  2023-10-05_16:08:17.png  2023-11-18_20:54:30.png
    2023-08-04_16:17:22.png  2023-10-05_20:24:05.png  2023-11-18_21:07:07.png
    2023-08-04_17:50:39.png  2023-10-05_20:24:21.png  2023-11-18_21:07:48.png
    2023-08-05_16:05:07.png  2023-10-05_20:32:49.png  2023-11-18_21:07:57.png

    Now don’t get me wrong, I like the fact that I can see more than one item per-line. But why is this specific to ls? It could actually be really useful to be able to take arbitrary input and columnate it in such a form for easier consumption. The column program does exist on Linux systems, but it is far inferior to what ls provides. In the ideal world, ls would simply display one filename per-line with a better column command, and you could compose the two to get a nicer viewing experience for your files.

    Tabbed — Good

    What is one thing that almost all your graphical applications have in common? They all have tabs. Your web browser has tabs, your terminal probably supports tabs (and if it doesn’t, I bet you use tmux). Your code editors have tabs, and even modern email-clients have tabs. Now let me ask you: why do we always reinvent the tab?

    Suckless — the same people that brought us dmenu — also created a lesser-known application called tabbed. You can find it here. It’s quite a simple piece of software. You simply run it together with another program (such as the st terminal) and it adds tab-support to it. Not only does this reduce code-duplication, but it also is beneficial for you the software user as it means you get a consistent UI with consistent-behavior and -key-bindings wherever you go.