The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use X11::Protocol;

my $opt_g = 0;
my $opt_v = 0;
my $do_root = 1;

# This is a fudge factor relating to how the X server allocates resource IDs.
# 21 seems to be the right value for XFree86 4.2.
my $client_shift = 21;

$x = new X11::Protocol;

sub get_prop {
    my($win, $name) = @_;
    return ($x->GetProperty($win, $x->atom($name),
			    $x->atom("STRING"), 0, 65535, 0))[0];
}

sub pre_walk {
    my $win = shift;
    my($root, $dad, @kids) = $x->QueryTree($win);

    my @argv = split(/\0/, get_prop($win, "WM_COMMAND"));
    my $cmd = $argv[0];
    $cmd =~ s[^.*/][];
    $cmd_name{$win >> $client_shift} = $cmd if $cmd ne "";
    map(pre_walk($_), @kids);
}

sub tree {
    my $win = shift;
    my($root, $dad, @kids) = $x->QueryTree($win);

    my $client = $win >> $client_shift;
    my $dad_client = $dad >> $client_shift;
    $id = $win & 0xfffff;

    my $name = "";
    if ($client != $dad_client) {
	my $client_id = sprintf "%x", $client;
	$client_id = "$cmd_name{$client}:$client_id"
	  if exists $cmd_name{$client};
	$name = "($client_id)";
    }
    $name .= sprintf("%x", $id);

    if ($opt_g) {
	my %geo = $x->GetGeometry($win);
	$name .= "($geo{width}x$geo{height}+$geo{x}+$geo{y})";
    }

    my $title = get_prop($win, "WM_ICON_NAME") || get_prop($win, "WM_NAME");

    $name .= "`" . $title ."'" if $title;


    if (not @kids) {
        return "-$name\n";
    }
    my @lines;
    for my $kid (@kids) {
        push @lines, tree($kid);
    }
    my $i;
    for ($i = $#lines; substr($lines[$i], 0, 1) ne "-"; $i--) {
        $lines[$i] = " " . $lines[$i];
    }
    if ($i > 0) {
        $lines[$i] = "`" . $lines[$i];
        $lines[$i] = "|" . $lines[$i] while $i-- > 1;
        $lines[$i] = "+" . $lines[$i];
    } else {
        $lines[0] = "-" . $lines[0];
    }
    return("-$name-" . shift @lines,
           map(" " x (length($name) + 2) . $_, @lines));
}

sub vt_ify {
    my @x = @_;
    for my $l (@x) {
	if ($opt_v) {
	    $l =~ s/\|-/\cNtq\cO/g;
	    $l =~ s/\| /\cNx\cO /g;
	    $l =~ s/`-/\cNmq\cO/g; #`;
	    $l =~ s/---/\cNqqq\cO/g;
	    $l =~ s/-\+-/\cNqwq\cO/g;
	}
    }
    return @x;
}

pre_walk($x->root);

foreach my $arg (@ARGV) {
    if ($arg eq "-g") {
	$opt_g = 1;
    } elsif ($arg eq "-v") {
	$opt_v = 1;
    } else {
	$do_root = 0;
	print tree(hex $arg);
    }
}

print tree($x->root) if $do_root;