FVWM Cookbook FAQ

July 2007: I no longer maintain this document. It now resides on the FvwmWiki found here: http://fvwmwiki.bu-web.de/FvwmCookbook but I keep this here as an historical note only. -- Thomas Adam.

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.


Contents

Window Placement/Positioning

WP1. Moving windows to the top-right of the screen.
WP2. Remembering the state of maximised windows.
WP3. Starting applications maximised.

Window Characteristics

WC1. Limiting instantiation of a window/application.
WC2. Switching focus between windows. (Alt-Tab)
WC3. Change of decor from focus/property change.
WC4. Different decors/colorsets for different pages/desks.
WC5. Shading of windows to move titlebar in the same direction.

Modules

M1. FvwmButtons: adaptive configuration.

Key Bindings

KB1. Adding Modifiers.

Miscellaneous

MC1. Iconifying windows except one.
MC2. "Show Desktop" Feature.


Window Placement/Positioning

WP1. Moving windows to the top-right of the screen.

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:

    DestroyFunc MoveWindowTopRight
    AddToFunc   MoveWindowTopRight
    + I ThisWindow AnimatedMove -0 +0
    

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.)

    DestroyModuleConfig FE-Maximize: *
    *FE-Maximize: Cmd Function
    *FE-Maximize: add_window     FuncCheckWindowAW
    *FE-Maximize: destroy_window FuncCheckWindowDW

    Module FvwmEvent FE-Maximize
    

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:

    DestroyFunc FuncCheckWindowAW
    AddToFunc   FuncCheckWindowAW
    + I ThisWindow PipeRead `while read; do \
      [ "$REPLY" = "$[w.class]" ] && echo 'ThisWindow (!Maximized) Maximize \
      || echo 'Nop'; done < $[FVWM_USERDIR]/windowlist`
    

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.

    DestroyFunc FuncCheckWindowDW
    AddToFunc   FuncCheckWindowDW
    + I ThisWindow (Maximized) Exec /bin/sh -c 'if ! grep -q $[w.class] \ 
      $[FVWM_USERDIR]/windowlist; then
      echo $[w.class] >> $[FVWM_USERDIR]/windowlist;
      fi'
    + I ThisWindow (!Maximized) Exec /bin/sh -c 'if grep -q $[w.class] \
      $[FVWM_USERDIR]/windowlist; then \
      sed -ie "/$[w.class]/d;" $[FVWM_USERDIR]/windowlist; fi'
    

Breaking this down a bit:

    + I ThisWindow (Maximized) Exec /bin/sh -c 'if ! grep -q $[w.class] \ 
      $[FVWM_USERDIR]/windowlist; then
      echo $[w.class] >> $[FVWM_USERDIR]/windowlist;
      fi'
      

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.

Similarly, but in the reverse; the following does the opposite:

    + I ThisWindow (!Maximized) Exec /bin/sh -c 'if grep -q $[w.class] \
      $[FVWM_USERDIR]/windowlist; then \
      sed -ie "/$[w.class]/d;" $[FVWM_USERDIR]/windowlist; fi'
    

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:

    + I ThisWindow (!Maximized) Exec /bin/sh -c 'if grep -q $[w.class] \
      $[FVWM_USERDIR]/windowlist; then sed \
      -e "/$[w.class]/d;" < $[FVWM_USERDIR]/windowlist > \
      $[FVWM_USERDIR]/.temp && mv $[FVWM_USERDIR]/.temp $[FVWM_USERDIR]/windowlist; fi'
    

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.

    DestroyModuleConfig FE-StartMaximised: *
    *FE-StartMaximised: Cmd Function
    *FE-StartMaximised: add_window FuncStartMaximised

    Module FvwmEvent FE-StartMaximised
    

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:

    DestroyFunc FuncStartMaximised
    AddToFunc	FuncStartMaximised
    + I Maximize
    

... which will maximise any new windows that appear on screen. Of course, as with WP1, you can qualify which windows start maximised:

+ I ThisWindow ("name_of_window") Maximize

There is an expanded reasoning behind this in the #fvwm FAQ


Window Characteristics

