window titles in screen and rxvt, from zsh

Recently I’ve been multiplexing work over many urxvt terminals: essential when you’re monitoring the logging output of several related binaries and managing several git branches related to each. The sensibilities of awesome have eased the window management, but I’ve become increasingly reliant on the window title to identify particular sessions. This is compounded one level further when I’m using screen; I want a useful identifier to show up in screen’s windowlist.

Getting nice prompts is a common need and there is plenty of information out there on setting your PS1, but for various reasons I’ve had problems finding a ‘nice’ setup that’s just worked exactly the way I’ve wanted it to with titles. Here are the pertinent parts of my working configuration:

  • I get the working directory and current command in the title
  • I get the title in the screen windowlist if running in screen
  • I get the same title in the WM_NAME of an rxvt or xterm, or the titlebar of an iTerm2, whether or not screen is involved


Here’s how I set my PS1 in .zshrc:

case "$TERM" in
  # [skipping some esoteric terminal emulators...]

  screen|screen.rxvt)
     # Set a coloured prompt
     PS1=$'%{\e[00;32m%}%*%{\e[00;34m%}%2~ %# %{\e[00m%}'
     ;;
  rxvt|rxvt-unicode|xterm|xterm-color)
     # Set the title, and a coloured prompt containing some useful info
     PS1=$'%{\e]0;%-3~\a\e\[00;32m%}%*%{\e[00;30m%}!%! %{\e[00;34m%}%2~ %# %{\e[0m%}'
     ;;
esac

I want an abbreviated form of the full command line so that it will fit in awesome’s tasklist. I set the titles before a command is run in zsh’s preexec hook. The screen window title and the xterm title are set with two different escapes.

function preexec() {
  local a=${${1## *}[(w)1]}  # get the command
  local b=${a##*\/}   # get the command basename
  a="${b}${1#$a}"     # add back the parameters
  a=${a//\%/\%\%}     # escape print specials
  a="${a:gs/[[:space:][:cntrl:]]##/ /}"  # sanitize fancy characters

  case "$TERM" in
    screen|screen.*)
      # See screen(1) "TITLES (naming windows)".
      # "\ek" and "\e\" are the delimiters for screen(1) window titles
      print -Pn "\ek%-3~ $a\e\\" # set screen title.  Fix vim: ".
      print -Pn "\e]2;%-3~ $a\a" # set xterm title, via screen "Operating System Command"
      ;;
    rxvt|rxvt-unicode|xterm|xterm-color|xterm-256color)
      print -Pn "\e]2;%m:%-3~ $a\a"
      ;;
  esac
}

This requires the title to be reset for screen (since it’s not reset by PS1), using zsh’s precmd hook:

function precmd() {
  case "$TERM" in
    screen|screen.rxvt)
      print -Pn "\ek%-3~\e\\" # set screen title
      print -Pn "\e]2;%-3~\a" # must (re)set xterm title
      ;;
  esac
}

The beauty of this method (unlike methods that bind screen’s status line escapes to xterm Operating System Command escapes) is that no termcapinfo hacks in .screenrc are necessary.

Update: changed preexec to use zsh subscripting rather than splitting by IFS. Fixes duplication of 0-argument commands, e.g. “top top”.
Update: replaced a $(subprocess) with built-ins.

Leave a Reply

Your email address will not be published. Required fields are marked *