FVWM Cookbook FAQ

This is a work in progress, but this page is intended to demonstrate
some tasks that a lot of people want to do in FVWM, but are unsure how.
This page is born out of frustration -- mostly on my part since over the
past few years, I have come to realise that I've been answering the same
questions, as various different incantations. Most of the questions and
their corresponding answers you see here come directly from the fvwm forums and the #fvwm IRC channel, on freenode.

At some point, I will be moving most of what will end up on this page
to the FvwmWiki -- but for the time
being, I'll maintain this static page, and probably keep both the wiki
page and this page in synch, when I end up moving the contents over to
it.

Lastly, bear with me with regards to the organisation of this page -- I
haven't yet decided how best to organise the various questions -- or
whether they need categories. Any comments or suggestions are of course
welcome.

This question is relatively straight-forward, and I remember having
some fun answering it on IRC. FVWM currently (in the 2.5.x trunk) has
the option for 'CenterPlacement' -- which will place window(s) in the
centre of the screen. For reasons unknown, the question asked was how to
get FVWM to place windows to the top-right corner of the screen.

The answer cannot come directly from assumed geometries -- that is, it
wouldn't just be sufficient to manually place any window and take the
geometry of it, and then apply that to all windows we wanted, since the
size of each window will differ. (The geometry of a window dictates not
only the position on the screen, but also its width and height.)

Luckily though, FVWM has all sorts of internal variables that it sets
for specific items. Some of them include the width and height of the current
window, and the width and height of the viewport (screen). So in that
way, the positioning can be done on a per-window basis -- which is what we
want. We can use a relative position for the window, hence:

There's a number of things you could do with this function. As it
stands it has yet to be called. Functions are context sensitive -- and
it's unusual you'd find a function being called that wouldn't perhaps be
acting upon a window or groups of windows (oh -- there's instances where
the option is undesirable, perhaps I'll leave that for another time.) One
thing you might do is bind it to FvwmEvent, for all windows that get
created. Here's how you would do that:

DestroyModuleConfig FE-movewindow: *
*FE-movewindow: Cmd Function
*FE-movewindow: add_window MoveWindowTopRight
# Start the module -- could also use:
#
# AddToFunc StartFunction I Module FvwmEvent FE-movewindow
#
# If it was to be used at every FVWM (re)start.
Module FvwmEvent FE-movewindow

Currently though, the MoveWindowTopLeft function moves all
windows to the top-right of the screen. This is probably not something
desirable. But there are ways of qualifying it. If you just wanted a
single window with a specific name, then in the function, you can replace:

+ I ThisWindow

... with:

+ I ThisWindow (name_of_window)

Note that the use of ThisWindow is superfluous in this case
when it is used on its own since the function is already running
within a window context. It doesn't hurt to use it though, especially
if you want to make absolutely sure that you'll be referencing a
window. Where it is useful is qualifying a specific window, as
explained above.

Or, you might decide that you wanted all windows to go to the top-right
of the screen, except certain window, in which case you could write:

+ I ThisWindow (!"thiswindow|thatwindow|theotherwindow")

There's a lot you can do with it. You might decide you'd rather have
the windows at the bottom left, but then that just becomes a question of
mathematics, and is hence left as an exercise to the reader.

WP2. Remembering the state of maximised windows.

This question was originally asked in IRC, although it since migrated
to the fvwm forums. I often see questions like this -- and what they
really amount to is: "I want to emulate part of the functionality of a
session manager, but don't want to run one". That's fair enough --
although most session managers have the option of excluding certain
applications.

The trick to this, is to get FVWM to remember the applications that
were maximized when they are closed, and then restored that way when
they're next open again. The solution proposed isn't the best, and it is
probably better to use FvwmPerl. But for something that works quickly, here's
what I came up with.

First thing to think about is FvwmEvent. That will have to be used in
order to catch the events of the windows being mapped and unmapped (opened
and destroyed, to use laymans' terms.)

So the FvwmEvent instance FE-Maximize just sets up listeners
for {add,destroy}_window, and will call those functions as necessary.
That's the easy bit -- FvwmEvent really does simplify things for us.

The next stage is to determine how to store the state
of those applications. The easiest way is to use a flat-file that just
stores a list of window names or classes. This file is the one that used
to make sure windows created get maximized, if they were maximized when
they were closed/destroyed. So this file needs to be read in a line at a
time, and each entry needs to be matched against the window that has been
created. It's not as complex as it sounds:

Note that I've made a decision to use the the window's class as opposed
to its actual name. That's important, since not all the names of windows
are consistent -- just look at Mozilla or Firefox. So uniqueness is
important -- and the class of window is unlikely to change, unless the
application allows for it via a command-line option, but then that becomes
a PEBCAK issue. :)

The next step is to then record the window if applicable, when any
window is destroyed. This is a little more involved than the previous
function, since we need to check to see whether the window is maximized or
not.

This part checks to see whether the window has been destroyed, and whether it
is maximized. If it is, then it greps the windowlist file for
previous entries of the same class name. If nothing is found in that
file, then the class name of the window is recorded in the
windowlist file to be maximized next time the window is matched.

This checks to see that for a window being closed that isn't maximised;
and that was previously listed as maximised, that the entry for it in the
windowlist file is removed, so that it isn't maximised next time
around.

One caveat with section above, is the use of sed. The "-i" flag was
introduced into sed recently, and certaintly isn't compatible with older
versions across different unixes. So for portability the following ought
to be used:

There's plenty of other variations -- such as recording iconic states,
and so forth.

WP3. Starting applications maximised.

Normally, most windows that appear on screen start up in a normal
state which is anything but maximized. Sometimes though, it's desirable
to have windows start up opened in a maximized state. In such instances,
FvwmEvent can be used to ensure certain windows are maximized.

As with most of the examples we've seen so far, the above just sets up
a module config, which we be using as an alias to the FvwmEvent module.
The module is then started. Then all that has to happen is for each
window that appears on the screen, to be given a command of maximize,
hence:

Sometimes there is a need to make sure that only one instance of a
running application is active, and any attempt to run another one silently
fails. It's rather specific, but is useful in some situations. For
example, I have a screen session that I have active. Rather than
reloading it and attaching it, I like to just warp to wherever the window
is. If the window isn't active, then it is lauched.

So the above looks for any window that is on the current desk, and if
it is, the focus is sent to that window. Else, if there isn't (TestRc
(NoMatch)), and there really isn't a window with that name, then it
is started. Note that there is quite a few ways that you can write the
above function.