WC1. Limiting instantiation of a window/application.

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.

    DestroyFunc LimitApplication
    AddToFunc   LimitApplication
    + I Any ($0, CurrentDesk) FlipFocus
    + I TestRc (NoMatch) None ($0, CurrentDesk) Exec exec $0
    

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:

    Key s A CM LimitApplication screen1
    Key x A CM LimitApplication xine
    Key d A CM LimitApplication irssi
    

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.

    SetEnv DIR Next                                                             

    AddToFunc FocusRaiseAndStuff
    + I Iconify off
    + I Focus
    + I Raise

    AddToFunc SwitchWindow
    + I $[DIR] (CurrentPage, !Iconic, !Sticky) FocusRaiseAndStuff
    + I Deschedule 134000
    + I PipeRead `[ "$[DIR]" == "Prev" ] && \
	echo 'SetEnv NDIR Next' || \
	echo 'SetEnv NDIR Prev'`
    + I Schedule 700 134000 SetEnv DIR $[NDIR]

    Key Tab A M  SwitchWindow
    

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:

    AddToDecor StickyDecor
    + BorderStyle Simple
    + TitleStyle -- Raised
    + ButtonStyle Reset
    + ButtonStyle All -- Raised
    + ButtonStyle 1 Active    Pixmap mini.fvwm.xpm
    + ButtonStyle 1 Inactive  Vector 5 25x40@1 25x60@1 75x60@0 75x40@0 25x40@1
    + ButtonStyle 3 Active   Pixmap mini-icons.xpm
    + ButtonStyle 3 InActive Pixmap mini-icons.xpm
    + ButtonStyle 6 Active    Pixmap mini-rball.xpm
    + ButtonStyle 6 Inactive  Vector 5 40x40@1 60x40@1 60x60@0 40x60@0 40x40@1
    + ButtonStyle 4 Active    Pixmap mini-iconify.xpm
    + ButtonStyle 4 InActive  Vector 5 25x25@1 25x75@1 75x75@0 75x25@0 25x25@1
    + ButtonStyle 2 Active    Pixmap mini-x.xpm
    + ButtonStyle 2 InActive  Vector 17 20x20@1 30x20@1 50x40@1 70x20@1 80x20@1     
      80x30@0 60x50@0 80x70@1 80x80@0 70x80@0 50x60@0 30x80@0 20x80@0 20x70@0 
      40x50@1 20x30@0 20x20@1
    

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:

    DestroyModuleConfig FvwmEvent-Sticky: *
    *FvwmEvent-Sticky: Delay 1
    *FvwmEvent-Sticky: configure_window FvwmToggleStickyPixmap
    *FvwmEvent-Sticky: focus_change     FvwmToggleStickyPixmap
    *FvwmEvent-Sticky: add_window       FvwmToggleStickyPixmap

    # This line ensures that it starts up when FVWM does.
    AddToFunc StartFunction I Module FvwmEvent FvwmEvent-Sticky
    

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.

    DestroyFunc FvwmToggleStickyPixmap
    AddToFunc   FvwmToggleStickyPixmap
    + I ThisWindow (sticky)  ChangeDecor StickyDecor
    + I ThisWindow (!sticky) ChangeDecor NormalDecor
    

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:

    DestroyModuleConfig FvwmEvent-newPage: *
    *FvwmEvent-newPage: new_page FvwmFuncNewStyle

    Module FvwmEvent FvwmEvent-newPage

    

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.

    DestroyFunc FvwmFuncNewStyle
    AddToFunc   FvwmFuncNewStyle
    + I PipeRead '[[ $[desk.n] -eq 0 && $[page.nx] -eq 0 && $[page.ny] -eq 0 ]] && echo \
    "Function ChangeDecorWithMenu || echo "Function RestoreDecor"'
  

The PipeRead in the above, just qualifies which desk we're on, when we've switched to it. It tests the desk number ($[desk.n]), and the two pages ($[pages.nx], $[pages.ny]).

