Copyright | (C) 2007 Andrea Rossato |
---|---|
License | BSD3 |
Maintainer | andrea.rossato@unibz.it |
Stability | unstable |
Portability | portable |
Safe Haskell | Safe-Inferred |
Language | Haskell98 |
This module gives a brief overview of the xmonad internals. It is intended for advanced users who are curious about the xmonad source code and want an brief overview. This document may also be helpful for the beginner/intermediate Haskell programmer who is motivated to write an xmonad extension as a way to deepen her understanding of this powerful functional language; however, there is not space here to go into much detail. For a more comprehensive document covering some of the same material in more depth, see the guided tour of the xmonad source on the xmonad wiki: http://haskell.org/haskellwiki/Xmonad/Guided_tour_of_the_xmonad_source.
If you write an extension module and think it may be useful for others, consider releasing it. Coding guidelines and licensing policies are covered at the end of this document, and must be followed if you want your code to be included in the official repositories. For a basic tutorial on the nuts and bolts of developing a new extension for xmonad, see the tutorial on the wiki: http://haskell.org/haskellwiki/Xmonad/xmonad_development_tutorial.
Synopsis
Writing new extensions
Here are some libraries that may be useful when writing your own module:
- XMonad.Prelude: Re-export commonly used functions from prelude, as well as some xmonad-specific helpers.
Libraries for writing window managers
Starting with version 0.5, xmonad and xmonad-contrib are packaged and distributed as libraries, instead of components which must be compiled by the user into a binary (as they were prior to version 0.5). This way of distributing xmonad has many advantages, since it allows packaging by GNU/Linux distributions while still allowing the user to customize the window manager to fit her needs.
Basically, xmonad and the xmonad-contrib libraries let users write
their own window manager in just a few lines of code. While
~/.xmonad/xmonad.hs
at first seems to be simply a configuration
file, it is actually a complete Haskell program which uses the xmonad
and xmonad-contrib libraries to create a custom window manager.
This makes it possible not only to edit the default xmonad configuration, as we have seen in the XMonad.Doc.Extending document, but to use the Haskell programming language to extend the window manager you are writing in any way you see fit.
xmonad internals
The main
entry point
xmonad installs a binary, xmonad
, which must be executed by the
Xsession starting script. This binary, whose code can be read in
Main.hs
of the xmonad source tree, will use recompile
to run ghc
in order to build a binary from ~/.xmonad/xmonad.hs
.
If this compilation process fails, for any reason, a default main
entry point will be used, which calls the xmonad
function with a default configuration.
Thus, the real main
entry point, the one that even the users' custom
window manager application in ~/.xmonad/xmonad.hs
must call, is
the xmonad
function. This function takes a configuration
as its only argument, whose type (XConfig
)
is defined in XMonad.Core.
xmonad
takes care of opening the connection with the X
server, initializing the state (or deserializing it when restarted)
and the configuration, and calling the event handler
(handle
) that goes into an infinite loop (using
forever
) waiting for events and acting accordingly.
The X monad and the internal state
The event loop which calls handle
to react to events is
run within the X
monad, which is a
StateT
transformer over IO
, encapsulated
within a ReaderT
transformer. The
StateT
transformer encapsulates the
(read/writable) state of the window manager (of type
XState
), whereas the ReaderT
transformer encapsulates the (read-only) configuration (of type
XConf
).
Thanks to GHC's newtype deriving feature, the instance of the
MonadState
class parametrized over
XState
and the instance of the
MonadReader
class parametrized over
XConf
are automatically derived for the X
monad. This way we can use get
,
gets
and modify
for the
XState
, and ask
and
asks
for reading the XConf
.
XState
is where all the sensitive information about
window management is stored. The most important field of the
XState
is the windowset
, whose type
(WindowSet
) is a synonym for a
StackSet
parametrized over a
WorkspaceID
(a String
), a layout type wrapped inside
the Layout
existential data type, the
Window
type, the ScreenID
and the
ScreenDetail
s.
What a StackSet
is and how it can be manipulated
with pure functions is described in the Haddock documentation of the
XMonad.StackSet module.
The StackSet
(WindowSet
) has four
fields:
current
, for the current, focused workspace. This is aScreen
, which is composed of aWorkspace
together with the screen information (for Xinerama support).visible
, a list ofScreen
s for the other visible (with Xinerama) workspaces. For non-Xinerama setups, this list is always empty.hidden
, the list of non-visibleWorkspace
s.floating
, a map from floatingWindow
s toRationalRect
s specifying their geometry.
The Workspace
type is made of a
tag
, a layout
and
a (possibly empty) stack
of windows.
XMonad.StackSet (which should usually be imported qualified, to
avoid name clashes with Prelude functions such as delete
and
filter
) provides many pure functions to manipulate the
StackSet
. These functions are most commonly used as
an argument to windows
, which takes a pure
function to manipulate the WindowSet
and does all the
needed operations to refresh the screen and save the modified
XState
.
During each windows
call, the
layout
field of the current
and
visible
Workspace
s are used to
physically arrange the stack
of windows on each
workspace.
The possibility of manipulating the StackSet
(WindowSet
) with pure functions makes it possible to
test all the properties of those functions with QuickCheck, providing
greater reliability of the core code. Every change to the
XMonad.StackSet module must be accompanied by appropriate QuickCheck
properties before being applied.
Event handling and messages
Event handling is the core activity of xmonad. Events generated by the X server are most important, but there may also be events generated by layouts or the user.
XMonad.Core defines a class that generalizes the concept of events,
Message
, constrained to types with a
Typeable
instance definition (which can be
automatically derived by GHC). Message
s are wrapped
within an existential type SomeMessage
. The
Typeable
constraint allows for the definition of a
fromMessage
function that can unwrap the message with
cast
. X Events are instances of this class, along
with any messages used by xmonad itself or by extension modules.
Using the Typeable
class for any kind of
Message
s and events allows us to define polymorphic functions
for processing messages or unhandled events.
This is precisely what happens with X events: xmonad passes them to
handle
. If the main event handling function doesn't have
anything to do with the event, the event is sent to all visible
layouts by broadcastMessage
.
This messaging system allows the user to create new message types,
simply declare an instance of the Typeable
and use
sendMessage
to send commands to layouts.
And, finally, layouts may handle X events and other messages within the same function... miracles of polymorphism.
The LayoutClass
Coding style
For coding style guidelines while contributing, please see the style guidelines of xmonad's CONTRIBUTING.md.
For examples of Haddock documentation syntax, have a look at its documentation or other extensions. Important points are:
- Every exported function (or even better, every function) should have a Haddock comment explaining what it does, and providing examples.
- Literal chunks of code can be written in comments using "birdtrack" notation (a greater-than symbol at the beginning of each line). Be sure to leave a blank line before and after each birdtrack-quoted section.
- Link to functions by surrounding the names in single quotes, modules in double quotes.
- Literal quote marks and slashes should be escaped with a backslash.
To generate and view the Haddock documentation for your extension, run
stack haddock --no-haddock-deps
If the builds succeeds, at the end stack should tell you where the
generated index.html
is located.
Alternatively, you can also run
cabal haddock
to similar effect.
For more information, see the Haddock documentation: http://www.haskell.org/haddock/doc/html/index.html.
For more information on the nuts and bolts of how to develop your own extension, see the tutorial on the wiki: http://haskell.org/haskellwiki/Xmonad/xmonad_development_tutorial.
Licensing policy
New modules should identify the author, and be submitted under the same license as xmonad (BSD3).