mirror of
https://github.com/0intro/wmii
synced 2024-11-24 22:59:45 +03:00
943 lines
32 KiB
TeX
943 lines
32 KiB
TeX
\chapter{Customizing \wmii}
|
||
|
||
There are several configuration schemes available for \wmii. If
|
||
you're only looking to add basic key bindings, status monitors,
|
||
\emph{et cetera}, you should have no trouble modifying the stock
|
||
configuration for your language of choice. If you're looking for
|
||
deeper knowledge of \wmii's control interface though, this
|
||
section is for you. We'll proceed by building a configuration
|
||
script in \POSIX\ |sh| syntax and then move on to a discussion
|
||
of the higher level constructs in the stock configuration
|
||
scripts.
|
||
|
||
For the purposes of pedagogy, we'll construct the script in the
|
||
literate programming style of Knuth, whereby we construct the
|
||
code in fragments and explain each one in detail. For your
|
||
convenience, each fragment name is linked to its definition.
|
||
|
||
\section{Events}
|
||
|
||
The \wmii\ control interface is largely event driven. Each event
|
||
is represented by a single, plain-text line written to the
|
||
|/event| file. You can think of this file as a named pipe. When
|
||
reading it, you won't receive an EOF\footnote{End of File} until
|
||
\wmii\ exits. Moreover, any lines written to the file will be
|
||
transmitted to everyone currently reading from it. Notable
|
||
events include key presses, the creation and destruction of
|
||
windows, and changes of focus and views.
|
||
|
||
We'll start building our configuration with an event processing
|
||
framework:
|
||
|
||
\begin{Fragment}{Event Loop}
|
||
# Broadcast a custom event
|
||
wmiir xwrite /event Start wmiirc
|
||
|
||
# Turn off globbing
|
||
set -f
|
||
# Open /event for reading
|
||
wmiir read /event |
|
||
# Read the events line by line
|
||
while read line; do
|
||
# Split the line into words, store in $@
|
||
set -- $line
|
||
event=$1; shift
|
||
line = "$(echo $line | sed ‘s/^[^ ]* //’ | tr -d ‘\n’)"
|
||
# Process the event
|
||
case $event in
|
||
Start) # Quit when a new instance starts
|
||
[ $1 = wmiirc ] && exit;;
|
||
«Event Handlers»
|
||
esac
|
||
done
|
||
\end{Fragment}
|
||
|
||
Now, we need to consider which types of events we'll need to
|
||
handle:
|
||
|
||
\begin{Fragment}{Event Handlers}
|
||
«View Button Events»
|
||
«Urgency Events»
|
||
«Unresponsive Clients»
|
||
«Notice Events»
|
||
«Key Events»
|
||
«Client Menu Events»
|
||
«Tag Menu Events»
|
||
\end{Fragment}
|
||
|
||
\section{Bar Items}
|
||
|
||
The bar is described by the files in the two directories |/lbar/| and
|
||
|/rbar/| for buttons on the left and right side of the bar,
|
||
respectively. The files act as control files (section
|
||
\ref{sec:controlfiles}) with the contents:
|
||
|
||
\begin{code}
|
||
color ‹Color Tuple›
|
||
label ‹Label›
|
||
\end{code}
|
||
|
||
A ‹Color Tuple› is defined as:
|
||
|
||
\begin{code}
|
||
‹Color Tuple› ≔ ‹foreground color› ‹background color› ‹border color›
|
||
‹* Color› ≔ ‹RGB color› | ‹RGBA color›
|
||
‹RGB color› ≔ #‹6 character RGB hex color code›
|
||
‹RGBA color› ≔ rgba:‹red›/‹green›/‹blue›/‹alpha›
|
||
\end{code}
|
||
|
||
\noindent
|
||
where all of the colors are represented as lowercase,
|
||
hexidecimal values. In the case of RGBA colors, they may be 1--4
|
||
characters long, though they will be standardized internally to
|
||
2 characters.
|
||
|
||
\medskip
|
||
|
||
Let's define our basic theme information now:
|
||
|
||
\begin{Fragment}{Theme Definitions}
|
||
normcolors=‘#000000 #c1c48b #81654f’
|
||
focuscolors=‘#000000 #81654f #000000’
|
||
background=‘#333333’
|
||
font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’
|
||
\end{Fragment}
|
||
|
||
\subsection{View Buttons}
|
||
|
||
With a basic understanding of bar items in mind, we can write
|
||
our view event handlers:
|
||
|
||
\index{events!CreateTag}
|
||
\index{events!DestroyTag}
|
||
\index{events!FocusTag}
|
||
\index{events!UnfocusTag}
|
||
\begin{Fragment}{View Button Events}
|
||
CreateTag) # CreateTag ‹Tag Name›
|
||
echo $normcolors $1 | wmiir create /lbar/$1;;
|
||
DestroyTag) # DestroyTag ‹Tag Name›
|
||
wmiir rm /lbar/$1;;
|
||
FocusTag) # FocusTag ‹Tag Name›
|
||
wmiir xwrite /lbar/$1 $focuscolors $1;;
|
||
UnfocusTag) # UnfocusTag ‹Tag Name›
|
||
wmiir xwrite /lbar/$1 $normcolors $1;;
|
||
\end{Fragment}
|
||
|
||
\subsection{Urgency}
|
||
|
||
\index{events!UrgentTag|(}
|
||
\index{events!NotUrgentTag|(}
|
||
Windows can specify that they require attention, and in X11
|
||
parlance, this is called urgency\footnote{\ICCCM{4.1.2.4}}. When
|
||
a window requests attention as such, or declares that it's been
|
||
satisfied, \wmii\ broadcasts an event for the client and an
|
||
event for each view that it belongs to. It also fills in the
|
||
layout box of any client deemed urgent. It's the job of a script
|
||
to decide how to handle urgency events above and beyond that
|
||
basic measure. The standard scripts simply mark urgent views
|
||
with an asterisk:
|
||
|
||
\begin{Fragment}{Urgency Events}
|
||
# The urgency events are ‘Client’ events when the program
|
||
# owning the window sets its urgency state. They're ‘Manager’
|
||
# events when wmii or the wmii user sets the state.
|
||
UrgentTag) # UrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
|
||
wmiir xwrite /lbar/$2 $2;;
|
||
NotUrgentTag) # NotUrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
|
||
wmiir xwrite /lbar/$2 $2;;
|
||
\end{Fragment}
|
||
\index{events!UrgentTag|)}
|
||
\index{events!NotUrgentTag|)}
|
||
|
||
\subsection{Notices}
|
||
|
||
The standard scripts provide a custom Notice event for
|
||
displaying status information. The events appear in the long bar
|
||
between the left and right sides for five seconds.
|
||
|
||
\begin{Fragment}{Notice Events}
|
||
Notice)
|
||
wmiir xwrite /rbar/!notice $line
|
||
kill $xpid 2>/dev/null # Let's hope this isn't reused...
|
||
{ sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } &
|
||
xpid = $!;;
|
||
\end{Fragment}
|
||
|
||
\section{Keys}
|
||
|
||
\label{sec:keybindings}
|
||
\index{key bindings}
|
||
\index{filesystem!/!keys}
|
||
\index{filesystem!/!event}
|
||
Now to the part you've no doubt been waiting for: binding keys.
|
||
When binding keys, you need to be aware of two files, |/keys|
|
||
and |/event|. The former defines which keys \wmii\ needs to
|
||
grab, and the latter broadcasts the events when they're pressed.
|
||
|
||
Key names are specified as a series of modifiers followed by a
|
||
key name, all separated by hyphens. Valid modifier names are
|
||
|Control|, |Shift|, |Mod1| (usually Alt), |Mod2|, |Mod3|, |Mod4|
|
||
(usually the Windows® key), and |Mod5|. Modifier keys can be
|
||
changed via |xmodmap(1)|, the details of which are beyond the
|
||
scope of this document.
|
||
|
||
Key names can be detected by running |xev| from a
|
||
terminal, pressing the desired key, and looking at the output
|
||
(it's in the parentheses, after the keysym). Or, more simply,
|
||
you can run the \man 1 {wikeyname} utility bundled with \wmii\
|
||
and press the key you wish to bind.
|
||
|
||
Examples of key bindings:
|
||
|
||
\begin{description}
|
||
\item[Windows® key + Capital A] |Mod4-Shift-A|
|
||
\item[Control + Alt + Space] |Mod1-Control-Space|
|
||
\end{description}
|
||
|
||
Now, let's bind the keys we plan on using:
|
||
|
||
\begin{Fragment}{Bind Keys}
|
||
{
|
||
cat <<!
|
||
Mod4-space
|
||
Mod4-d
|
||
Mod4-s
|
||
Mod4-m
|
||
Mod4-a
|
||
Mod4-p
|
||
Mod4-t
|
||
Mod4-Return
|
||
Mod4-Shift-space
|
||
Mod4-f
|
||
Mod4-Shift-c
|
||
Mod4-Shift-t
|
||
Mod4-h
|
||
Mod4-j
|
||
Mod4-k
|
||
Mod4-l
|
||
Mod4-Shift-h
|
||
Mod4-Shift-j
|
||
Mod4-Shift-k
|
||
Mod4-Shift-l
|
||
!
|
||
for i in 1 2 3 4 5 6 7 8 9 0; do
|
||
echo Mod4-$i
|
||
echo Mod4-Shift-$i
|
||
done
|
||
} | wmiir write /keys
|
||
\end{Fragment}
|
||
|
||
and lay a framework for processing their events:
|
||
|
||
\begin{Fragment}{Key Events}
|
||
Key) # Key ‹Key Name›
|
||
case $1 in
|
||
«Motion Keys»
|
||
«Client Movement Keys»
|
||
«Column Mode Keys»
|
||
«Client Command Keys»
|
||
«Command Execution Keys»
|
||
«Tag Selection Keys»
|
||
«Tagging Keys»
|
||
esac;;
|
||
\end{Fragment}
|
||
|
||
\section{Click Menus}
|
||
|
||
Sometimes, you have your hand on the mouse and don't want to
|
||
reach for the keyboard. To help cope, \wmii\ provides a
|
||
mouse-driven, single-click menu. The default configuration uses
|
||
it for client and tag menus.
|
||
|
||
\begin{Fragment}{Click Menu Initialization}
|
||
clickmenu() {
|
||
if res=$(wmii9menu -- “$@”); then eval “$res”; fi
|
||
}
|
||
\end{Fragment}
|
||
|
||
\section{Control Files}
|
||
|
||
\label{sec:controlfiles}
|
||
|
||
Several directories including the root, have control files,
|
||
named |ctl|. These files are used to control the object (e.g., a
|
||
client or tag) represented by the directory. Each line of the
|
||
file, with the possible section of the first, represents a
|
||
control variable and its value. In the case of all but the root
|
||
|/ctl| file, the first line represents the id of the directory.
|
||
In the case of |/tag/foo/ctl|, for instance, the first line
|
||
should read |foo|. This is useful when dealing with the special
|
||
|sel/| directories. For instance, when |foo| is the selected
|
||
tag, the special |/tag/sel| directory is a link to |/tag/foo|,
|
||
and the first line of |/tag/sel/ctl| will read |foo|, just as
|
||
if you'd accessed |/tag/foo/ctl| directly.
|
||
|
||
The rest of the lines, the control variables, can be modified by
|
||
writing new values to the control file. For instance, if a
|
||
client is fullscreen, its control file will contain the line:
|
||
|
||
\begin{code}
|
||
fullscreen on
|
||
\end{code}
|
||
|
||
\noindent To restore the client from fullscreen, either of the
|
||
following lines may be written to its control file:
|
||
|
||
\begin{code}
|
||
fullscreen off
|
||
fullscreen toggle
|
||
\end{code}
|
||
|
||
When next read, the |fullscreen on| line will have been replaced
|
||
with |fullscreen off|. No care need be taken to preserve the
|
||
other contents of the file. They're generated anew each time
|
||
it's read.
|
||
|
||
\section{Clients}
|
||
|
||
\def\clientlabel{/client/$\langle\mathit{client}\rangle$/}
|
||
\index{filesystem!/client/*/@\clientlabel|(}
|
||
Clients are represented by directories under the |/client/|
|
||
tree. Subdirectory names represent the client's X11 window ID.
|
||
The special |sel/| directory represents the currently selected
|
||
client. The files in these directories are:
|
||
|
||
\begin{description}
|
||
\item[ctl] The client's control file, containing the following
|
||
properties:
|
||
\index{filesystem!/client/*/@\clientlabel!ctl}
|
||
\begin{description}
|
||
\item[allow] The set of unusual actions the client is
|
||
allowed to perform, in the same format as the tag set.
|
||
\begin{description}
|
||
\item[activate] The client is allowed to activate
|
||
itself—that is, focus its window and, as the case may
|
||
require, uncollapse it and select a tag it resides on.
|
||
This flag must be set on a client if you wish it able to
|
||
activate itself from the system tray.
|
||
\end{description}
|
||
\item[floating] Defines whether this client is likely to
|
||
float when attached to a new view. May be |on|, |off|,
|
||
|always|, or |never|. Ordinarilly, the value changes
|
||
automatically whenever the window is moved between the
|
||
floating and managed layers. However, setting a value of
|
||
|always| or |never| overrides this behavior.
|
||
\item[fullscreen] The client's fullscreen state. When
|
||
|on|, the client is displayed fullscreen on all of its
|
||
views. Possible values are |on|, |off|, and |toggle|.
|
||
\item[group] The client's group ID, or |0| if not part of
|
||
a group. Clients tend to open with the same tags and in
|
||
the same columns as the last active member of their
|
||
group. Setting this property is only useful when done
|
||
via the rules file.
|
||
\item[kill] When written, the window is closed politely,
|
||
if possible.
|
||
\item[pid] Read-only value of the PID of the program that
|
||
owns the window, if the value is available and the
|
||
process is on the same machine as wmii.
|
||
\item[slay] When written, the client is disconnected
|
||
peremptorily. If the client's PID is available and the
|
||
process is the same machine as wmii, its parent process
|
||
is killed
|
||
\item[tags] The client's tags. The same as the tags file.
|
||
\item[urgent] The client's urgency state. When |on|, the
|
||
client's layout box will be highlighted. Possible values
|
||
are |on|, |off|, and |toggle|.
|
||
\end{description}
|
||
\item[props] The client's window class (the X11
|
||
|WM_CLASS|\footnote{\ICCCM{4.1.2.5}}
|
||
property) and title string, separated by colons. This file
|
||
is not writable.
|
||
\index{filesystem!/client/*/@\clientlabel!props}
|
||
\item[label] The client's window title. May be written to
|
||
change the client's title.
|
||
\index{filesystem!/client/*/@\clientlabel!label}
|
||
\item[tags]
|
||
\index{filesystem!/client/*/@\clientlabel!tags}
|
||
The client's tags. Tag names are separated by |+|, |-|, or
|
||
|^| signs. Tag names which directly follow a |+| sign are
|
||
added, while whose following a |-| sign are removed and
|
||
those following a |^| are toggled. If the value written
|
||
begins with one of these characters, the value is appended
|
||
to the clients tags rather than replacing them.
|
||
|
||
Tags formatted as |/‹regex›/| are treated as regular
|
||
expressions, which place the client on any extant matching
|
||
tag\footnote{While a client with a regex tag will always
|
||
appear in all matching views, it will not keep those views
|
||
in existence. When the last client explicitly tagged with a
|
||
view is removed, the view is deleted as soon as it becomes
|
||
inactive.}. Regular expression tags which directly follow a
|
||
minus sign are treated as exclusion expressions. For
|
||
example, the tag string |+/foo/-/food/| will match the tag
|
||
|foobar|, but not the tag |foodstand|.
|
||
\end{description}
|
||
|
||
\index{filesystem!/client/*/@\clientlabel|)}
|
||
|
||
\subsection{Key Bindings}
|
||
|
||
To control clients, we'll add the following key bindings:
|
||
|
||
\begin{Fragment}{Client Command Keys}
|
||
Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;;
|
||
Mod4-f) wmiir xwrite /client/sel/ctl Fullscreen toggle;;
|
||
\end{Fragment}
|
||
|
||
And to manage their tags, we'll need:
|
||
|
||
\begin{Fragment}{Tagging Keys}
|
||
Mod4-Shift-t)
|
||
# Get the selected client's id
|
||
c=$(wmiir read /client/sel/ctl | sed 1q)
|
||
# Prompt the user for new tags
|
||
tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
|
||
# Write them to the client
|
||
wmiir xwrite /client/$c/tags $tag;;
|
||
Mod4-Shift-[0-9])
|
||
wmiir xwrite /client/sel/tags ${1##*-};;
|
||
\end{Fragment}
|
||
|
||
\subsection{Click Menus}
|
||
|
||
\index{events!ClientMouseDown}
|
||
\begin{Fragment}{Client Menu Events}
|
||
ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button›
|
||
[ $2 = 3 ] && clickmenu \
|
||
“Delete:wmiir xwrite /client/$1/ctl kill” \
|
||
“Kill: wmiirxwrite /client/$1/ctl slay” \
|
||
“Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on”
|
||
\end{Fragment}
|
||
|
||
\subsection{Unresponsive Clients}
|
||
|
||
\index{events!UnresponsiveClient|(}
|
||
When \wmii\ tries to close a window, it waits 8 seconds for the
|
||
client to respond, and then lets its scripts decide what to do
|
||
with it. The stock scripts prompt the user for input:
|
||
|
||
\begin{Fragment}{Unresponsive Clients}
|
||
UnresponsiveClient) # UnresponsiveClient ‹Client ID›
|
||
{
|
||
# Use wihack to make the xmessage a transient window of
|
||
# the problem client. This will force it to open in the
|
||
# floaing layer of whatever views the client is attached to
|
||
resp=$(wihack -transient $1 \
|
||
xmessage -nearmouse -buttons Kill,Wait -print \
|
||
“The following client is not responding.” \
|
||
“What would you like to do?$(echo)” \
|
||
$(wmiir read /client/$1/label))
|
||
[ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
|
||
} &;;
|
||
\end{Fragment}
|
||
\index{events!UnresponsiveClient|)}
|
||
|
||
\section{Views}
|
||
|
||
\def\taglabel{/tag/$\langle\mathit{tag}\rangle$/}
|
||
\index{filesystem!/tag/*/@\taglabel|(}
|
||
Views are represented by directories under the |/tag/| tree. The
|
||
special |sel/| directory represents the currently selected
|
||
client. The |sel| tag is treated similarly elsewhere. The files
|
||
in these directories are:
|
||
|
||
\begin{description}
|
||
\item[ctl]
|
||
The view's control file. The properties are:
|
||
\index{filesystem!/tag/*/@\taglabel!ctl|(}
|
||
\begin{description}
|
||
\item[select ‹Area›] Select the column ‹Area›, where
|
||
‹Area› is a 1-based column index, or |~| for the floating
|
||
area. It may be optionally preceded by ‹Screen›|:|, where
|
||
‹Screen› is a 0-based Xinerama screen index, or “sel”. When
|
||
omitted, ‹Screen› defaults to 0, the primary screen.
|
||
\item[select ‹Area› ‹Client Index›] Select the column ‹Area›, and
|
||
the ‹Client Index›th client.
|
||
\item[select client ‹Client ID›] Select the client with the
|
||
X11 window ID ‹Client ID›.
|
||
\item[select ‹Direction›]
|
||
Select the client in ‹Direction› where ‹Direction› may be
|
||
one of ‹up $\wedge$ down $\wedge$ left $\wedge$ right›.
|
||
\item[send client ‹Client ID› ‹Area›] Send ‹Client ID› to
|
||
‹Area›. ‹Area› may be |sel| for the selected area, and
|
||
|client ‹Client ID›| may be |sel| for the currently selected
|
||
client.
|
||
\item[send client ‹Client ID› ‹Direction›]
|
||
Send ‹Client ID› to a column or position in its column in
|
||
the given direction.
|
||
\item[send client ‹Client ID› toggle] If ‹Client ID› is
|
||
floating, send it to the managed layer. If it's managed,
|
||
send it to the floating layer.
|
||
\item[swap client ‹Client ID› \ldots] The same as the |send|
|
||
commands, but swap ‹Client ID› with the client at the given
|
||
location.
|
||
\item[colmode ‹Area› ‹Mode›] Set ‹Area›'s mode to ‹Mode›,
|
||
where ‹Mode› is a string of values similar to tag
|
||
specifications. Values which may be added and removed are as
|
||
follows for managed areas:
|
||
|
||
\begin{description}
|
||
\item[stack] One and only one client in the area is
|
||
uncollapsed at any given time. When a new client is
|
||
selected, it is uncollapsed and the previously selected
|
||
client is collapsed.
|
||
\item[max] Collapsed clients are hidden from view
|
||
entirely. Uncollapsed clients display an indicator
|
||
{\it‹n›/‹m›}, where ‹m› is the number of collapsed
|
||
clients directly above and below the client, plus one,
|
||
and ‹n› is the client's index in the stack.
|
||
\item[default] Like subtracting the stack mode, but all
|
||
clients in the column are given equal height.
|
||
\end{description}
|
||
|
||
For the floating area, the values are the same, except that
|
||
in |max| mode, floating clients are hidden when the managed
|
||
layer is selected.
|
||
\item[grow ‹Frame› ‹Direction› {[‹Amount›]}] Grow ‹Frame› in
|
||
the given direction, by ‹Amount›. ‹Amount› may be any
|
||
integer, positive or negative. If suffixed with |px|,
|
||
it specifies an exact pixel amount, otherwise it specifies a
|
||
“reasonable increment”. Defaults to 1.
|
||
|
||
‹Frame› may be one of:
|
||
\begin{itemize}
|
||
\item client ‹Client ID›
|
||
\item ‹Area› ‹Client Index›
|
||
\end{itemize}
|
||
\item[nudge ‹Frame› ‹Direction› {[‹Amount›]}] Like
|
||
|grow|, but move the client in ‹Direction› instead of
|
||
resizing it.
|
||
\end{description}
|
||
\index{filesystem!/tag/*/@\taglabel!ctl|)}
|
||
\end{description}
|
||
|
||
\index{filesystem!/tag/*/@\taglabel|)}
|
||
|
||
\subsection{Key Bindings}
|
||
|
||
We'll use the following key bindings to interact with views:
|
||
|
||
\begin{Fragment}{Motion Keys}
|
||
Mod4-h) wmiir xwrite /tag/sel/ctl select left;;
|
||
Mod4-l) wmiir xwrite /tag/sel/ctl select right;;
|
||
Mod4-k) wmiir xwrite /tag/sel/ctl select up;;
|
||
Mod4-j) wmiir xwrite /tag/sel/ctl select down;;
|
||
Mod4-space) wmiir xwrite /tag/sel/ctl select toggle;;
|
||
\end{Fragment}
|
||
|
||
\begin{Fragment}{Client Movement Keys}
|
||
Mod4-Shift-h) wmiir xwrite /tag/sel/ctl send sel left;;
|
||
Mod4-Shift-l) wmiir xwrite /tag/sel/ctl send sel right;;
|
||
Mod4-Shift-k) wmiir xwrite /tag/sel/ctl send sel up;;
|
||
Mod4-Shift-j) wmiir xwrite /tag/sel/ctl send sel down;;
|
||
Mod4-Shift-space) wmiir xwrite /tag/sel/ctl send sel toggle;;
|
||
\end{Fragment}
|
||
|
||
\begin{Fragment}{Column Mode Keys}
|
||
Mod4-d) wmiir xwrite /tag/sel/ctl colmode sel -stack-max;;
|
||
Mod4-s) wmiir xwrite /tag/sel/ctl colmode sel stack-max;;
|
||
Mod4-m) wmiir xwrite /tag/sel/ctl colmode sel stack+max;;
|
||
\end{Fragment}
|
||
|
||
\subsection{Click Menus}
|
||
|
||
\index{events!LeftBarMouseDown}
|
||
\begin{Fragment}{Tag Menu Events}
|
||
LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name›
|
||
[ $1 = 3 ] && clickmenu \
|
||
“Delete:delete_view $2”
|
||
\end{Fragment}
|
||
|
||
\section{Command and Program Execution}
|
||
|
||
Perhaps the most important function we need to provide for is
|
||
the execution of programs. Since \wmii\ users tend to use
|
||
terminals often, we'll add a direct shortcut to launch one.
|
||
Aside from that, we'll add a menu to launch arbitrary programs
|
||
(with completions) and a separate menu to launch wmii specific
|
||
commands.
|
||
|
||
We use |wmiir setsid| to launch programs with their own session
|
||
IDs to prevent untoward effects when this script dies.
|
||
|
||
\begin{Fragment}{Command Execution Initialization}
|
||
terminal() { wmiir setsid xterm “$@” }
|
||
proglist() {
|
||
IFS=:
|
||
wmiir proglist $1 | sort | uniq
|
||
unset IFS
|
||
}
|
||
\end{Fragment}
|
||
|
||
\subsection{Key Bindings}
|
||
\begin{Fragment}{Command Execution Keys}
|
||
Mod4-Return) terminal & ;;
|
||
Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;;
|
||
Mod4-a) {
|
||
set -- $(proglist $WMII_CONFPATH | wimenu)
|
||
which=$(which which)
|
||
prog=$(PATH=$WMII_CONFPATH $which $1); shift
|
||
eval exec $prog “$@”
|
||
} &;;
|
||
\end{Fragment}
|
||
|
||
\section{The Root}
|
||
|
||
The root filesystem contains the following:
|
||
|
||
\index{!filesystem!/|(}
|
||
\begin{description}
|
||
\item[ctl] The control file. The properties are:
|
||
\index{filesystem!/!ctl}
|
||
\begin{description}
|
||
\item[bar on ‹top $\wedge$ bottom›] Controls where the bar
|
||
is shown.
|
||
\item[border] The border width, in pixels, of floating
|
||
clients.
|
||
\item[colmode ‹Mode›] The default column mode for newly
|
||
created columns.
|
||
\item[focuscolors ‹Color Tuple›] The colors of focused
|
||
clients.
|
||
\item[normcolors ‹Color Tuple›] The colors of unfocused
|
||
clients and the default color of bar buttons.
|
||
\item[font ‹Font›] The font used throughout \wmii. If
|
||
prefixed with |xft:|, the Xft font renderer is used, and
|
||
fonts may be antialiased. Xft font names follow the
|
||
fontconfig formula. For instance, 10pt, italic Lucida
|
||
Sans would be specified as
|
||
|
||
\begin{code}
|
||
xft:Lucida Sans-10:italic
|
||
\end{code}
|
||
|
||
See \man 1 {fc-match}.
|
||
|
||
\item[grabmod ‹Modifier Keys›] The key which must be
|
||
pressed to move and resize windows with the mouse
|
||
without clicking hot spots.
|
||
\item[incmode ‹Mode›] Controls how X11 increment hints are
|
||
handled in managed mode. Possible values are:
|
||
\begin{description}
|
||
\item[ignore] Increment hints are ignored entirely.
|
||
Clients are stretched to fill their full allocated
|
||
space.
|
||
\item[show] Gaps are shown around managed client
|
||
windows when their increment hints prevent them from
|
||
filling their entire allocated space.
|
||
\item[squeeze] When increment hints cause gaps to show
|
||
around clients, \wmii\ will try to adjust the sizes
|
||
of the clients in the column to minimize lost space.
|
||
\end{description}
|
||
\item[view ‹Tag›] Change the currently visible view.
|
||
\item[exec ‹Command›] Replaces this \wmii\ instance with
|
||
‹Command›. ‹Command› is split according to rc quoting
|
||
rules, and no expansion occurs. If the command fails to
|
||
execute, \wmii\ will respawn.
|
||
\item[spawn ‹Command›] Spawns ‹Command› as it would spawn
|
||
|wmiirc| at startup. If ‹Command› is a single argument
|
||
and doesn't begin with |/| or |./|,%
|
||
\hskip 1ex|$WMII_CONF|\-|PATH| is
|
||
searched for the executable. Otherwise, the whole
|
||
argument is passed to the shell for evaluation.
|
||
\end{description}
|
||
\item[keys] The global keybindings. See section \ref{sec:keybindings}.
|
||
\index{filesystem!/!keys|primary}
|
||
\item[event] The global event feed. See section \ref{sec:keybindings}.
|
||
\index{filesystem!/!event|primary}
|
||
\item[colrules]
|
||
\index{filesystem!/!colrules}
|
||
The |/colrules| file contains a list of
|
||
rules which affect the width of newly created columns.
|
||
Rules have the form:
|
||
|
||
\begin{quote}\texttt{
|
||
/‹regex›/ -> ‹width›{\color{gray}[}+‹width›{\color{gray}]*}}
|
||
\end{quote}
|
||
|
||
Where,
|
||
|
||
\begin{code}
|
||
‹width› ≔ ‹percent of screen› | ‹pixels›px
|
||
\end{code}
|
||
|
||
When a new column, ‹n›, is created on a view whose name
|
||
matches ‹regex›, it is given the ‹n›th supplied ‹width›.
|
||
If there is no ‹n›th width, it is given
|
||
$1/\mbox{‹ncol›th}$ of the screen.
|
||
|
||
\item[rules]
|
||
\index{filesystem!/!rules}
|
||
The |/rules| file contains a list of
|
||
rules similar to the colrules. These rules set
|
||
properties for a client when it is created.
|
||
Rules are specified:
|
||
|
||
\begin{quote}\texttt{
|
||
/‹regex›/ -> ‹key›{\color{gray}=}‹value› {\color{gray}\ldots}}
|
||
\end{quote}
|
||
|
||
When a client's ‹name›:‹class›:‹title› matches
|
||
‹regex›, the matching rules are applied. For each
|
||
‹key›=‹value› pair, the |ctl| file property matching
|
||
‹key› is set to ‹value›. Additionally, the following
|
||
keys are accepted and have special meaning:
|
||
|
||
\begin{description}
|
||
\item[continue]
|
||
Normally, when a matching rule is encountered,
|
||
rule matching stops. When the continue key is
|
||
provided (with any value), matching continues at
|
||
the next rule.
|
||
\item[force-tags]
|
||
Like tags, but overrides any settings obtained
|
||
obtained from the client's group or from the
|
||
|_WMII_TAGS| window property.
|
||
\end{description}
|
||
|
||
\end{description}
|
||
|
||
\index{!filesystem!/|)}
|
||
|
||
\subsection{Configuration}
|
||
|
||
We'll need to let \wmii\ know about our previously defined theme
|
||
information:
|
||
|
||
\begin{Fragment}{Configuration}
|
||
«Theme Definitions»
|
||
|
||
xsetroot -solid $background
|
||
wmiir write /ctl <<!
|
||
border 2
|
||
focuscolors $focuscolors
|
||
normcolors $normcolors
|
||
font $font
|
||
grabmod Mod4
|
||
!
|
||
\end{Fragment}
|
||
|
||
\subsection{Key Bindings}
|
||
|
||
And we need a few more key bindings to select our views:
|
||
|
||
\begin{Fragment}{Tag Selection Keys}
|
||
Mod4-t)
|
||
# Prompt the user for a tag
|
||
tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
|
||
# Write it to the filesystem.
|
||
wmiir xwrite /ctl view $tags;;
|
||
Mod4-[0-9])
|
||
wmiir xwrite /ctl view ${1##*-};;
|
||
\end{Fragment}
|
||
|
||
\section{Tieing it All Together}
|
||
|
||
\begin{code}
|
||
#!/bin/sh
|
||
«Click Menu Initialization»
|
||
«Command Execution Initialization»
|
||
|
||
«Configuration»
|
||
|
||
«Bind Keys»
|
||
«Event Loop»
|
||
\end{code}
|
||
|
||
\section{The End Result}
|
||
|
||
For clarity, here is the end result:
|
||
|
||
\begin{code}
|
||
#!/bin/sh
|
||
# «Click Menu Initialization»
|
||
clickmenu() {
|
||
if res=$(wmii9menu -- “$@”); then eval “$res”; fi
|
||
}
|
||
# «Command Execution Initialization»
|
||
terminal() { wmiir setsid xterm “$@” }
|
||
proglist() {
|
||
IFS=:
|
||
wmiir proglist $1 | sort | uniq
|
||
unset IFS
|
||
}
|
||
|
||
# «Configuration»
|
||
# «Theme Definitions»
|
||
normcolors=‘#000000 #c1c48b #81654f’
|
||
focuscolors=‘#000000 #81654f #000000’
|
||
background=‘#333333’
|
||
font=‘drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*’
|
||
|
||
xsetroot -solid $background
|
||
wmiir write /ctl <<!
|
||
border 2
|
||
focuscolors $focuscolors
|
||
normcolors $normcolors
|
||
font $font
|
||
grabmod Mod4
|
||
!
|
||
|
||
# «Bind Keys»
|
||
{
|
||
cat <<!
|
||
Mod4-space
|
||
Mod4-d
|
||
Mod4-s
|
||
Mod4-m
|
||
Mod4-a
|
||
Mod4-p
|
||
Mod4-t
|
||
Mod4-Return
|
||
Mod4-Shift-space
|
||
Mod4-f
|
||
Mod4-Shift-c
|
||
Mod4-Shift-t
|
||
Mod4-h
|
||
Mod4-j
|
||
Mod4-k
|
||
Mod4-l
|
||
Mod4-Shift-h
|
||
Mod4-Shift-j
|
||
Mod4-Shift-k
|
||
Mod4-Shift-l
|
||
!
|
||
for i in 1 2 3 4 5 6 7 8 9 0; do
|
||
echo Mod4-$i
|
||
echo Mod4-Shift-$i
|
||
done
|
||
} | wmiir write /keys
|
||
|
||
# «Event Loop»
|
||
# Broadcast a custom event
|
||
wmiir xwrite /event Start wmiirc
|
||
|
||
# Turn off globbing
|
||
set -f
|
||
# Open /event for reading
|
||
wmiir read /event |
|
||
# Read the events line by line
|
||
while read line; do
|
||
# Split the line into words, store in $@
|
||
set -- $line
|
||
event=$1; shift
|
||
line = "$(echo $line | sed ‘s/^[^ ]* //’ | tr -d ‘\n’)"
|
||
|
||
# Process the event
|
||
case $event in
|
||
Start) # Quit when a new instance starts
|
||
[ $1 = wmiirc ] && exit;;
|
||
|
||
# «Event Handlers»
|
||
# «View Button Events»
|
||
CreateTag) # CreateTag ‹Tag Name›
|
||
echo $normcolors $1 | wmiir create /lbar/$1;;
|
||
DestroyTag) # DestroyTag ‹Tag Name›
|
||
wmiir rm /lbar/$1;;
|
||
FocusTag) # FocusTag ‹Tag Name›
|
||
wmiir xwrite /lbar/$1 $focuscolors $1;;
|
||
UnfocusTag) # UnfocusTag ‹Tag Name›
|
||
wmiir xwrite /lbar/$1 $normcolors $1;;
|
||
|
||
# «Urgency Events»
|
||
# The urgency events are ‘Client’ events when the program
|
||
# owning the window sets its urgency state. They're ‘Manager’
|
||
# events when wmii or the wmii user sets the state.
|
||
UrgentTag) # UrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
|
||
wmiir xwrite /lbar/$2 $2;;
|
||
NotUrgentTag) # NotUrgentTag ‹‘Client’ or ‘Manager’› ‹Tag Name›
|
||
wmiir xwrite /lbar/$2 $2;;
|
||
|
||
# «Unresponsive Clients»
|
||
UnresponsiveClient) # UnresponsiveClient ‹Client ID›
|
||
{
|
||
# Use wihack to make the xmessage a transient window of
|
||
# the problem client. This will force it to open in the
|
||
# floaing layer of whatever views the client is attached to
|
||
resp=$(wihack -transient $1 \
|
||
xmessage -nearmouse -buttons Kill,Wait -print \
|
||
“The following client is not responding.” \
|
||
“What would you like to do?$(echo)” \
|
||
$(wmiir read /client/$1/label))
|
||
[ $resp = Kill ] && wmiir xwrite /client/$1/ctl slay
|
||
} &;;
|
||
|
||
# «Notice Events»
|
||
Notice)
|
||
wmiir xwrite /rbar/!notice $line
|
||
kill $xpid 2>/dev/null # Let's hope this isn't reused...
|
||
{ sleep 5; wmiir xwrite /rbar/!notice ‘ ’; } &
|
||
xpid = $!;;
|
||
|
||
# «Key Events»
|
||
Key) # Key ‹Key Name›
|
||
case $1 in
|
||
# «Motion Keys»
|
||
Mod4-h) wmiir xwrite /tag/sel/ctl select left;;
|
||
Mod4-l) wmiir xwrite /tag/sel/ctl select right;;
|
||
Mod4-k) wmiir xwrite /tag/sel/ctl select up;;
|
||
Mod4-j) wmiir xwrite /tag/sel/ctl select down;;
|
||
Mod4-space) wmiir xwrite /tag/sel/ctl select toggle;;
|
||
|
||
# «Client Movement Keys»
|
||
Mod4-Shift-h) wmiir xwrite /tag/sel/ctl send sel left;;
|
||
Mod4-Shift-l) wmiir xwrite /tag/sel/ctl send sel right;;
|
||
Mod4-Shift-k) wmiir xwrite /tag/sel/ctl send sel up;;
|
||
Mod4-Shift-j) wmiir xwrite /tag/sel/ctl send sel down;;
|
||
Mod4-Shift-space) wmiir xwrite /tag/sel/ctl send sel toggle;;
|
||
|
||
# «Column Mode Keys»
|
||
Mod4-d) wmiir xwrite /tag/sel/ctl colmode sel -stack-max;;
|
||
Mod4-s) wmiir xwrite /tag/sel/ctl colmode sel stack-max;;
|
||
Mod4-m) wmiir xwrite /tag/sel/ctl colmode sel stack+max;;
|
||
|
||
# «Client Command Keys»
|
||
Mod4-Shift-c) wmiir xwrite /client/sel/ctl kill;;
|
||
Mod4-f) wmiir xwrite /client/sel/ctl fullscreen toggle;;
|
||
|
||
# «Command Execution Keys»
|
||
Mod4-Return) terminal & ;;
|
||
Mod4-p) eval exec wmiir setsid “$(proglist $PATH | wimenu)” &;;
|
||
Mod4-a) {
|
||
set -- $(proglist $WMII_CONFPATH | wimenu)
|
||
prog=$(PATH=$WMII_CONFPATH which $1); shift
|
||
eval exec $prog “$@”
|
||
} &;;
|
||
|
||
# «Tag Selection Keys»
|
||
Mod4-t)
|
||
# Prompt the user for a tag
|
||
tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
|
||
# Write it to the filesystem.
|
||
wmiir xwrite /ctl view $tag;;
|
||
Mod4-[0-9])
|
||
wmiir xwrite /ctl view ${1##*-};;
|
||
|
||
# «Tagging Keys»
|
||
Mod4-Shift-t)
|
||
# Get the selected client's id
|
||
c=$(wmiir read /client/sel/ctl | sed 1q)
|
||
# Prompt the user for new tags
|
||
tags=$(wmiir ls /tag | sed ‘s,/,,; /^sel$/d’ | wimenu)
|
||
# Write them to the client
|
||
wmiir xwrite /client/$c/tags $tag;;
|
||
Mod4-Shift-[0-9])
|
||
wmiir xwrite /client/sel/tags ${1##*-};;
|
||
|
||
esac;;
|
||
|
||
# «Client Menu Events»
|
||
ClientMouseDown) # ClientMouseDown ‹Client ID› ‹Button›
|
||
[ $2 = 3 ] && clickmenu \
|
||
“Delete:wmiir xwrite /client/$1/ctl kill” \
|
||
“Kill:wmiir xwrite /client/$1/ctl slay” \
|
||
“Fullscreen:wmiir xwrite /client/$1/ctl fullscreen on”
|
||
|
||
# «Tag Menu Events»
|
||
LeftBarMouseDown) # LeftBarMouseDown ‹Button› ‹Bar Name›
|
||
[ $1 = 3 ] && clickmenu \
|
||
“Delete:delete_view $2”
|
||
esac
|
||
done
|
||
\end{code}
|
||
|