If we land on Desk 0 page 0 0, then the function ChangeDecorWithMenu is run.

    DestroyFunc ChangeDecorWithMenu
    AddToFunc  ChangeDecorWithMenu
    + I All (!Iconic,  AcceptsFocus, CurrentPage) WindowStyle Colorset 0
    + I All (!Iconic, AcceptsFocus, CurrentPage)  WindowStyle HilightColorset 3
    

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:

    DestroyFunc RestoreDecor
    AddToFunc   RestoreDecor
    + I All (!Iconic, AcceptsFocus, CurrentPage) WindowStyle HilightColorSet 1
    

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:

    DestroyFunc RaiseOrShade
    AddToFunc   RaiseOrShade
    + C     Raise
    + D     WindowShade $[func.context]

    Mouse 1         SF      A RaiseOrShade
    

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:

    DestroyFunc FuncShadeMe
    AddToFunc FuncShadeMe
    + I PipeRead `case $[func.context] in "t") \
	echo 'ThisWindow (Shaded) WindowStyle TitleAtTop'; \
	echo WindowShade toggle;; \
	"[") echo 'ThisWindow (!Shaded) WindowStyle TitleAtLeft' && echo WindowShade [;; \
	"]") echo 'ThisWindow (!Shaded) WindowStyle TitleAtRight' && echo WindowShade ];; \
	"-") echo 'ThisWindow (!Shaded) WindowStyle TitleAtTop' && echo WindowShade -;; \
	"_") echo 'ThisWindow (!Shaded) WindowStyle TitleAtBottom' && echo WindowShade _;;esac`
    

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:

    DestroyFunc RaiseOrShade
    AddToFunc   RaiseOrShade
    + C     Raise
    + D     FuncShadeMe

    Mouse 1         SF      A RaiseOrShade
    

Modules

M1. FvwmButtons: adaptive configuration.

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:

    *OSXDock: (24x26+1100+0, Padding 1 0, Icon v4.png, ActionOnPress, \
    Action(Mouse 1) Menu MenuVol Rectangle +$left+25 0 0m)
    

... which works fine. In order for us to 'flag' it, we just need to add the "Id" command to it:

    *OSXDock: (24x26+1100+0, Id "A", Padding 1 0, Icon v4.png, ActionOnPress, \
    Action(Mouse 1) Menu MenuVol Rectangle +$left+25 0 0m)
    

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:

    DestroyMenu MenuVol
    AddToMenu MenuVol
    + "100%%" Exec exec aumix -w 100
    + "90%%" Exec exec aumix -w 90
    + "80%%" Exec exec aumix -w 80
    + "70%%" Exec exec aumix -w 70
    + "60%%" Exec exec aumix -w 60
    + "50%%" Exec exec aumix -w 50
    + "40%%" Exec exec aumix -w 40
    + "30%%" Exec exec aumix -w 30
    + "20%%" Exec exec aumix -w 20
    + "10%%" Exec exec aumix -w 10
    + "0%%" Exec exec aumix -w 0
    

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:

    DestroyMenu MenuVol
    AddToMenu MenuVol
    + "100%%" ChangeSoundVolAndIcon 100
    + "90%%" ChangeSoundVolAndIcon 90
    + "80%%" ChangeSoundVolAndIcon 80
    + "70%%" ChangeSoundVolAndIcon 70
    + "60%%" ChangeSoundVolAndIcon 60
    + "50%%" ChangeSoundVolAndIcon 50
    + "40%%" ChangeSoundVolAndIcon 40
    + "30%%" ChangeSoundVolAndIcon 30
    + "20%%" ChangeSoundVolAndIcon 20
    + "10%%" ChangeSoundVolAndIcon 10
    + "0%%"  SoundChangeIcon 
    

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.

    DestroyFunc ChangeSoundVolAndIcon
    AddToFunc ChangeSoundVolAndIcon
    + I Exec exec aumix -w $0
    

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:

    DestroyFunc SoundChangeIcon
    AddToFunc SoundChangeIcon
    + I Exec exec aumix -w 0
    + I SendToModule OSXDock ChangeButton "A" Icon mute.png
    



Key Bindings

KB1. Adding Modifiers.

To be written.




Miscellaneous

MC1. Iconifying windows except one.

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 Q. MC1, 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.

    DestroyFunc ShowDesktop
    AddToFunc   ShowDesktop
    + I All (CurrentPage, Iconic, State 1) RestoreDesktop
    + I TestRc (Match) Break
    + I All (CurrentPage, !Iconic, !State 1) ThisWindow State 1 True
    + I All (CurrentPage, !Iconic, State 1) Iconify
    

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:

    *MyPanel: (1x2, Icon showdesk.png, Action (Mouse 1) Function ShowDesktop)
    

You might also bind that to a key binding, or somesuch.



-- Thomas Adam <thomas@edulinux.homeunix.org>