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.
WP1. Moving windows to the top-right of the screen.
WP2. Remembering the state of maximised windows.
WP3. Starting applications maximised.
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.
M1. FvwmButtons: adaptive configuration.
MC1. Iconifying windows except one.
MC2. "Show Desktop" Feature.
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.
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.
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
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.
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.
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.
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... :)
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
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
To be written.
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
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.
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.