I have long tossed around a handful of terminal multiplexers - screen, tmux, and dvtm mainly - as a means to the end of multitasking on the terminal. They all have their problems - primarily when it comes to scrolling (awkward modal methods) and mouse reporting1. Somewhere, during my recent week of no internet, I came to several intertwined realizations…
At the core of this web of realizations is the notion that (from a user's standpoint), there is 'multitasking,' and there is 'simultasking.' Simultasking is my own term, and it wouldn't surprise me if others have discussed this paradigm before, and named it better than I ever could. Regardless, that's the term we have to deal with for the rest of this article. Simultasking is a subset of multitasking - it's the branch of multitasking that requires simultaneous (or near-simultaneous) interaction with the multiple tasks at hand. Multitasking is playing a little bit of Zork after you finish writing a paragraph, simultasking is playing around in interactive Ruby as you read a Ruby tutorial. Multitasking is keeping the fish documentation on hand as you write a fish script (just in case), simultasking is holding a conversation in naim while fiddling with fermat.
These usage paradigms can be extended to UI paradigms. In screen and other programs, multitasking is the notion of a 'window,' simultasking a 'split.' In a typical GUI, multitasking is a 'tab,' simultasking (possibly) a 'window.' I mused briefly on the subject previously, in reference to the then-hypothetical, now-real iOS multitasking. Looking at how multitasking will manifest itself in iOS 4, we can see that the primary means of multitasking is just that - multitasking. The special software that gets granted background privileges is simultasking - reading a book while listening to Pandora. It could even be argued that (from the standpoint of the user, not the machine) through its simple and relatively snappy launching interface, iPhone multitasked all along, but never simultasked (aside from Apple's own software).
So, my realization was that I have two very different use-cases (multitasking and simultasking), and one solution may not be ideal for each case. At this point, I feel that I should have picked a better word for simultasking (which, don't forget, is a subset of multitasking), because I'm about to introduce a third concept - the task. A task, to me, is specifically that which constitutes one instance of multitasking. So, that full-screen Zork is one task, and that single screen split into Ruby and the Ruby tutorial is also just one task. Thus, I could be simultasking my Ruby education, and after every lesson, I could reward myself by multitasking and playing a bit of Zork. For the simultasks (those tasks made from simultasking), I need something like dvtm to allow me two sessions on the same screen. But, for tasks, I have come to another important realization - *nix built-in job control is enough.
Every one of the aforementioned multiplexers supports this notion in some way or another, of course. Hell, in dvtm with mouse support, I can just double-click like a madman to reconfigure my workspace from task to simultask. But just because these applications support task-mode doesn't mean that I need any of them, when *nix job control works so elegantly. I've seen talk on *nix usergroups before where the very idea of using job control is considered laughable. But I find it almost beautiful in its elegance. One quick touch of ctrl-z backgrounds a process and gives control back to the shell. The (shell) builtin 'jobs' presents the user with a list of active jobs, and the (again, shell) builtin 'fg' brings a job back to the foreground. If simultasking becomes necessary, one of those jobs could be an instance of dvtm wrapped inside a dtach session.
Realization three, then, is that I can make this just slightly more pleasant for myself while still not dealing with always running a multiplexer. Now, at this point it's important to mention that my shell of choice is fish2, because that which follows is currently only valid for fish, but should be easily enough translated to other shells. The first stage in making things easier for myself is already complete - a simple modification to the default 'fish_prompt' function which handles the drawing of the prompt. The following gets added toward the beginning:
set __fish_jobs_num (count (jobs))
if test $__fish_jobs_num -gt 0
set -g __fish_jobs (set_color normal)(set_color --bold) "$__fish_jobs_num"
set -g __fish_jobs $__fish_prompt_normal
First of all, this sets the environmental variable '__fish_jobs_num' to the number of running jobs (by running count on jobs). Then, we check to see if this number is 0 (no jobs in background) or greater than 0 (stuff running!). If 0, we set the environmental variable '__fish_jobs' to the same color as the normal fish prompt. If it's greater than 0, we set '__fish_jobs' to normal/bold (which I generally have set to a both a bold font and a striking color), and we toss in the number of jobs. We have to do something with this variable, so we change the line…
printf '%s@%s %s%s%s> ' $USER $__fish_prompt_hostname "$__fish_prompt_cwd" (prompt_pwd) "$__fish_prompt_normal"
printf '%s@%s %s%s%s>%s ' $USER $__fish_prompt_hostname "$__fish_prompt_cwd" (prompt_pwd) "$__fish_jobs" "$__fish_prompt_normal"
…adding our control commands in right before the '>' and moving the reset to normal until after. The same is done for the '#' line given to the root user. My fish_prompt.fish file can be downloaded here, if anyone so desires. These changes should be easy to work in to any other custom prompts as well, and the results are…
The next step is a fish function, 'z,' to aid me in multitasking in this fashion. Z will do several things (which I am to code in this order)… Z will, when called by itself, simply list jobs and then give a prompt for a job to foreground, mimicking the two-step (and more verbose) process of jobs ; fg %whatever. Z, when called as 'z command' should check to see whether or not the given command is already running. If so, it foregrounds the running job, if not, it simply executes the command. If more than one instance is running, it gives the user a list. Z -k will put z into kill mode, essentially operating the same as above, but substituting 'kill' for 'fg.' Z will basically just be a simple little task manager, designed to cut down on typing. Why the name 'z?' Well, it's easy to access, it's a metaphor for ctrl-z, and the letter 'z' makes me think of things switching places, like a task to the foreground.
So, tonight, I continue to work on 'z.' Until then, managing jobs with ctrl-z, jobs, fg, and bg is really quite elegant anyway. Fish (not sure about other shells) supports tab completion for fg, which is handy. It may feel clumsy to some, old-timey to others, but I honestly feel much purer, much more back-to-basics when I rely on this method for managing my tasks. I also do make use of dtach for persistence, a simple and clean little tool which takes one aspect of screen and breaks it out into its own little space. Use the -z flag to make ctrl-z affect dvtm (the task) rather than its child processes.
*1: I admit that much of this could be my own failing. I like dvtm the best, because it is the simplest. But it does weird graphical things, and doesn't pass through mouse reporting properly. Screen is like the old grandfather, and my main reason to use that would be that it's probably installed on more systems than any other. Tmux is very nice, and getting mouse reporting to work, and nonmodal scrolling (hopefully with the mouse) could just be a matter of configuration on my end, I'm not sure. In a perfect world, terminal emulators would have their own vertical and horizontal splits (Terminal.app has a horizontal split, but all splits are attached to the same session, that's rather useless to me.). Anyway, I digress.
*2: My love of fish goes against my general theory that I should use as many tools as are available on the most systems as possible. Often, I try to learn tools that are required for POSIX compliance, etc. Fish is certainly not the most common shell. I have used bash enough that I can adapt a lot of what I do in fish over to bash pretty easily. And anything that I mention in this or other articles, though written for fish, will probably be adapted to bash at some point. It's not high priority, however, as the machines I regularly use (mine, and my hosting) have fish. But I know I won't always be so lucky.