pwm - A simple but extensible X11 window manager written in Perl.





This manual page documents pwm, a simple but extensible X11 window manager written in Perl. Development of pwm was motivated by perlwm (, which is a window manager written entirely in Perl. The idea of implementing X11 window manager in Perl is great since it allows you to fully customize the behavior of the window manager with a little Perl programming. Similarly to perlwm, pwm is built based on X11::Protocol module developed by Stephen McCamant.

The notable features of pwm are its simplicity, compactness, and programmable cascaded/tiled window placement algorithms.

pwm is simple in a sense that it is entirely written in Perl, and it requires only X11::Protocol module from CPAN. pwm is written with less than 1,000 lines of code. If you are familiar with X11 protocol and basics of Perl programming, you can easily read and understand the source code of pwm.

pwm is compact since it provides minimal window decorations. pwm has no pop-up menus, graphical icons, and window animations. pwm is designed to consume the minimum amount of screen space for letting users and applications to use as wide screen space as possible. For instance, pwm draws the window title inside the window, rather than outside the window, which saves dozen-pixel lines around the window.

pwm supports two types of window placement algorithms: programmed mode and tiled mode.

In the programmed mode, you can specify rules for inferring appropriate window geometries. By default, Emacs is placed at the top-left corner of the screen with 45% window width and 100% window height. Midori, firefox, chromium, xpdf, xdvi, LibreOffice, tgif and Mathematica are placed next to the Emacs with 55% window width and 100% window height. The terminal window is placed at the bottom-right corner with 50% window width and 70% window height. If there exist more than two terminal windows, the size of each terminal window is shrunk to 1/4 of the screen, and placed in a non-overlapping way.

In the titled mode, all windows are placed in a titled fashion so that any window will have the same window width and height, and that any window will not overlap with others, as like tile-based window managers. Moreover, pwm tries to allocate larger area for Emacs; i.e., if there are three windows, say, Emacs and two terminals, Emacs will occupy the half of the screen, and each terminal will have the quarter of the screen.




A Makefile for Debian GNU/Linux systems is available at

An example session is as follows.

  $ wget
  # make install
  $ make fetch-skelton
  $ cp skel.xinitrc ~/.xinitrc
  $ cp skel.Xdefaults ~/.Xdefaults
  $ xinit


1. Install the 8x8maru bitmap font

8x8maru bitmap font is available at:

Save the bitmap font file `8x8maru.bdf' in a directory contained in the X11 font path. The current font path can be checked with

  $ xset q

Note that the font file index `fonts.dir' file in the font directory must be updated so that X server can recognize 8x8maru font. An example session is as follows.

  $ mkdir -p $HOME/lib/fonts
  $ cd $HOME/lib/fonts
  $ wget
  $ xset +fp $HOME/lib/fonts
  $ mkfontdir
  $ xset fp rehash
2. Change the window manager

In X window system, multiple window managers cannot be run simultaneously. So, to use pwm, you have to disable or kill the currently-running window manager before starting pwm. The process of invoking a window manager as well as other applications is operating system dependent. A window manager can be invoked in several ways --- from per-user scripts (i.e., ~/.xinitrc and ~/.xsessionrc), system-wide scripts (e.g., /etc/X11/xinit/xinitrc and /etc/X11/Xsession), and display managers (e.g., xdm and gdm). Check manual pages (e.g., xinit(1), startx(1), Xsession(5), xdm(1)) for details.

This section explains the (possibly) simplest way to change the window manager.

1. Change the runlevel from 3 to 2 to disable the display manager.

Edit /etc/inittab and replace the line `id:3:initdefault:' with `id:2:initdefault:'. After the reboot, the system will not start a display manager such as xdm and gdm. Reboot the system, and login via console. Make sure X window system is not running.

2. Create the client script ~/.xinitrc.

Create ~/.xinitrc file. An exmple ~/.xinitrc file is:

  xset +fp $HOME/lib/fonts
  emacs &
  pwm &
  pwmmon &
  pwmlog &
3. Start X server with xinit.

Run xinit to start X server.


Since Perl is one of interpreters, you can easily customize the behavior of pwm by directly modifying its code. For instance, if you want to change the appearance of window frames, edit the constants section. If you want to change the keyboard binding, edit the hash variable %KEYBOARD_HANDLER_FOR. The key of the hash is the name of an X11 keysym imported with X11::Keysyms, which is automatically generated from /usr/include/X11/keysymdef.h. The value of the hash is self explanatory: modifier is the mask of keyboard modifiers and callback is the reference to the callback function.


Mod1 + Button1

Move the current active window while dragging with pressing Mod1 + Button1.

Mod1 + Button3

Resize the current active window while dragging with pressing Mod1 + Button3.

Ctrl + Mod1 + i

Focus the next window. Available windows are circulated in the order of top-left, bottom-left, top-right, and bottom-right.

Ctrl + Mod1 + m

Raise or lower the current active window.

Ctrl + Mod1 + '

Toggle the maximization of the current active window.

Ctrl + Mod1 + ;

Toggle the vertical maximization of the current active window.

Ctrl + Mod1 + ,

Layout all available windows in the programmed mode.

Ctrl + Mod1 + .

Layout all available windows in the tiled mode.

Ctrl + Mod1 + z

Destroy the current active window.

Ctrl + Mod1 + 1

Run a command "(unset STY; rxvt) &" via system() function.

Ctrl + Mod1 + 2

Run a command "pidof emacs || emacs &" via system() function.

Ctrl + Mod1 + 3

Run a command "pidof chrome || chromium &" via system() function.

Ctrl + Mod1 + 6 -- Ctrl + Mod1 + 9, Ctrl + Mod1 + 0

Run a ssh command with different arguments. Edit according to your environment.

Mod1 + F1 -- Mod1 + F4

Switch to the virtual screen 1--4, respectively.

Shift + F5 -- Shift + F9

Run a shell script. Edit according to your environment.


X11::Protocol dies when event handlers receive null data. You can fix the problem by applying the following patch to X11/

  ---  2006-10-09 05:16:30.000000000 +0900
  +++       2012-11-14 09:06:26.000000000 +0900
  @@ -729,7 +729,7 @@
        $backing_planes, $backing_pixel, $save_under, $map_is_installed,
        $map_state, $override_redirect, $colormap, $all_event_masks,
        $your_event_mask, $do_not_propagate_mask)
  -      = unpack("xCxxxxxxLSCCLLCCCCLLLS", $data);
  +      = defined $data ? unpack("xCxxxxxxLSCCLLCCCCLLLS", $data) : (0) x 15;
        $colormap = "None" if !$colormap and $self->{'do_interp'};
  @@ -829,7 +829,7 @@
        my $self = shift;
        my($data) = @_;
        my($depth, $root, $x, $y, $width, $height, $border_width)
  -      = unpack("xCxxxxxxLssSSSxxxxxxxxxx", $data);
  +      = defined $data ? unpack("xCxxxxxxLssSSSxxxxxxxxxx", $data) : (0) x 7;
        return ("depth" => $depth, "root" => $root, "x" => $x, "y" => $y,
             "width" => $width, "height" => $height,
  @@ -899,7 +899,7 @@
        my $self = shift;
        my($data) = @_;
        my($format, $type, $bytes_after, $len) =
  -      unpack "xCxxxxxxLLLxxxxxxxxxxxx", substr($data, 0, 32);
  +      defined $data ? unpack("xCxxxxxxLLLxxxxxxxxxxxx", substr($data, 0, 32)) : (0) x 4;
        my($m) = $format / 8;
        my($val) = substr($data, 32, $len * $m);
        return ($val, $type, $format, $bytes_after);


The latest version of pwm is available at


X11::Protocol(3pm), X11::Keysyms(3pm), X11::Xlib(3pm), twm(1), perlwm(1), pwmon(1), pwmlog(1)


Hiroyuki Ohsaki <ohsaki[atmark]>