Introduction

The StackSet data type encodes a window manager abstraction. The
window manager is a set of virtual workspaces. On each workspace is a
stack of windows. A given workspace is always current, and a given
window on each workspace has focus. The focused window on the current
workspace is the one which will take user input. It can be visualised
as follows:

Note that workspaces are indexed from 0, windows are numbered
uniquely. A * indicates the window on each workspace that has
focus, and which workspace is current.

The Zipper

We encode all the focus tracking directly in the data structure, with a zipper:

A Zipper is essentially an updateable and yet pure functional
cursor into a data structure. Zipper is also a delimited
continuation reified as a data structure.

The Zipper lets us replace an item deep in a complex data
structure, e.g., a tree or a term, without an mutation. The
resulting data structure will share as much of its components with
the old structure as possible.

and Conor McBride's zipper differentiation paper.
Another good reference is:

The Zipper, Haskell wikibook

Xinerama support

Xinerama in X11 lets us view multiple virtual workspaces
simultaneously. While only one will ever be in focus (i.e. will
receive keyboard events), other workspaces may be passively
viewable. We thus need to track which virtual workspaces are
associated (viewed) on which physical screens. To keep track of
this, StackSet keeps separate lists of visible but non-focused
workspaces, and non-visible workspaces.

Master and Focus

Each stack tracks a focused item, and for tiling purposes also tracks
a master position. The connection between master and focus
needs to be well defined, particularly in relation to insert and
delete.

We puncture the workspace list, producing a hole in the structure
used to track the currently focused workspace. The two other lists
that are produced are used to track those workspaces visible as
Xinerama screens, and those workspaces not visible anywhere.

A stack is a cursor onto a window list.
The data structure tracks focus by construction, and
the master window is by convention the top-most item.
Focus operations will not reorder the list that results from
flattening the cursor. The structure can be envisaged as:

A Stack can be viewed as a list with a hole punched in it to make
the focused position. Under the zipper/calculus view of such
structures, it is the differentiation of a [a], and integrating it
back has a natural implementation used in index.

Construction

O(n). Create a new stackset, of empty stacks, with given tags,
with physical screens whose descriptions are given by m. The
number of physical screens (length m) should be less than or
equal to the number of workspace tags. The first workspace in the
list will be current.

Set focus to the given workspace. If that workspace does not exist
in the stackset, the original workspace is returned. If that workspace is
hidden, then display that workspace on the current screen, and move the
current workspace to hidden. If that workspace is visible on another
screen, the workspaces of the current screen and the other screen are
swapped.

Xinerama operations

Xinerama in X11 lets us view multiple virtual workspaces
simultaneously. While only one will ever be in focus (i.e. will
receive keyboard events), other workspaces may be passively
viewable. We thus need to track which virtual workspaces are
associated (viewed) on which physical screens. To keep track of
this, StackSet keeps separate lists of visible but non-focused
workspaces, and non-visible workspaces.

Operations on the current stack

O(s). Extract the stack on the current workspace, as a list.
The order of the stack is determined by the master window -- it will be
the head of the list. The implementation is given by the natural
integration of a one-hole list cursor, back to a list.

focusUp, focusDown. Move the window focus up or down the stack,
wrapping if we reach the end. The wrapping should model a cycle
on the current stack. The master window, and window order,
are unaffected by movement of focus.

swapUp, swapDown, swap the neighbour in the stack ordering, wrapping
if we reach the end. Again the wrapping model should cycle on
the current stack.

focusUp, focusDown. Move the window focus up or down the stack,
wrapping if we reach the end. The wrapping should model a cycle
on the current stack. The master window, and window order,
are unaffected by movement of focus.

swapUp, swapDown, swap the neighbour in the stack ordering, wrapping
if we reach the end. Again the wrapping model should cycle on
the current stack.

Setting the master window

focusUp, focusDown. Move the window focus up or down the stack,
wrapping if we reach the end. The wrapping should model a cycle
on the current stack. The master window, and window order,
are unaffected by movement of focus.

swapUp, swapDown, swap the neighbour in the stack ordering, wrapping
if we reach the end. Again the wrapping model should cycle on
the current stack.

focusUp, focusDown. Move the window focus up or down the stack,
wrapping if we reach the end. The wrapping should model a cycle
on the current stack. The master window, and window order,
are unaffected by movement of focus.

swapUp, swapDown, swap the neighbour in the stack ordering, wrapping
if we reach the end. Again the wrapping model should cycle on
the current stack.

Composite operations

O(w). shift. Move the focused element of the current stack to stack
n, leaving it as the focused element on that stack. The item is
inserted above the currently focused element on that workspace.
The actual focused workspace doesn't change. If there is no
element on the current stack, the original stackSet is returned.

O(n). shiftWin. Searches for the specified window w on all workspaces
of the stackSet and moves it to stack n, leaving it as the focused
element on that stack. The item is inserted above the currently
focused element on that workspace.
The actual focused workspace doesn't change. If the window is not
found in the stackSet, the original stackSet is returned.