twm window manager

12 Nov

twm is historical. It dates back to 1987, and is still being developed (although I think "maintained" would be more appropriate). I simply wanted to try it out and see what I can do with it, if I can customize it to my needs, both in functionality and looks.

It's in most Linux distros' repos. If it isn't I honestly don't know what to do. It seems the source is available here.

Imagine it is 1987. Having a graphical environment at all is a new thing. There's none of those recent "back-to-the-basics" software philosophies - this is the basics. Being able to manage windows must have been amazing.
Placing a new window is a major event. It requires the user's full attention - you have to give it a place on your screen with a click. 30 years later, this seems incredibly annoying. I don't know if the "RandomPlacement" option has been added in later - it does exactly that, and between these two placement policies there's nothing else.

This, and the lack of any sort of virtual desktops, pretty much removes twm from any serious consideration (yes i know there's ways around that, and anyhow the next logical step is to use vtwm - which I will review seperately, soon). Nevertheless I was curious to see what i can do with it - and was surprised how versatile it is otherwise! The minds behind this were not from the stone ages, even if their hardware was (lol emoji here).

I found it pretty easy to get around - you only have to press the left mouse button to get a menu - but if you're confused, you might want to read this post which outlines twm's basic usage. It's worth pointing out that some unusual behaviour can actually be very useful - esp. the fact that I can decide with a click (or hotkey) when a window rises to the top of the stack, regardless of being focused or not. I bound this to a middle click (both on title and window itself), which enables me to quickly click through the windows that are on top of each other.

The idea of "Icons" (which are not small graphic images, but really small rectangles with some text, representing minimized windows) needs some getting used to, especially since there seem to be two approaches:

  • single, independent, more-or-less square icons on the desktop, with some sort of bitmap image representing the minimzed programme (ugly!)
  • an icon manager with rectangles with text, plus a small bitmap image in some cases

both are clickable and clicking them does what one would expect: it restores or iconizes the window.
I opted to remove the bigger, square icons completely and go with the icon manager only, which is comparable to a modern panel.

All configuration happens through a single file, usually ~/.twmrc.

To make it short, this resource was invaluable! Especially the Icons and Configs links towards the end of the page. There is also this thread on LinuxQuestions.

After some fiddling I was able to get good mouse & keyboard functionality, and remove/replace some of the most annoying "graphical" features (mostly the b/w checkerboard frame around focused windows). A lot more is possible, if one wanted to go that way! Keybinds, custom functions for window placement/movement/resizing, the whole lot. The config file cannot include more config files (e.g. a seperate menu file), but there are ways to script around that.
On debian systems a classical debian menu is part of the default root menu.

I was not able to change "Focus follows Mouse" to "Click to Focus", although some comments suggest that this is possible. That's another big no-no for me.

############################################################################
# Font Section
############################################################################
#ResizeFont "*new century schoolbook-medium-i-*--24-*-iso8859-*"
IconManagerFont "-*-terminusv-medium-*-condensed-*-12-*-*-*-*-*-*-*"
MenuFont    "-*-terminusv-bold-*-condensed-*-12-*-*-*-*-*-*-*"
TitleFont   "-*-terminusv-bold-*-condensed-*-12-*-*-*-*-*-*-*"
IconFont    "-*-terminusv-bold-*-condensed-*-12-*-*-*-*-*-*-*"
# font: http://dt.iki.fi/terminusv

############################################################################
# Variables Section
############################################################################
#
# TWM Boolean Variables
#
AutoRelativeResize  # Allow resize from any point within the window
#ClientBorderWidth  # Take border width from initial border width of window
DecorateTransients  # Transient windows should have titlebars
DontMoveOff     # Do not allow windows to be moved of the screen
ForceIcons      # Force use of "Icons" list instead of client-supplied one
NoBackingStore  # Backing store for twm's menus
NoCaseSensitive     # For sorting icon names in icon manager
NoDefaults      # Needed when building own title buttons and bindings
NoGrabServer        # When popping up menus or moving opaque windows
NoMenuShadows       # Don't draw drop shadows behind menus
#NoRaiseOnMove      # Don't automatically raise when windows are moved
#NoRaiseOnResize        # Don't automatically raise when windows are resized
# NoRaiseOnWarp     # Don't automatically raise window when f.warpto
# NoSaveUnders      # Repaint instead of save-under for menu selection
#NoTitleFocus       # Don't set input focus when window is entered
OpaqueMove      # F.move window instead of just an outline
RandomPlacement     # Don't give ouline-drag for no-geometry windows
RestartPreviousState    # 'Remember' previous state when window manager is restarted
ShowIconManager     # Show icon manager on startup
SortIconManager     # Sort icons alphabetically in iconmanager
WarpUnmapped        # Allow f.warpto to de-iconify windows

#
# TWM Numeric Variables
#
MenuBorderWidth     1
BorderWidth     2   # Frame border width in pixels
ButtonIndent        0   # 0, Title button indentation in pixels
ConstrainedMoveTime 400 # Time (msec) in which double click allows only move in hor or vert direction
FramePadding        2   # Pixelwidth between titlebar decorations and the window frame
IconBorderWidth     2   # Border of icons in pixels
MoveDelta       3   # Number of pixels to move before f.move starts working (also f.deltastop)
TitleButtonBorderWidth  1   # 0, Distance between title buttons
TitlePadding        8   # 16, Distance between title buttons, text and highlight area

#
# TWM String Variables
#
# Path to look for bitmaps if they cannot be found in "bitmapFilePath" resource
IconDirectory       "/usr/include/X11/bitmaps"
IconDirectory       "~/.config/twm/icons"
UsePPosition        "on"        # program requested location, "on" "off" "nonzero"

# TWM Complex Variables
#
IconManagerGeometry "=160x10-0+0" 1
# Define regions to put icons (multiple lines allowed)
# IconRegion    geomstring  # define geometry)
#       vgrav       # North or South fill direction
#       hgrav       # East for West fill direction
#       gridwidth   # grid dimensions to put icons in
#       gridheight
IconRegion      "=300x300+200-0" North East 30 30

Color
{
    #DefaultBackground "#222222"
    #DefaultForeground "#bbbbbb"
    TitleBackground "#222222"
    TitleForeground "#bbbbbb"
    MenuBackground "#222222"
    MenuForeground "#bbbbbb"
    MenuBorderColor "#666666"
    MenuTitleBackground "#bbbbbb"
    MenuTitleForeground "#222222"
    BorderColor "#005577"
    IconBackground "#222222"
    IconForeground "#999999"
    IconBorderColor "#999999"
    IconManagerBackground "#222222"
    IconManagerForeground "#bbbbbb"
    IconManagerHighlight "#005577"
    BorderTileBackground "#666666"
    BorderTileForeground "#666666"
}

Cursors {
    # cursorname    "string" for names in include/X11/cursorfont.h
    # cursorname    "image" "mask" for cursors taken from bitmap files
    Frame       "top_left_arrow"    # "spider"
    Title       "top_left_arrow"
    Icon        "top_left_arrow"
    IconMgr     "top_left_arrow"
    Move        "fleur"
    Resize      "fleur"
    Menu        "sb_left_arrow"
    Button      "hand2"
    Wait        "watch"
    Select      "dot"
    Destroy     "pirate"
}

DontIconifyByUnmapping {
    "Xjewel"
}
IconifyByUnMapping

IconManagerDontShow {
#   "names of things which you don't want to see in the icon manager"
    "oclock"
    "xcpustate"
    "xdaliclock"
    "Xman"
    "xmeter"
    "xpbiff"
    "TWM Icon Manager"
    "xload"
    "xeyes"
    "xclock"
}

IconManagers {          # Definition of iconmanagers...
    # "winname" ["iconname"]    "geometry"  columns
    # "XTerm"           "=300x5+800+5"  5
}

#ForceIcons
Icons {
#   "name"  "name.icon"
    "xterm" "terminal"
    "URxvt" "terminal"
}

NoHighlight {
    "TWM Icon Manager"
}

NoStackMode { }     # ignore stacking request for these windows

# MakeTitle { } # Create title bars even when NoTitle has been specified
NoTitle {
#   "names of things for which you don't want a title bar"
    "oclock"
    "swissclock"
    "swisswatch"
    "TWM Icon Manager"
    "xcb"
    "xcmap"
    "xcpustate"
    "xdaliclock"
    "xeyes"
    "Xman"
    "xmeter"
    "xpbiff"
    "xpostit"
    "xload"
    "xclock"
}

#~ NoTitleHighlight # don't highlight titlebar when focused in window
Pixmaps {
    # TitleHighlight    "hlines2"
    TitleHighlight  "dimple3" # better than notitlehighlight
}

StartIconified  {
#    "console"
}

WarpCursor {        # warp cursor in window when de-iconified
#    "xterm"
}

WindowRing {        # windows to cycle through by f.warpring
#    "xterm"
}

######################################################################
# FUNCTIONS
######################################################################
Function "dmenu_run_hist" {
f.exec "exec dmenu_run_hist -fn 'terminusv:style=bold' -nb '#222222' -nf '#bbbbbb' -sb '#005577' -sf '#dddddd' "
}

Function "pacmenu" {
f.exec "exec pacmenu -l 'dmenu -fn 'terminus:size=9' -nb '#222222' -nf '#bbbbbb' -sb '#005577' -sf '#dddddd' -l 33' "
}

######################################################################
# Titlebuttons
######################################################################
# bitmaps are stored in /usr/include/X11/bitmaps
# ":bitmap" uses internal bitmap
#         (:dot, :xlogo, :iconify, :resize, :question, :delete, :menu)
#---------------------------------------------------------------------
RightTitleButton    "minimize2" = f.iconify
# RightTitleButton  ":menu"     = f.menu "WindowSettings"
RightTitleButton    ":resize"   = f.resize
RightTitlebutton    "close2"    = f.delete

#Button = KEYS : CONTEXT : FUNCTION
#----------------------------------
Button1 =      : root   : f.menu "rootmenu"
Button3 =      : root   : f.menu "TwmWindows"
Button2 =      : root   : f.menu "othermenu"

Button2 =      : window|title  : f.raiselower
Button1 =      : title  : f.move
Button3 =      : title  : f.menu "titlemenu"

Button2 =      : frame  : f.raiselower
Button1 =      : frame  : f.move
Button3 =      : frame  : f.menu "rootmenu"

Button2 =      : iconmgr  : f.delete
Button1 =      : iconmgr  : f.iconify
Button3 =      : iconmgr  : f.iconify

"Tab"   = m | s : all           : f.backiconmgr
"Tab"   = m     : all           : f.forwiconmgr
"Tab"   = m | s : all           : f.upiconmgr
"Tab"   = m     : all           : f.downiconmgr

"Return" = s : all : f.function "dmenu_run_hist"
"Return" = s | c : all : f.function "pacmenu"
"F4" = m : all : f.destroy
"q" = m4 : all : f.destroy
"d" = m : all : f.function "dmenu_run_hist"
"r" = c | m : all : f.restart
"q" = s | m4 : all : f.quit
"t" = m4 : all : f.exec "exec my term"

#
# And a menu with the usual things
#
menu "rootmenu"
{
"Twm"   f.title
"Show Iconmgr"  f.showiconmgr
"Hide Iconmgr"  f.hideiconmgr
"──────────────"        f.nop
"Run..."    ("#aa0000":"#222222")   f.function "dmenu_run_hist"
"Terminal"  f.exec "exec urxvt &"
"──────────────"        f.nop
"Restart"   f.restart
"Exit"      f.quit
}

menu "titlemenu" {
"Window Ops"        f.title
"Iconify"   f.iconify
"Resize"    f.resize
"Move"      f.move
"Raise"     f.raise
"Lower"     f.lower
"──────────────"        f.nop
"Focus"     f.focus
"Unfocus"   f.unfocus
"──────────────"        f.nop
"Refresh"       f.winrefresh
"AutoRaise"     f.autoraise
"DeIconify"     f.deiconify
"Force Move"        f.forcemove
"Identify"      f.identify
"MaxVertToggle"         f.zoom
"MaxFullToggle"     f.fullzoom
"──────────────"        f.nop
"RootMenu"      f.menu "rootmenu"
"──────────────"        f.nop
"Caution"       f.title
"Quit"          ("red":"white")f.delete
"Destroy"       ("red":"white")f.destroy
}

This article is part of another article.
There was a thread on crunchbang forums which inspired me to do something like this myself. You can compare this article with the older twm article from that thread.
There also is an accompanying forum thread on bunsenlabs forums.

Next Post Previous Post