As to how it is invoked, there's any number of ways. In the form as it
is above, it can only match based on the function receiving parameters --
so this would suggest something along the lines of key-binings, as
in:

So that "$0" in the function is expanded to whatever is passed into the
function -- in this case, either "screen1", or, "xine", or "irssi". The
drawback to this approach is that the applications being checked/launched
can only be done so via FVWM -- be it via key bindings as demonstrated, or
mouse bindings, etc. Launching, say, irssi, from an xterm by-passes the
use of the function, and so the window will start up, when we might not
want it to. In situations like that, it's best to use FvwmEvent (see:
Q. WP2 -- in part, anyway.) The function will need
adapting.

WC2. Switching focus between windows. (Alt-Tab)

I don't like the default behaviour of FVWM's Alt-Tab key combination --
yes, the WindowList is useful, but it's not what I want to see when I want
to focus other windows --- I prefer (dare I say it) functionality similar
to MS-Windows. I'd much rather be able to cycle windows. There's some
suggestions in the main FVWM FAQ about how
to do this, although that still didn't quite emulate what I was after ---
indeed, none of the solutions cycled back round to the first window. So,
here's my solution to it.

The SwitchWindow function does all the work. It might look elaborate,
but all it is doing is flipping the value of Next and Prev into the DIR
environment variable, so that if the last window is reached, it goes back
to the first. The schedule command is used because without it, the
focusing of windows gets confused -- so the delay via the Schedule
commands ensures this isn't the case. The caveat being that switching
windows can be ever so slightly slow.

WC3. Change of decor from focus/property change.

I originally wrote this for a friend of mine (hi, Heather). She wanted
sticky windows to have a different decor to any other window. The effect
overall is quite interesting, and could easily be adapted. The first step
is to define a decor for the sticky windows. Example:

This looks more complex than it actually is -- and it's beyond the
scope of this document to discuss how the decor works. Suffice it to say
that with the above decor, the buttons of a window will inherit different
pixmaps.

