Quest for a Window Manager

To be frank, I was never really in love with the mainstream window managers.

Quest for a Window Manager

I use OpenBSD-current as my daily driver.  While I occasionally run into issues it has been, by and large, rock solid.  While running -current I would run into issue with the various windows managers being slightly out of sync.  (This is not a complaint, just background impetus for this post)  To be frank, I was never really in love with the mainstream window managers.  Gnome seemed the "most polished" but there was always something that just didn't quite work the way I wanted it to.  KDE had the most apps that I never used (although looking at KDE Plasma as part of this article it does look really sexy.  Maybe it will pass the spouse factor).  Enlightenment looked the coolest and the ideas behind it are impressive, but it was too fringe.

I decide to try and utilize a windows manager that came default installed with OpenBSD.  The default window manager is FVWM.  Very basic (ugly)... yet clickable.  I tried to do some work with the Xresources to make it less ugly and came the to realization, yet again, that I am not a graphics designer.  

Aside: I envy those people that can take a look at say, FVWM, and see with some color pallet tweaks here, and new skinning there, make a turd look like a gem.

So I finally ended up at CWM.  I had heard it was no frills (which is true).  After some initial tinkering found it to be exactly what I was searching for.

Aside: I'm an old school vi/vim guy.  Once I figured out how to move around and the difference between edit and command modes, I discovered I cannot go back.  I will hit escape and try to move the cursor around even as I type this.  (Can get real annoying on editors that throw all work away when hitting escape).

In CWM window movement and resize just uses the H J K L keys.  You can also use the mouse to move windows (by pressing ALT and dragging) but that is slower and who has time for that.

As for apps.  No borders or headers are drawn.  This is not a problem for most of the apps I use because they all draw the X box to close the window.  For the rare occasion that window was not drawn with an X (mostly on browser popup windows) pressing CTRL+ALT+X will do the job.

I also found that with few exceptions, everything I do is from the command line (CTRL+ALT+Enter).  I do use a few graphical apps which I have added to a second button click menu. (Chrome; Firefox; VLC; xcalc; Wireshark)  That's it.  Everything else is launched from command line.  (I'm not opposed to additional apps.  I just don't need any)

My .cwmrc file is very minimal

# cwmrc

# set the gap at the top of the screen(s) to be 30 pixels down
# so that time shows.
# allow a 10 pixel gap on the right for space to click on the desktop
gap 30 0 0 10 

# ignore application when cycling thru them
ignore xmobar
ignore xidle

# bind windows key to menu-cmd
#bind-key 4                                             menu-cmd
# sometimes you need screenshots
bind-key C-Print            "/home/user/bin/scapture.sh"
bind-key CM-Print           "/home/user/bin/scapture.sh -root"

moveamount 5

# Commands in our application menu
command chrome          /usr/local/bin/chrome
command firefox         /usr/local/bin/firefox
command vlc             /usr/local/bin/vlc
command calc            xcalc
command wireshark       /usr/local/bin/wireshark

I've been using this configuration with little tweaks hear and there for a few years now without any real issue.  The only thing that I may want to change slightly is the menu bar. I am currently using xmobar from ports, which is a very minimalistic menubar for time and some stats  UPDATE:  I reticently ran into termbar.  Believe it or not it is even more minimalistic that xmobar.

xmobar Screenshot

The bar runs in the background and can execute scripts at specific intervals.  I found that running the scripts too often to make the bar update faster cause higher CPU than I wanted to devote to setting that will mostly stay static.

# xmobarrc
Config { font = "-*-fixed-*-*-*-*-15-*-*-*-*-*-*-*"
       , additionalFonts = []
       , borderColor = "black"
       , border = TopB
       , bgColor = "black"
       , fgColor = "grey"
       , alpha = 255
       , position = Top
       , textOffset = -1
       , iconOffset = -1
       , lowerOnStart = True
       , pickBroadest = False
       , persistent = False
       , hideOnStart = False
       , iconRoot = "."
       , allDesktops = True
       , overrideRedirect = True
       , sepChar = "%"
       , alignSep = "}{"
       , template =  " } %date% { %battery% | %mixer%"
       , commands = [ Run Com "/home/user/.xmobar/scripts/power.sh" [] "battery" 100
                    , Run Date "<fc=#ee9a00>%a %b %d %I:%M %p %Z</fc>" "date" 600
                    , Run Com "/home/user/.xmobar/scripts/mixer.sh" [] "mixer" 100
                    ]
       }
#!/bin/ksh

# Time to sleep between updates
STIME=3
WARNPCT=30
ALRTPCT=10

_norm="\033[39m" 
_alrt="\033[31m" 
_warn="\033[33m" 
_rset="\033[0m" 
_hide="\033[2m" 


