Subject: PATCH: define key-bindings on individual windows

From: Scott Smedley <[email-masked]>
Date: Fri, 13 Feb 2004 15:06:06 +1100

Hi all,

Find attached a patch to FVWM to define key-bindings on individual
windows. (Previously, a key binding had to be defined for ALL windows
or NONE.)

Example .fvwm2rc syntax:

=== BEGIN ===

AddToFunc CtrlTab
+ I Current (FvwmTabs) Function NextTab
+ I Current (!FvwmTabs) SendKeyEvent current

Key Tab A C Function CtrlTab

=== END ===

Here, Ctrl-Tab in an FvwmTabs window will perform a 'NextTab' operation
but in all other windows the Ctrl-Tab keypress is passed through untouched.
(ie. is treated as if there were no FVWM key binding for Ctrl-Tab.)

The patch involves the addition of a new keyword - SendKeyEvent
which basically allows the intercepted key event to be sent through
to a specified window. I hope to generalise SendKeyEvent to be able to
send ANY key event in the near future. (Maybe call it FakeKeypress?)

If this patch is applied I am happy to update the doco.

Comments/criticism most welcome.

SCoTTie! :)
Index: fvwm/builtins.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/builtins.c,v
retrieving revision 1.404
diff -u -r1.404 builtins.c
--- fvwm/builtins.c	24 Jan 2004 23:20:21 -0000	1.404
+++ fvwm/builtins.c	13 Feb 2004 04:00:23 -0000
@@ -4252,3 +4252,46 @@
 
 	return;
 }
+
+void fev_copy_last_event(XEvent *dest); /* libs/FEvent.c */
+
+void CMD_SendKeyEvent(F_CMD_ARGS)
+{
+	char *token;
+	FvwmWindow *fw = exc->w.fw;
+	XEvent e;
+
+	if (fw == NULL)
+	{
+		fvwm_msg(ERR, "CMD_SendKeyEvent", "No window specified\n");
+		return;
+	}
+
+	fev_copy_last_event(&e);
+
+	action = GetNextToken(action, &token);
+	if (!token || StrEquals(token, "current"))
+	{
+		if (e.type != KeyPress)
+		{
+			fvwm_msg(ERR, "CMD_SendKeyEvent", "Last event wasn't keypress\n");
+			return;
+		}
+	}
+	else
+	{
+		KeySym keysym = XStringToKeysym(token);
+		// fvwm_msg(DBG, "CMD_SendKeyEvent", "keysym value: %d\n", keysym);
+		if (keysym == NoSymbol)
+		{
+			fvwm_msg(ERR, "CMD_SendKeyEvent", "Unknown keysym: %s\n", token);
+			return;
+		}
+		e.xkey.type = KeyPress;
+		e.xkey.state = 0;	// TODO: Ctrl/Shift/Alt/Meta etc.
+		e.xkey.keycode = XKeysymToKeycode(dpy, keysym);
+	}
+	e.xkey.window = FW_W(fw);
+	FSendEvent(dpy, e.xkey.window, False, KeyPressMask, &e);
+	return;
+}
Index: fvwm/commands.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/commands.h,v
retrieving revision 1.41
diff -u -r1.41 commands.h
--- fvwm/commands.h	16 Jul 2003 09:10:40 -0000	1.41
+++ fvwm/commands.h	13 Feb 2004 04:00:23 -0000
@@ -185,6 +185,7 @@
 	F_WINDOWID,
 	F_WINDOW_SHADE,
 	F_WINDOW_STYLE,
+	F_SEND_KEY_EVENT,
 
 	F_END_OF_LIST = 999,
 
@@ -343,6 +344,7 @@
 P(Scroll);
 P(Send_ConfigInfo);
 P(Send_WindowList);
+P(SendKeyEvent);
 P(SendToModule);
 P(set_mask);
 P(set_nograb_mask);
Index: fvwm/functable.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/functable.c,v
retrieving revision 1.29
diff -u -r1.29 functable.c
--- fvwm/functable.c	16 Jul 2003 09:10:40 -0000	1.29
+++ fvwm/functable.c	13 Feb 2004 04:00:24 -0000
@@ -516,6 +516,10 @@
 		FUNC_DONT_REPEAT, 0),
 	/* - Internal, used for module communication */
 
+	CMD_ENT("sendkeyevent", CMD_SendKeyEvent, F_SEND_KEY_EVENT,
+		FUNC_NEEDS_WINDOW, CRS_SELECT),
+    /* - Send a keyboard event to a window */
+
 	CMD_ENT("sendtomodule", CMD_SendToModule, F_SEND_STRING,
 		FUNC_DONT_REPEAT, 0),
 	/* - Send a string (action) to a module */

---

From: Dominik Vogt <[email-masked]>
Date: Tue, 17 Feb 2004 10:14:24 +0100

> Comments/criticism most welcome.

I'm not quite happy with this patch for several reasons.

1) Generating keyboard events out of sequence can totally confuse
   applications or fvwm itself.  In your example, you hold down
   Ctrl and tap tha Tab key.  The modifier mask has the bit for
   the Ctrl key set.  Now the event is passed on, but when it is
   processed by fvwm or the receiving application, the real
   modifier mask may have already changed.  This can lead to
   erratic behaviour.  For example, an application may wait for a
   KeyRelease event that will never come.

