Two years ago I switched from terminator to URXVT. This is now my day to day terminal emulator. URXVT is the unicode version of RXVT started long time ago by Rob Nation. Today I’ll show you how I fixed one of the very missing useful feature: open a new terminal in current working directory. By “current working directory” I mean “the working directory of the shell in the most recent focused terminal”.

This is actually a frequently asked question. However I didn’t find answer which convinced me. Here are some threads talking about:

The proposed answers are IMHO weak scripts or hack playing with X11. I want a structural solution and thanks to RXVT plugins we can implement it … in Perl 😒. But first here are my requirements:

  • MUST be optional; I still have to open terminal in home directory
  • MUST work without having to parse window title or other fancy X11 trick
  • SHOULD work with multi-user and multi-display

With the following URXVT Perl extension(s), I hope to fulfill this feature once and for all.

I implemented two new URXVT extensions: remember-last-dir and open-last-dir.

remember-last-dir keeps in shared memory the shell PID of last focused terminal. Currently the IPC key is using only the user ID. Not the display ID. However it could be a nice improvement. This extension must always be enabled.

#!/usr/bin/perl

use IPC::Shareable;

my $glue = sprintf 'urxvt-last-dir-%d', $<;
my %options = (
    create    => 'yes',
    exclusive => 0,
    mode      => 0700,
    destroy   => 'no',
);

sub on_focus_in {
    my ($self) = @_;
    my $pid;
    tie $pid, 'IPC::Shareable', $glue, { %options };
    $pid = $self->{shell_pid};
}

sub on_child_start {
    my($self, $pid) = @_;
    $self->{shell_pid} = $pid;
}

open-last-dir is initializing the terminal with the working directory of current PID in shared memory. This extension is optionally launched from URXVT command line: urxvt -pe open-last-dir.

#!/usr/bin/perl

use IPC::Shareable;

my $glue = sprintf 'urxvt-last-dir-%d', $<;
my %options = (
    create    => 'yes',
    exclusive => 0,
    mode      => 0700,
    destroy   => 'no',
);

sub on_init {
    my ($self) = @_;
    my $pid;
    tie $pid, 'IPC::Shareable', $glue, { %options };

    if (defined $pid and $pid ne "") {
        my $link = sprintf "/proc/%d/cwd", $pid;
        my $wd = readlink $link;
        if (-e $wd) {
            $self->resource("chdir", $wd);
        }
    }
}

I guess we could improve this code. This is here a quick dump of my setup. Please don’t blame me for missing error handling, code factoring, … If you want to contribute or help me going further with this extension, please leave me a message in my blog issues.

Thanks for reading,

R.