mixer() {
        OMUTED=$(/usr/bin/sndioctl -n output.mute 2>/dev/null || echo "-1" )
        OVOL=$(/usr/bin/sndioctl -n output.level 2>/dev/null || echo "-1" )
#       IMUTED=$(/usr/bin/sndioctl -n input.mute)
#       IVOL=$(/usr/bin/sndioctl -n input.level)
        HP=$(/usr/bin/mixerctl -n outputs.hp_sense 2>/dev/null || echo "")
        omth="spkr"

        if [ ! -z "$HP" -a "$HP" == "plugged" ]; then
                omth="hdph"
        fi
        if [ $OMUTED == "-1" ]; then
                echo -n "Error"
        else
                if [ $OMUTED -eq 0 ]; then
                        _dc=$(dc -e "$OVOL 100 * p" | cut -f1 -d.)
                        _colr=""
                        [[ $_dc -ge 70 ]] && _colr="${_warn}"
                        [[ $_dc -ge 90 ]] && _colr="${_alrt}"
                        echo -n "${_colr}$omth ${_dc}%${_norm}"
                else
                                        echo -n "Muted"
                fi
        fi
}

power() {
        ac=$(/usr/sbin/apm -a 2>/dev/null || echo "256")
        case $ac in
                0)      # disconnected
                        bat=$(/usr/sbin/apm -l 2>/dev/null || echo "-1")
                        tmr=$(/usr/sbin/apm -m 2>/dev/null || echo "?")
                        [[ $bat -le $WARNPCT ]] && echo -n "${_warn}"
                        [[ $bat -le $ALRTPCT ]] && echo -n "${_alrt}"
                        echo -n "$bat% (${tmr}min)"
                        [[ $bat -le $WARNPCT ]] && echo -n "${_norm}"
                ;;
                1)      # connected
                        echo -n "a/c"
                ;;
                2)      # backup power
                        echo -n "bkup"
                ;;
                *)      # unknown
                        echo -n "unk"
                ;;
        esac
}

cpu() {
        freq=$(/sbin/sysctl -n hw.cpuspeed 2>/dev/null || echo "?")
        pol=$(/sbin/sysctl -n hw.perfpolicy 2>/dev/null || echo "")
        printf "%s(%s)" $pol $freq
}

sdate() {
        # max width 20
        d=$(date +"%a %b %d %H:%M %Z")
        printf "${_warn}$d${_norm}"
}