2) It suffers from the same problems as the FakeClick command.
   Apart from #1 above, it's impossible to determine to which
   window the event has to be sent.  There is no guarantee that
   the top level client window is able to handle key events.

I'm not saying the patch is useless.  It would be nice to further
improve it to a clone of FakeClick that can replay key and
modifier press and release events and send them to arbitrary
windows (not just client windows).  But a future FakeKey command
has to be marked as a debug command to "use at own risk" like
FakeClick.

I'll accept a patch that duplicates the FakeClick syntax (or
better: unifies both commands to use the same core loop).

The application-specific-bindings problem, however, can only be
solved in a different way.  The binding syntax and code can be
enhanced to allow window selection similar to the Style command.

(Side note to Mikhael and Olivier:  see why I want a unified
syntax for Style + WindowStyle?  If we had a single style target
spec syntax, it could easily be applied to the binding commands
too.  Instead, we now need new commands WindowKey, WindowMouse and
WindowPointerKey).

> +void fev_copy_last_event(XEvent *dest); /* libs/FEvent.c */

You don't need this function and it does not copy the event you
think it does.  See below.

> +	fev_copy_last_event(&e);

Just make a copy of exc->x.etrigger:

  e = *(exc->x.etrigger);

You are not really interested in the last event received by fvwm
but in the event that triggered the function.  I know, the whole
FEvent.[ch] and execcontext.[ch] code should be documented ...

> +		KeySym keysym = XStringToKeysym(token);

Use FvwmStringToKeysym() from libs/Bindings.h instead.  It does
some extra things to normalize the returned KeySym.

> +	FSendEvent(dpy, e.xkey.window, False, KeyPressMask, &e);

Ciao

Dominik ^_^  ^_^
--
Visit the official FVWM web page at <URL:http://www.fvwm.org/>.
To unsubscribe from the list, send "unsubscribe fvwm-workers" in the
body of a message to [email-masked].
To report problems, send mail to [email-masked]

---

From: Scott Smedley <[email-masked]>
Date: Wed, 18 Feb 2004 00:24:02 +1100

Hi Dominik,

Thanks for the feedback, muchly appreciated.

>    In your example, you hold down
>    the Ctrl key set.

Yep.

>    Now the event is passed on, but when it is
>    modifier mask may have already changed.

I'm not sure what you mean. If the modifier mask changes that implies
another event is generated. Are you just saying that the associated
KeyRelease event never gets sent to the application?

>    This can lead to

Yes, I get the feeling that modifying FVWM to allow KeyRelease events to
get through would be a challenging hack at best. Is this a fair synopsis?

I guess it would involve remembering which KeyPress events FVWM lets
through & adding some sort of handler to KeyRelease events to check
if there's an associated KeyPress event that got sent through. If
there is, send the KeyRelease event through & "forget" the KeyPress
event.

>    it's impossible to determine to which

Agreed.

> better: unifies both commands to use the same core loop).

I can do that.

> enhanced to allow window selection similar to the Style command.

Can you please elaborate?

> > +		e.xkey.state = 0;	// TODO: Ctrl/Shift/Alt/Meta etc.

On a slight tangent, do you know how I can get the modifier state
from the string name of a keysym?

SCoTTie! :)

---

From: Dominik Vogt <[email-masked]>
Date: Tue, 17 Feb 2004 15:17:16 +0100

> KeyRelease event never gets sent to the application?

Yes, and in addition, when the event is received, the modifier
mask may differ from the actual state of the modifiers.  Some code
in fvwm relies on an accurate modifier mask to work correctly, and
other programs maybe too.

> there is, send the KeyRelease event through & "forget" the KeyPress

I already tried that and failed since if a module or application
grabs the keyboard, fvwm never gets the Key... events.

> Can you please elaborate?

Something like

  Key (rxvt) TSIF N do_something

The binding code would make sure that only the proper keys and
buttonms are grabbed for such windows.  Currently, all keys and
buttons with bindings attached are grabbed on all windows.

> from the string name of a keysym?

:-P

Look at the AddBinding() function in libs/Bindings.c.  Basically,
given a specific keycode, you just try all possible modifier
entries (max. 9) in the Xmodmap and see if they yield the desired
keysym.  The problem is that only the two entries in the modifier
map have a fixed meaning (0 = no modifier, 1 = shift).  All others
are undefined and can not be queried from the X server, as far as
I know.

You can not do much more than allowing the user to specify the
modifiers and keycodes in addition to keysyms.  The switch that
gueasses the modifier mask in AddWindow() could be extracted and
put into a new function for general use.

Ciao

Dominik ^_^  ^_^

---

From: Scott Smedley <[email-masked]>
Date: Wed, 18 Feb 2004 02:19:59 +1100

Hi Dominik,

> mask may differ from the actual state of the modifiers.

Why would the modifier mask be wrong? I understand the modifier
mask to be set to whatever Shift/Ctrl/Alt/etc keys are pressed when
the (say) Tab key is pressed.

good night!

SCoTTie! :)

---

From: Dominik Vogt <[email-masked]>
Date: Tue, 17 Feb 2004 16:35:36 +0100

> the (say) Tab key is pressed.

Because there is no guarantee these modifiers are still pressed
when the new event is processed.  Normally, it is guaranteed that
if an application freezes key event processing and then queries
the current keyboard state, it gets the same modifier mask as in
the event that initiated the freeze.  That's not the case here.

Ciao

Dominik ^_^  ^_^