home

Comk: Graphical Menus In OCaml

Re-Introducing the Comk library:
Making single-key and dmenu-like fast graphical menus with an OCaml EDSL.

Interactive Menu UIs

Have you ever tried dmenu?

It's on all major distros; you can do some cool user-workflows with graphical but keyboard-oriented menus:

file=$(ls ~/Desktop | dmenu -f -l 10 -p "Pick a file:")
# Opens a graphical menu, lets you select a file, and returns its name:
echo "The file: $file"

The thing is, dmenu uses “fuzzy search” which is nice for discoverability but for repetitive tasks it requires way too many keystrokes and way too much thinking (“which minimal string will be the one matching best?”).

I want something like dmenu but that speeds up the most commonly walked path using Vim-like single-key sequences; and that retains “fuzzy-search” when needed or as a fall-back.

Configuring global shortcuts is often something that window managers can do. It turns out, I change WMs way more often than I want to change my basic workflows. Moreover most WMs do not support key sequences properly, visual feedback, or falling back to dmenu-style search.

Enter Comk

Comk, “Configurable Menus for Keyboards,” is on Gitlab: smondet/comk.

It uses Wall and tsdl to draw menus on SDL+OpenGL windows.

One can quickly try out with dune utop src/lib/:

(* Defining the specification: *)
let spec =
  Comk.Interspec.(
    make [
      item ~key:'a' ~matching:"hello" "Hello!"
        (effect (fun () ->
             ignore (Sys.command "nohup xmessage -center 'Hellow World' &")))
    ; item ~key:'b' "Go to sub-menu"
       (fun _ -> menu [
           item ~matching:"thing-to-match" "This one is a no op"
              (effect (fun () -> print_string "done"))
       ])
    ]) ;;
(* Start the UI: *)
let () =
  Comk.Display.with_sdl ~text_size:30. spec ;;

Below are screenshots obtained with the above code, first chosing the 'a' entry:

GIF using the 'a' key in the code example.

… then with the sub-menu ('b'):

GIF using the 'b' key to open the sub-menu.

The module Comk.Interspec.Example (in the library!) provides some examples to build upon. The example application is actually just a Cmdliner “main” wrapped around that module, cf. main.ml.

My Usage

It started as an exercise to try the Wall library; I have been using this for ≥ 2 years! From a global, WM-level, keyboard shortcut I can be:

  • Taking various kinds of screenshots.
  • Manipulating xrandr custom “profiles” (e.g. manipulating external monitors for presentations, working with code, …).
  • Starting various GUI programs.
  • Putting 2FA codes in the clipboard (oathtool --totp ... | xlcip -sel clip).
  • Running various note-taking commands (saving screenshot, URLs, starting new notes in Emacs, …).
  • Starting terminals with custom Themes/profiles and pre-defined ssh connections or dune utop ... calls.
  • “Caffeinating” the laptop (SDL has a function to prevent screen-saving).
  • Starting Jack-audio multi-application setups.
  • Manipulating many compartmentalized browser instances (which will deserve their own blog-post one day, see option --user-data-dir for Chromium).

Things To Do

If there is some interest (and this is a call for help 😼) this could be packaged a bit better (incl. opam).

First, we would need to clean up the graphical design (right now there is even some distracting debug output!), make mouse support work, and fix a few issues.

Then, we should find ways of speeding up the start-up of menu-applications (in my case from about 100 ms to non-perceptible), it might be only a WM problem but things could be done at the application level, e.g. running a specific “display server” for Wall (@let-def's idea).

After 8 years of blograstination, this is post #1 of my attempt at not getting too fast lagging behind on the #100DaysToOffload “challenge” … Let's see where this goes.