Discussion:
FVWM: Trying to understand fvwm function execution
Chris Siebenmann
2012-02-07 20:41:22 UTC
Permalink
I feel like I'm missing some important bits about how fvwm handles
functions.

I'm trying to write a function that's fed a window name and has
following goals. First, it only operates on terminal windows.
Then:
- if no (terminal) window by that name exists, it should do nothing.
- if the currently selected/focused window has that name (and is a terminal
window), it should do nothing.
- otherwise, it should deiconify, focus, and raise the window, then
move the pointer to be inside the window.

My current version is:

AddToFunc ToWindow
+ I Current ("XTerm|9term|Gnome-terminal", "$0") Break
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Iconify False
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Raise
+ I Next ("XTerm|9term|Gnome-terminal", "$0") WarpToWindow 80 5

This works but seems unnecessarily repetitive (actually I'm suddenly
not convinced that it does nothing if the current window is a terminal
window called $0). So I thought I could rewrite it like this (with
comments about the logic):

AddToFunc ToWindow
# do nothing if current window is called $0
+ I Current ("XTerm|9term|Gnome-terminal", "$0") Break
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
# if no window is called $0, this stops; otherwise Focus has
# made that window the current window.
+ I TestRc (NoMatch) Break
# do the rest of the work
+ I Iconify False
+ I Raise
+ I WarpToWindow 80 5

However, this doesn't work if no such window actually exists; before
anything from the function seems to execute (even an 'Echo' statement
inserted as the first command in the function), the mouse cursor changes
to the 'select a window' cursor. Clearly I'm missing something both
about how fvwm executes functions and how it selects windows and handles
conditional commands.

Do I need to make all commands conditional in this function in order
to have things work right, by putting plain 'Current ' in front of the
last three lines? (A version with this change seems to do nothing if
there's no terminal window by that name, but I feel I'm just sort of
writing code by superstition instead of actual understanding at this
point.)

Thanks in advance.

- cks
Thomas Adam
2012-02-07 23:27:39 UTC
Permalink
Post by Chris Siebenmann
I feel like I'm missing some important bits about how fvwm handles
functions.
I'm trying to write a function that's fed a window name and has
following goals. First, it only operates on terminal windows.
- if no (terminal) window by that name exists, it should do nothing.
- if the currently selected/focused window has that name (and is a terminal
window), it should do nothing.
- otherwise, it should deiconify, focus, and raise the window, then
move the pointer to be inside the window.
AddToFunc ToWindow
+ I Current ("XTerm|9term|Gnome-terminal", "$0") Break
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Iconify False
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Raise
+ I Next ("XTerm|9term|Gnome-terminal", "$0") WarpToWindow 80 5
This is why it's better to do this:

Style XTerm State 1
Style 9term State 1
Style Gnome-terminal State 1

DestroyFunc ToWindow
AddToFunc ToWindow
+ I Current (State 1, $0) Break
+ I Next (State 1, $0) SomeFunction

DestroyFunc SomeFunction
AddToFunc SomeFunction
+ I Iconify False
+ I Focus
+ I Raise
+ I WarpToWindow 80 5

So you can see here, by using State, that we've shifted the repetitiveness
to the style of the window, and not a pre-condition to all commands we might
need to reference a bunch of terminals.
Post by Chris Siebenmann
This works but seems unnecessarily repetitive (actually I'm suddenly
not convinced that it does nothing if the current window is a terminal
window called $0). So I thought I could rewrite it like this (with
AddToFunc ToWindow
# do nothing if current window is called $0
+ I Current ("XTerm|9term|Gnome-terminal", "$0") Break
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
# if no window is called $0, this stops; otherwise Focus has
# made that window the current window.
+ I TestRc (NoMatch) Break
# do the rest of the work
+ I Iconify False
+ I Raise
+ I WarpToWindow 80 5
However, this doesn't work if no such window actually exists; before
anything from the function seems to execute (even an 'Echo' statement
Context is about how functions run, and the way you have it there, FVWM will
need a window context to run in -- that is, a window to operate on. Your
use of "$0" implies this to some extent, in that you have a name or
something being passed in, but you don't say how.

If FVWM doesn't have an operand window, then it will ask for one. This will
depend on how you're trying to call this function. This is perhaps the most
important piece of information you've failed to provide.
Post by Chris Siebenmann
Do I need to make all commands conditional in this function in order
to have things work right, by putting plain 'Current ' in front of the
last three lines? (A version with this change seems to do nothing if
there's no terminal window by that name, but I feel I'm just sort of
writing code by superstition instead of actual understanding at this
point.)
No you don't. Consider this:

AddToFunc ToWindow
+ I Current ("XTerm|9term|Gnome-terminal", "$0") Break
+ I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
+ I TestRc (NoMatch) Break
+ I Iconify False
+ I Raise
+ I WarpToWindow 80 5


If I bind this to a key like this:

Key f A M ToWindow

Then press Alt-f, FVWM evaluates the context of the binding. It may well be
that the window we're operating on is implied, in which case FVWM won't ask
for a window -- and indeed, your implementation of ToWindow doesn't. FVWM
processes the commands in order, top-to-bottom. It comes across "Current"
and evaluates that in the context of the binding. Of course, there is
nothing special about Current, Next, or TestRc, they can run inside a
function just fine, and at this point assume no specific window -- but
because you have this:

+ I Iconify False

... if FVWM processes this from your ToWindow function and there is no
implied operand window for ToWindow to be running in, it will prompt for a
window to run against, because it needs a window.

Let me elaborate more on what I mean by operand window, and implied context.
Mouse bindings are good for this:

Mouse 0 1 A FuncWindowOpsOrClose

DestroyFunc FuncWindowOpsOrClose
AddToFunc FuncWindowOpsOrClose
+ H Nop
+ C Echo Clicked $[w.id]
+ D Close

Pressing any mouse button on button 1 on a window will call
FuncWindowOpsOrClose -- and note that here:

+ D Close

I do not need:

+ D ThisWindow Close

Nor do I need:

+ D Current Close

It's because the binding happens on a window already.

Once you have a window you're hooked on it though. So really, establishing
a binding to one window can happen outside the function before invoking it,
and that's precisely what I did with my original suggestion to you above:

DestroyFunc ToWindow
AddToFunc ToWindow
+ I Current (State 1, $0) Break
+ I Next (State 1, $0) SomeFunction

DestroyFunc SomeFunction
AddToFunc SomeFunction
+ I Iconify False
+ I Focus
+ I Raise
+ I WarpToWindow 80 5

So can you not see now, why in SomeFunction I do not need to prefix the
commands with Current, because they're all called in the matched window from
ToWindow, coming from the Next command?

There are other ways of achieving this though. 'Pick' is another useful
command which would have helped you as well, but I shied away from
mentioning it above to help illustrate what was going on. But:

Pick ToWindow

Would work just as well with how you've written things.

-- Thomas Adam
--
"Deep in my heart I wish I was wrong. But deep in my heart I know I am
not." -- Morrissey ("Girl Least Likely To" -- off of Viva Hate.)
Chris Siebenmann
2012-02-08 00:42:01 UTC
Permalink
| On Tue, Feb 07, 2012 at 03:41:22PM -0500, Chris Siebenmann wrote:
| > My current version is:
| > AddToFunc ToWindow
| > + I Current ("XTerm|9term|Gnome-terminal", "$0") Break
| > + I Next ("XTerm|9term|Gnome-terminal", "$0") Iconify False
| > + I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
| > + I Next ("XTerm|9term|Gnome-terminal", "$0") Raise
| > + I Next ("XTerm|9term|Gnome-terminal", "$0") WarpToWindow 80 5
|
| This is why it's better to do this:
|
| Style XTerm State 1
| Style 9term State 1
| Style Gnome-terminal State 1
|
| DestroyFunc ToWindow
| AddToFunc ToWindow
| + I Current (State 1, $0) Break
| + I Next (State 1, $0) SomeFunction

Oh that's clever. I hadn't even thought of using states that way,
and it makes everything much more modular (I don't have to hunt down
N places to add things to if I decide that rxvt is also a terminal).
Clearly I need to think more about ways to use States.

Thank you!

| > This works but seems unnecessarily repetitive (actually I'm suddenly
| > not convinced that it does nothing if the current window is a terminal
| > window called $0). So I thought I could rewrite it like this (with
| > comments about the logic):
| >
| > AddToFunc ToWindow
| > # do nothing if current window is called $0
| > + I Current ("XTerm|9term|Gnome-terminal", "$0") Break
| > + I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
| > # if no window is called $0, this stops; otherwise Focus has
| > # made that window the current window.
| > + I TestRc (NoMatch) Break
| > # do the rest of the work
| > + I Iconify False
| > + I Raise
| > + I WarpToWindow 80 5
| >
| > However, this doesn't work if no such window actually exists; before
| > anything from the function seems to execute (even an 'Echo' statement
|
| Context is about how functions run, and the way you have it there, FVWM will
| need a window context to run in -- that is, a window to operate on. Your
| use of "$0" implies this to some extent, in that you have a name or
| something being passed in, but you don't say how.
|
| If FVWM doesn't have an operand window, then it will ask for one. This will
| depend on how you're trying to call this function. This is perhaps the most
| important piece of information you've failed to provide.

Sorry, my lack of clarity. This is being invoked (more or less) as

echo "Function ToWindow \"$win\"" | FvwmCommand -c

(and for testing I have been just doing the 'Function ...' inside a
FvwmConsole.)

| > Do I need to make all commands conditional in this function in order
| > to have things work right, by putting plain 'Current ' in front of the
| > last three lines? (A version with this change seems to do nothing if
| > there's no terminal window by that name, but I feel I'm just sort of
| > writing code by superstition instead of actual understanding at this
| > point.)
|
| No you don't. Consider this:
|
| AddToFunc ToWindow
| + I Current ("XTerm|9term|Gnome-terminal", "$0") Break
| + I Next ("XTerm|9term|Gnome-terminal", "$0") Focus
| + I TestRc (NoMatch) Break
| + I Iconify False
| + I Raise
| + I WarpToWindow 80 5
[...]
| It may well be that the window we're operating on is implied,
| in which case FVWM won't ask for a window -- and indeed, your
| implementation of ToWindow doesn't. FVWM processes the commands in
| order, top-to-bottom. It comes across "Current" and evaluates that in
| the context of the binding. Of course, there is nothing special about
| Current, Next, or TestRc, they can run inside a function just fine,
| and at this point assume no specific window -- but because you have
| this:
|
| + I Iconify False
|
| ... if FVWM processes this from your ToWindow function and there is no
| implied operand window for ToWindow to be running in, it will prompt
| for a window to run against, because it needs a window.

I guess my lack of understanding this: when I invoked ToWindow with

Function ToWindow "nosuchwindow"

I expected the 'Next' to fail and then the TestRc to succeed and abort
the function before FVWM reached 'Iconify False' and needed a window
to operate on. Instead nothing seems to be run at all before FVWM asks
for a window -- I can put an unconditional 'Echo' in as the first + I
in ToWindow, invoke 'Function ToWindow "nosuchwindow"', and if I then
clicked on the root window, no Echo output showed up at all. (If I did
select a window, things seemed to work as expected.)

| Once you have a window you're hooked on it though. So really, establishing
| a binding to one window can happen outside the function before invoking it,
| and that's precisely what I did with my original suggestion to you above:
|
| DestroyFunc ToWindow
| AddToFunc ToWindow
| + I Current (State 1, $0) Break
| + I Next (State 1, $0) SomeFunction
|
| DestroyFunc SomeFunction
| AddToFunc SomeFunction
| + I Iconify False
| + I Focus
| + I Raise
| + I WarpToWindow 80 5
|
| So can you not see now, why in SomeFunction I do not need to prefix the
| commands with Current, because they're all called in the matched window from
| ToWindow, coming from the Next command?

Yes, I think I understand. Calling 'Next ... SomeFunction' (or I assume
any successful conditional window selection command) sets the window
binding in effect for SomeFunction, and if Next cannot find such a
window it will not run SomeFunction at all. Do I have that right?

- cks
Thomas Adam
2012-02-08 00:49:27 UTC
Permalink
Post by Chris Siebenmann
Yes, I think I understand. Calling 'Next ... SomeFunction' (or I assume
any successful conditional window selection command) sets the window
binding in effect for SomeFunction, and if Next cannot find such a
window it will not run SomeFunction at all. Do I have that right?
Yes.

-- Thomas Adam
--
"Deep in my heart I wish I was wrong. But deep in my heart I know I am
not." -- Morrissey ("Girl Least Likely To" -- off of Viva Hate.)
Loading...