length() {
        _s=$(echo "$1" | sed -r -e 's/'$(echo "\033")'\[[0-9]{2}m//g')
#       echo "length \'$1\' \'$_s\'" >&2
        echo ${#_s}
}

center() {
        _w=$1
        _v=$2
        _vw=$(length "${_v}")
        if [ -z "${_w}" -a ${_w} -le 0 ]; then
                return "$2" # invalid width... just return the value
        fi
        if [ ${_w} -lt ${_vw} ]; then   # truncate _v to width
                printf "%${_w}s" "${_v}"
        else
                _d=$((($_w - $_vw) / 2))
#               echo "left $_vw $_d" >&2
                printf "%${_d}s%s%${_d}s" " " "$_v" " "
        fi
        }

right() {
        _w=$1
        _v=$2
        _vw=$(length "${_v}")
        if [ -z "${_w}" -a ${_w} -le 0 ]; then
                return "$2" # invalid width... just return the value
        fi
        if [ ${_w} -lt ${_vw} ]; then   # truncate _v to width
                printf "%${_w}s" "${_v}"
        else
                _d=$((($_w - $_vw)))
#               echo "left $_vw $_d" >&2
                printf "%${_d}s%s" " " "$_v"
        fi
}

left() {
        _w=$1
        _v=$2
        _vw=$(length "${_v}")
        if [ -z "${_w}" -a ${_w} -le 0 ]; then
                return "$2" # invalid width... just return the value
        fi
        if [ ${_w} -lt ${_vw} ]; then   # truncate _v to width
                printf "%${_w}s" "${_v}"
        else
                _d=$((($_w - $_vw)))
#               echo "left $_vw $_d" >&2
                printf "%s%${_d}s" "$_v" " "
        fi
}

do_exit() {
        LOOPF=0
}

trap 'exec $0' USR1 # restart myself... forces re-read of script
trap do_exit HUP INT QUIT TERM PIPE ALRM # reset term and exit

# reset STIME if pass as arg 1
STIME=${1:-$STIME}
WIDTH=$(/usr/bin/tput columns)
THRD=$(($WIDTH/3))
LOOPF=1

# Hide Cursor
/usr/bin/tput civis
/usr/bin/tput clear
while [ $LOOPF -eq 1 ]; do
        /usr/bin/tput cup 0 0
        MID=$(center "$THRD" "$(sdate)")
        RIGHT=$(right "$THRD" "$(printf "%s | %s | %s" "$(power)" "$(cpu)" "$(mixer)")")
        LEFT=$(left "$THRD" "")
#       LEFT=$(left "$THRD" "$(printf "w%d t%d m%d r%d" $WIDTH $THRD $(length "${MID}") $(length "${RIGHT}"))")
#       printf "|%-${THRD}s|%${THRD}s|%${THRD}s|" "${LEFT}" "${MID}"  "${RIGHT}"
        printf "%s%s%s" "${LEFT}" "${MID}" "${RIGHT}"
        [[ $STIME == "-1" ]] && exit
        /bin/sleep $STIME
done
# just in case we exit from loop
/usr/bin/tput cnorm
exit 0
termbar script

Here is the .xsession script that I run

#!/bin/sh

function window_id {
        local _n=$1
        local _id
        if [ -z "$_n" ]; then
                return ""
        fi
        _id=`xwininfo -name $_n 2>/dev/null | grep "Window id:" | awk '{ print $4 }'`
        return $_id
}

resources=$HOME/.Xresources

if [ -f "$resources" ]; then
        /usr/X11R6/bin/xrdb -load "$resources"
fi

screen_width=0
screen_height=0
primary_screen_offset_x=0
primary_screen=`xrandr | grep primary | awk '{ print $1 }'`
echo "primary_screen=$primary_screen"

# Enable second screen if present
$HOME/bin/XHDMI.sh start

# Find the dimensions of the primary screen
for i in `xrandr | grep connected | grep -v disconnected | \
awk '{ if ($3 ~ /primary/) { printf "%s:%s\n", $1, $4 } else { printf "%s:%s\n", $1, $3 } }'`; do
        D=`echo $i | cut -f1 -d:`
        S=`echo $i | cut -f2 -d:`
        A=`echo $S | tr "x+" " "`
        if [ $primary_screen == $D ]; then
                set -- $A
                primary_screen_offset=$4
                screen_width=$1
                screen_height=$2
        fi
done
echo "primary_screen_offset=$primary_screen_offset"
echo "screen_width=$screen_width"
echo "screen_height=$screen_height"

# adjust the mouse buttons and scroll direction
$HOME/bin/XFixMouse.sh

# start xcompmgr
# with a dark background this didn't seem to add anything. When the background
# is colored or tectured this does add some nice visual effects.
#xcompmgr -c &

# stop programs that were started prior to login
for i in "xconsole xclock"; do
        xid=`window_id $i`
        if [ ! -z "$xid" ]; then
                xkill -id $xid >/dev/null 2>&1
        fi
done

# disable system beep
xset b off

# screensaver
xidle -delay 5 -timeout 300 -program "/usr/X11R6/bin/xlock" &
# set the background
xsetroot -solid black
# start an xterm
xterm -geometry 80x48+1+$((primary_screen_offset+30)) &

TERMCWID=133
# run 'xterm' then maximize and run 'tput columns' to get the column length of the display
# XXX need way to figure out xterm character width with full width instead of setting it manually
# use termbar if it exists
if [ -f $HOME/bin/termbar ]; then
        xterm -geometry ${TERMCWID}x1+1+$primary_screen_offset -name termbar -class termbar -e $HOME/bin/termbar &
else
       # start xmobar
       /usr/local/bin/xmobar &
fi

# start the window manager
/usr/X11R6/bin/cwm
User .xsession script
! Xresource overrides
! enable color
*customizations:        -color

XIdle*timeout: 300

XTerm*background: black
XTerm*foreground: green
XTerm*font: -*-fixed-*-*-*-*-15-*-*-*-*-*-*-*
XTerm*rightScrollBar: true
XTerm*scrollLines: 10
XTerm*visualBell: true
XTerm*scrollKey: true
XTerm*scrollTtyOutput: false
XTerm*loginShell: true

XClock*update: 60
XClock*twentyfour: false
XClock*analog: false
XClock*background: black
XClock*foreground: green

XLoad*showLabel: false
XLoad*borderWidth: 0
XLoad*background: black
XLoad*foreground: green

XLock.description: off
XLock.dpmstandby: 10
XLock.dpmsoff: 30
XLock.mode: blank
XLock.echokeys: on
XLock.echokey: *
XLock.usefirst: no
XLock.lockdelay: 5
XLock.timeout: 60
XLock.background: black
XLock.foreground: green
XLock.username: Username:
XLock.password: Password:
XLock.info:

! ~/bin/termbar inside xterm
! calculate geometry in xsession script
termbar*internalBorder: 6
termbar*saveLines: 0
termbar*scrollBar: false
termbar*title: termbar
termbar*foreground: green
termbar*background: black
termbar*color1: red
termbar*color3: orange
User .Xresources file

Future considerations

I'm always looking for ways to make my desktop better.  Currently I'm experimenting with the follow.  I'm interested if someone has solved this.

  • better menubar than xmodbar? See UPDATE
  • ability to take screenshots by selecting the screen that I actually want to capture
  • Ability to record actions on the screen to file for producing live videos.  (possibly something with ffmpeg??)