The main focus (no pun intended) of this is to then decide which events
need to be flagged to change those windows that will become sticky. When
a window receives focus, we will need to check to see if it is sticky (the
state of the window might have been modified, without necessarily using
something like the mouse, which would send a focus event.) Also, since
toggling the state of a window is happening, we will need to check for the
ConfigureNotify event. Lastly, we will also want to check the state of
any windows that are mapped on the screen, since they might explicitly
have a style line declaring them Sticky. Hence:

The last part, is of course, to define the action that is to be called
when any one of those events occurs. It's simple. All that has to be
done is a 'toggle' effect, essentially testing whether a window is sticky,
and if it is to change the decor, and vice-versa.

Note that there's any number of things you could do with this, and it's
not so much the application of what has been done, but what you _could_ do
with it. :) You might be wondering why I am using 'ThisWindow' as
opposed to 'Current'. (You might not, but...) 'Current' assumes that the
operand window is the window that currently has the focus. In this
situation, it's probably true that it will, but that might not always be
the case.

WC4. Different decors/colorsets for different pages/desks.

This is very similar to Q. WC3 in that this is more
a variation on a theme. The difference here though is that rather than
relying on a change of property of a window, there's a need to do
something when a new page/desk event occurs -- and then for whichever page
or desk requires the new decors, to adapt those for all windows (in this
situation, anyway.) You could construct all sorts of elaborate things
with this idea.

For simplicity's sake, let's assume that the desktop and page we want
to flag as different is "0 0 0". The first thing we should do is setup
FvwmEvent. Remember that all we need to do is to listen for a change of
page/desk:

Then the fun starts -- defining the FvwmFuncNewStyle function
that will ultimately do all the hard work for us. The logic behind this
is when a change of desk/page happens, make sure that we're on desk 0,
page 0 0. If we are, then change all windows to use a decor or colorset,
or what have you. In addition to that, when we leave "0 0 0", we'll need
to restore the decor/colorsets to what they were originally.

Here, I have said that all windows that are not iconic (!Iconic), and
that accept focus (AcceptsFocus). (In the case of applying colorset styles
to windows this is important, since if the window cannot accept focus,
then the hilight colourset is useless applied to it, and just wastes
precious little memory), and that are on the current page to have
an inactive colorset defined as 0, and an active one defined as 3. Hence
those might look like the following:

Colorset 0 fg black, bg #60a0c0
Colorset 3 fg black, bg darkgreen

The caveat here though is that the Colorset command is only
applicable to FVWM 2.5.X -- in FVWM stable (in the 2.4.X trunk -- at this
time of writing) uses FvwmTheme to handle such things. So in order for
this to work, you would have to use SendToModule FvwmTheme
.....

RestorDecor was the other function that we had defined
earlier on. That will get run on all pages to restore windows to some
'default' hilight colorset:

And you could do other things with these functions. You could
incorporate it into Q. WC3 so that you selectively
flagged different sticky windows. Or you could (as the title of this
question alludes to) use these functions to change decors, and so forth.
All good fun... :)

WC5. Shading of windows to move titlebar in the same
direction.

FVWM has the ability to shade windows in all directions: North, South,
East, West, and diagionally between those. Whilst this is useful, what
the shading currently does not do, is move the titlebar to the correct
side of the window, based on the direction the window is being shaded. So
for example, if a window is told to shade to the left, then all one would
likely see is two vertical borders juxtaposed, and nothing more -- the
titlebar would have shaded inside it. The following function best shows
this in action, as describing it takes far too many words:

Clicking once with mouse button 1 on any of the window sides/frame
would raise the window. Double-clicking on the window would shade the
window in that direction -- try it, and you'll see what happens. To
de-shade it, just double-click again. Fun, eh? The trick to that relies
on what $[func.context] expands to. Whenever a window is shaded, it is
told in which direction it is doing so -- FVWM translates that into
various 'symbols' that depict a given direction. So, in order to move the
titlebar in the same direction, we just need to trnslate those symbols to
directions, and set the titlebar location. We'll need a helper function
for that, though:

So you can see from the above that when $[func.context] interpolates to
a "[" that the window is shading to the left, and "]" to the right, etc.
Then knowing that means the style of the window to which the shade
operation is being applied need just be told where to put the titlebar.
The last thing left to do is adapt the RaiseOrShade function to
use this helper function:

FvwmButtons is an extremely useful module in that it has the ability to
swallow applications and house them as containers. Most people usually
just leave it at that, although FvwmButtons' use can get quite involved
with some needed 'added extras' to crop up from time to time. To take a
simple example, let's imagine that a button with an FvwmButtons instance
controls the sound volume -- and that the desired effect is to have the
icon change when the volume is muted, and restored when unmuted, etc.,
etc.

This requires flagging the correct button within the FvwmButtons
instance so that it can be 'referenced' from within FVWM itself -- via
functions, menus, and so forth. Here's an example of a snippet of a
FvwmButtons instance:

Hence, we can now refer to this button as button A. Now we
have to concentrate on changing the icon to reflect the state of the
volume. The menu entries which hold the volume increments might be
defined as the following:

This is fine, and will work, but we have yet to connect the actions of
the menu operations, and the changing of the icon. The continual
duplication of the "Exec exec" lines for the same program (but with
different parameters) can be smartened up into a function call:

By separating out the functionality into a different function, we only
then have to change one item, if any changes to the calling program need
to be made, hence the following will provide the same functionality as in
the original version -- the only difference is that we're passing in the
volume level as a parameter.

Added to which we can now add to that function the definition to change
the FvwmButtons icon:

+ I SendToModule OSXDock ChangeButton "A" Icon v4.png

The SendToModule command accepts some parameters -- the first
one is the module alias of the module we're wishing to send the command
to. In this case, it is 'OSXDock'. The next command is 'ChangeButton'
which accepts a parameter -- the button Id we defined earlier on, and then
the last parameter is the new icon the button is to take on.

The sharp-eyed amongst you will have realised that for a volume of
zero; we will need to change the icon to mute. We can do this by
calling for that value only, a different function instance:

This is mostly similar to Q. MC2 although where some
problems lie is in the differences between the current stable (2.4.X) and
unstable (2.5.X) releases of FVWM. In the unstable release, one can use
the following style option to disclude the possibility of a window being
iconified:

Style FvwmButtons !Iconifiable

Then, to iconify windows, one might use:

All (CurrentPage, !Iconic) Iconify

... which one might bind solely to a key binding, or a mouse binding,
or put it as part of a complex function that could get called.

In FVWM 2.4.X however, one would have to qualify the window
explicitly that wasn't going to be iconified:

All (CurrentPage, !Iconic, !MyWindowName) Iconify

MC2. "Show Desktop" Feature.

One thing that again has been borrowed from MS-Windows is the idea of
"Show Desktop" -- that is, making the root window visible. In it's
simplest form, it can be emulated as per Zahnklinik Ungarn ,
although what this does not do is take into account the restoration of
windows to their original position afterwards. Indeed, the application of
this is probably best suited to a button inside an FvwmButtons instance --
and this is what we'll demonstrate here.

In its simplest form one can iconify all windows with the following
command:

All (CurrentPage, !Iconic) Iconify

... which would iconify windows on the current page. But the problem
then arises of making sure that the toggle action restores the original
windows to their state, Now, FVWM allows for a window, or windows to be
flagged via a number (anywhere between 0 - 31 inclusive). These states
can the be used to flag the windows before they iconify so that they can
be restored.

In many ways this function works backwards -- the very first thing it
does is to look at all windows on the current page, that are iconic, and
have a state of one. If that matches, even for one window then the
RestoreDesktop function gets called. The reason why the check is
done at the beginning of this function rather than at the end (as logic
might well dictate) is because the window state flags get cleared --
invalidating the windows in the first place.

The next line then checks the success of the first line. If:

+ I All (CurrentPage, Iconic, State 1) RestoreDesktop

... returns true (i.e. it matched some windows) then the rest of the
function isn't executed. The remaining lines in that function flag all
windows to use a state number of one (chosen arbitrarily for this
example), and then iconify all windows with that state.

Moving on to the RestoreDesktop, that does the reverse -- in
that it deiconifies all windows with the state 1 bit on them, and then
removes it:

DestroyFunc RestoreDesktop
AddToFunc RestoreDesktop
+ I All (CurrentPage, Iconic, State 1) Iconify off
+ I All (CurrentPage, State 1) ThisWindow State 1 False

I mentioned you might want to bind this operation to FvwmButtons. As
an example, here's a part of a likely config: