Subject: Re: PATCH: window-specific key/mouse bindings

From: scott <[email-masked]>
Date: Wed, 3 Mar 2004 03:50:12 +1100

Find attached updated patch. Includes small performance improvement,
code tidyup & bugfix to ensure bindings work in windows with dynamically
changing class/resource/name values.

SCoTT. :)
-- 
[email-masked]
Index: fvwm/add_window.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/add_window.c,v
retrieving revision 1.360
diff -u -r1.360 add_window.c
--- fvwm/add_window.c	19 Feb 2004 09:36:01 -0000	1.360
+++ fvwm/add_window.c	2 Mar 2004 16:48:06 -0000
@@ -1441,13 +1441,11 @@
 	 * list. */
 	GrabAllWindowKeys(
 		dpy, FW_W_FRAME(fw), Scr.AllBindings,
-		C_WINDOW|C_TITLE|C_RALL|C_LALL|C_SIDEBAR, GetUnusedModifiers(),
-		True);
+		C_WINDOW|C_TITLE|C_RALL|C_LALL|C_SIDEBAR, GetUnusedModifiers(), True);
 #endif
 	GrabAllWindowKeys(
 		dpy, FW_W_FRAME(fw), Scr.AllBindings,
-		C_TITLE|C_RALL|C_LALL|C_SIDEBAR|C_WINDOW, GetUnusedModifiers(),
-		True);
+		C_TITLE|C_RALL|C_LALL|C_SIDEBAR|C_WINDOW, GetUnusedModifiers(), True);
 	setup_focus_policy(fw);
 
 	return;
Index: fvwm/bindings.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/bindings.c,v
retrieving revision 1.87
diff -u -r1.87 bindings.c
--- fvwm/bindings.c	2 Nov 2003 17:04:44 -0000	1.87
+++ fvwm/bindings.c	2 Mar 2004 16:48:07 -0000
@@ -101,6 +101,11 @@
 	return;
 }
 
+Bool bindingAppliesToFWindow (Binding *b, FvwmWindow *fw)
+{
+	return bindingAppliesToWindow(b, &fw->class, fw->name.name);
+}
+
 static int activate_binding(Binding *binding, binding_t type, Bool do_grab)
 {
 	FvwmWindow *t;
@@ -136,6 +141,13 @@
 	/* grab keys immediately */
 	for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
 	{
+#if 0
+		if (!bindingAppliesToFWindow(binding, t))
+		{
+			continue;
+		}
+#endif
+
 		if (!IS_EWMH_DESKTOP(FW_W(t)) &&
 		    (binding->Context & (C_WINDOW | C_DECOR)) &&
 		    BIND_IS_KEY_BINDING(type))
@@ -237,8 +249,8 @@
 	int *nr_left_buttons, int *nr_right_buttons,
 	unsigned short *buttons_grabbed, Bool is_silent)
 {
-	char *action, context_string[20], modifier_string[20], *ptr, *token;
-	char key_string[201] = "";
+	char *action, context_string[80], modifier_string[20], *ptr, *token;
+	char key_string[201] = "", buffer[80], *windowName = NULL;
 	int button = 0;
 	int n1=0,n2=0,n3=0;
 	KeySym keysym = NoSymbol;
@@ -361,7 +373,34 @@
 	token = PeekToken(ptr, &ptr);
 	if (token != NULL)
 	{
-		n2 = sscanf(token, "%19s", context_string);
+		char *p = token;
+		if (*p == '(')
+		{
+			/* A window name has been specified for the binding. */
+			strcpy(buffer, p+1);
+			p = buffer;
+			while (*p != ')')
+			{
+				if (*p == '\0')
+				{
+					if (!is_silent)
+						fvwm_msg(ERR, "ParseBinding",
+							"Syntax error in line %s - missing ')'", tline);
+					return 0;
+				}
+				++p;
+			}
+			*p++ = '\0';
+			windowName = buffer;
+			if (*p == '\0')
+			{
+				if (!is_silent)
+					fvwm_msg(ERR, "ParseBinding",
+						"Syntax error in line %s - no context specified with window", tline);
+				return 0;
+			}
+		}
+		n2 = sscanf(p, "%79s", context_string);
 	}
 	token = PeekToken(ptr, &action);
 	if (token != NULL)
@@ -437,7 +476,7 @@
 	/* BEGIN remove */
 	CollectBindingList(
 		dpy, pblist, &rmlist, type, STROKE_ARG((void *)stroke)
-		button, keysym, modifier, context);
+		button, keysym, modifier, context, windowName);
 	if (rmlist != NULL)
 	{
 		is_binding_removed = True;
@@ -502,7 +541,7 @@
 	rc = AddBinding(
 		dpy, pblist, type, STROKE_ARG((void *)stroke)
 		button, keysym, key_string, modifier, context, (void *)action,
-		NULL);
+		NULL, windowName);
 
 	return rc;
 }
Index: fvwm/bindings.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/bindings.h,v
retrieving revision 1.16
diff -u -r1.16 bindings.h
--- fvwm/bindings.h	29 Jun 2003 19:53:22 -0000	1.16
+++ fvwm/bindings.h	2 Mar 2004 16:48:07 -0000
@@ -4,6 +4,8 @@
 #define BINDINGS_H
 
 /* ---------------------------- included header files ---------------------- */
+#include "fvwm.h"			/* FvwmWindow */
+#include "libs/Bindings.h"	/* Binding */
 
 /* ---------------------------- global definitions ------------------------- */
 
@@ -20,6 +22,16 @@
 void update_key_bindings(void);
 unsigned int MaskUsedModifiers(unsigned int in_modifiers);
 unsigned int GetUnusedModifiers(void);
+Bool bindingAppliesToFWindow (Binding *b, FvwmWindow *fw);
+void grabAllFWindowKeys(
+    Display *dpy, Window w, Binding *blist, unsigned int contexts,
+    unsigned int dead_modifiers, Bool fGrab, FvwmWindow *fw);
+void grabAllFWindowKeysAndButtons(
+    Display *dpy, Window w, Binding *blist, unsigned int contexts,
+    unsigned int dead_modifiers, Cursor cursor, Bool fGrab,
+    FvwmWindow *fw);
+
+
 
 #endif /* BINDINGS_H */
 
Index: fvwm/builtins.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/builtins.c,v
retrieving revision 1.405
diff -u -r1.405 builtins.c
--- fvwm/builtins.c	19 Feb 2004 09:36:01 -0000	1.405
+++ fvwm/builtins.c	2 Mar 2004 16:48:14 -0000
@@ -2553,12 +2553,13 @@
 			}
 			else if (e.type == KeyPress)
 			{
+				/* TODO: should I be using <t> or <exc->w.fw>? */
+				int context = GetContext(&t, t, &e, &nonewin);
 				escape = CheckBinding(
 					Scr.AllBindings, STROKE_ARG(0)
 					e.xkey.keycode, e.xkey.state,
-					GetUnusedModifiers(),
-					GetContext(&t, t, &e, &nonewin),
-					BIND_KEYPRESS);
+					GetUnusedModifiers(), context,
+					BIND_KEYPRESS, &t->class, t->name.name);
 				if (escape != NULL)
 				{
 					if (!strcasecmp(escape,"escapefunc"))
@@ -4209,7 +4210,8 @@
 	/* check for a binding */
 	stroke_action = CheckBinding(
 		Scr.AllBindings, sequence, 0, modifiers, GetUnusedModifiers(),
-		exc->w.wcontext, BIND_STROKE);
+		exc->w.wcontext, BIND_STROKE, &exc->w.fw->class,
+		exc->w.fw->name.name);
 
 	/* execute the action */
 	if (stroke_action != NULL)
Index: fvwm/events.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/events.c,v
retrieving revision 1.490
diff -u -r1.490 events.c
--- fvwm/events.c	26 Feb 2004 14:40:34 -0000	1.490
+++ fvwm/events.c	2 Mar 2004 16:48:24 -0000
@@ -1461,14 +1461,18 @@
 static void __handle_bpress_on_root(const exec_context_t *exc)
 {
 	char *action;
+	XClassHint tmp;
 
 	PressedW = None;
 	__handle_bpress_stroke();
 	/* search for an appropriate mouse binding */
+	/* exc->w.fw is always NULL, hence why we use "root". */
+	tmp.res_class = tmp.res_name = "root";
 	action = CheckBinding(
 		Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
 		exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
-		BIND_BUTTONPRESS);
+		BIND_BUTTONPRESS, &tmp, tmp.res_class);
+
 	if (action && *action)
 	{
 		const exec_context_t *exc2;
@@ -1541,7 +1545,7 @@
 		action = CheckBinding(
 			Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
 			e->xbutton.state, GetUnusedModifiers(),
-			exc->w.wcontext, BIND_BUTTONPRESS);
+			exc->w.wcontext, BIND_BUTTONPRESS, &fw->class, fw->name.name);
 		if (__handle_bpress_action(exc, action))
 		{
 			f.do_swallow_click = 1;
@@ -1616,7 +1620,9 @@
 	/* need to search for an appropriate stroke binding */
 	action = CheckBinding(
 		Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
-		GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE);
+		GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
+		&ea->exc->w.fw->class, ea->exc->w.fw->name.name);
+
 	/* got a match, now process it */
 	if (action != NULL && (action[0] != 0))
 	{
@@ -2280,45 +2286,60 @@
 	const XEvent *te = ea->exc->x.etrigger;
 	const FvwmWindow * const fw = ea->exc->w.fw;
 	Bool is_second_binding;
+	const XClassHint *winClass1, *winClass2;
+	XClassHint tmp;
+	char *name1, *name2;
+	const exec_context_t *exc;
+	exec_context_changes_t ecc;
 
 	PressedW = None;
 
 	/* Here's a real hack - some systems have two keys with the
 	 * same keysym and different keycodes. This converts all
 	 * the cases to one keycode. */
-	kc = XKeysymToKeycode(
-		dpy, XKeycodeToKeysym(dpy, te->xkey.keycode, 0));
+	kc = XKeysymToKeycode(dpy, XKeycodeToKeysym(dpy, te->xkey.keycode, 0));
 
 	/* Check if there is something bound to the key */
 	sf = get_focus_window();
 	if (sf == NULL)
 	{
+		tmp.res_name = tmp.res_class = name1 = "root";
+		winClass1 = &tmp;
 		kcontext = C_ROOT;
 	}
-	else if (sf == ea->exc->w.fw)
+	else
 	{
-		kcontext = ea->exc->w.wcontext;
+		winClass1 = &sf->class;
+		name1 = sf->name.name;
+		kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
+	}
+
+	if (fw == NULL)
+	{
+		tmp.res_name = tmp.res_class = name2 = "root";
+		winClass2 = &tmp;
 	}
 	else
 	{
-		kcontext = C_WINDOW;
+		winClass2 = &fw->class;
+		name2 = fw->name.name;
 	}
-	action = CheckTwoBindings(
-		&is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
-		te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
-		ea->exc->w.wcontext, BIND_PKEYPRESS);
+	/* Searching the binding list with a different 'type' value
+	 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
+	 * The different context value does though. */
+	action = CheckTwoBindings(&is_second_binding, Scr.AllBindings,
+		STROKE_ARG(0) kc, te->xkey.state, GetUnusedModifiers(), kcontext,
+		BIND_KEYPRESS, winClass1, name1, ea->exc->w.wcontext,
+		BIND_PKEYPRESS, winClass2, name2);
+
 	if (action != NULL)
 	{
-		const exec_context_t *exc;
-		exec_context_changes_t ecc;
-
 		exc = ea->exc;
 		if (is_second_binding == False)
 		{
 			ecc.w.fw = sf;
 			ecc.w.wcontext = kcontext;
-			exc = exc_clone_context(
-				ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
+			exc = exc_clone_context(ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
 		}
 		execute_function(NULL, exc, action, 0);
 		if (is_second_binding == False)
Index: fvwm/fvwm.1.in
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/fvwm.1.in,v
retrieving revision 1.123
diff -u -r1.123 fvwm.1.in
--- fvwm/fvwm.1.in	19 Feb 2004 09:36:01 -0000	1.123
+++ fvwm/fvwm.1.in	2 Mar 2004 16:49:00 -0000
@@ -5753,6 +5753,26 @@
 which mouse button is used to tear off menus.  See the section
 "Tear Off Menus" for details.
 
+By default, the binding applies to all windows. You can specify that
+the binding only applies within an individual window by prefixing the
+.I Context
+field with a wildcard pattern (in brackets) containing the class, resource
+or name of the window you want the binding to apply to.
+
+The following example shows how the same key-binding can be used to
+perform different functions depending on the window that is focussed:
+.EX
+Key V (rxvt)A C Echo ctrl-V-in-RXVT
+Key V (*term)A C Echo ctrl-V-in-Term
+Key V A C Echo ctrl-V-elsewhere
+.EE
+
+This example shows how to display the WindowList when Button 3 is
+pressed on an rxvt window:
+.EX
+Mouse 3 (rxvt)A A WindowList
+.EE
+
 .I Modifiers
 is any combination of 'N' for no modifiers, 'C' for control, 'S'
 for shift, 'M' for Meta, 'L' for Caps-Lock or 'A' for any
Index: fvwm/module_interface.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/module_interface.c,v
retrieving revision 1.159
diff -u -r1.159 module_interface.c
--- fvwm/module_interface.c	2 Mar 2004 11:19:33 -0000	1.159
+++ fvwm/module_interface.c	2 Mar 2004 16:49:11 -0000
@@ -619,7 +619,7 @@
 				GetContext(
 					NULL, exc->w.fw, &tmpevent,
 					&targetWindow),
-				BIND_KEYPRESS);
+				BIND_KEYPRESS, &exc->w.fw->class, exc->w.fw->name.name);
 			if (escape != NULL)
 			{
 				if (!strcasecmp(escape,"escapefunc"))
Index: libs/Bindings.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/libs/Bindings.c,v
retrieving revision 1.60
diff -u -r1.60 Bindings.c
--- libs/Bindings.c	17 Feb 2004 14:17:06 -0000	1.60
+++ libs/Bindings.c	2 Mar 2004 16:49:13 -0000
@@ -52,6 +52,10 @@
 	{
 		free(b->Action2);
 	}
+	if (b->windowName)
+	{
+		free(b->windowName);
+	}
 	free(b);
 
 	return;
@@ -128,7 +132,7 @@
 	Display *dpy, Binding **pblist, binding_t type,
 	STROKE_ARG(void *stroke)
 	int button, KeySym keysym, char *key_name, int modifiers, int contexts,
-	void *action, void *action2)
+	void *action, void *action2, char *windowName)
 {
 	int i;
 	int min;
@@ -250,6 +254,8 @@
 					(action) ? stripcpy(action) : NULL;
 				(*pblist)->Action2 =
 					(action2) ? stripcpy(action2) : NULL;
+				(*pblist)->windowName =
+					windowName ? stripcpy(windowName) : NULL;
 				(*pblist)->NextBinding = temp;
 				bound_mask |= bind_mask;
 				count++;
@@ -259,7 +265,11 @@
 	return count;
 }
 
-static Bool AreBindingsEqual(Binding *b1, Binding *b2)
+/*
+ * replacesBinding() - does the new binding, b1, replace a current
+ * binding, b2?
+ */
+static Bool replacesBinding(Binding *b1, Binding *b2)
 {
 	if (b1->type != b2->type)
 	{
@@ -277,6 +287,24 @@
 	{
 		return False;
 	}
+
+	/* definition: "global binding" => b->windowName == NULL
+	 * definition: "window-specific binding" => b->windowName != NULL
+	 */
+	if (b1->windowName && b2->windowName)
+	{
+		/* Both bindings are window-specific. The existing binding, b2,
+		 * is only replaced (by b1) if it applies to the same window */
+		if (strcmp(b1->windowName, b2->windowName) != 0)
+			return False;
+	}
+	else if (b1->windowName || b2->windowName)
+	{
+		/* 1 binding is window-specific, the other is global - no need to
+		 * replace this binding. */
+		return False;
+	}
+
 	if (BIND_IS_KEY_BINDING(b1->type) || BIND_IS_MOUSE_BINDING(b1->type))
 	{
 		return True;
@@ -301,7 +329,7 @@
 void CollectBindingList(
 	Display *dpy, Binding **pblist_src, Binding **pblist_dest,
 	binding_t type, STROKE_ARG(void *stroke)
-	int button, KeySym keysym, int modifiers, int contexts)
+	int button, KeySym keysym, int modifiers, int contexts, char *windowName)
 {
 	Binding *tmplist = NULL;
 	Binding *btmp;
@@ -312,7 +340,7 @@
 	/* generate a private list of bindings to be removed */
 	AddBinding(
 		dpy, &tmplist, type, STROKE_ARG(stroke)
-		button, keysym, NULL, modifiers, contexts, NULL, NULL);
+		button, keysym, NULL, modifiers, contexts, NULL, NULL, windowName);
 	/* now find equivalent bindings in the given binding list and move
 	 * them to the new clist */
 	for (bold = *pblist_src, oldprev = NULL; bold != NULL;
@@ -321,7 +349,7 @@
 		for (btmp = tmplist, tmpprev = NULL; btmp != NULL;
 		     tmpprev = btmp, btmp = btmp->NextBinding)
 		{
-			if (AreBindingsEqual(btmp, bold))
+			if (replacesBinding(btmp, bold))
 			{
 				/* move matched binding from src list to dest
 				 * list */
@@ -342,10 +370,37 @@
 	return;
 }
 
+/*
+ * bindingAppliesToWindow()
+ *
+ * The Key/Mouse/PointerKey syntax (optionally) allows a window name
+ * (or class or resource) to be specified with the binding, denoting
+ * which windows the binding can be invoked in. This function determines
+ * if the binding actually applies to a window based on its
+ * name/class/resource.
+ */
+Bool bindingAppliesToWindow(Binding *binding, const XClassHint *winClass,
+	const char *winName)
+{
+	/* If no window name is specified with the binding then that means
+	 * the binding applies to ALL windows. */
+	if (binding->windowName == NULL)
+		return True;
+
+	if (matchWildcards(binding->windowName, winName) == TRUE ||
+		matchWildcards(binding->windowName, winClass->res_name) == TRUE ||
+		matchWildcards(binding->windowName, winClass->res_class) == TRUE)
+	{
+		return True;
+	}
+	return False;
+}
+
 Bool __compare_binding(
 	Binding *b, STROKE_ARG(char *stroke)
 	int button_keycode, unsigned int modifier, unsigned int used_modifiers,
-	int Context, binding_t type)
+	int Context, binding_t type, const XClassHint *winClass,
+    const char *winName)
 {
 	if (b->type != type || !(b->Context & Context))
 	{
@@ -356,24 +411,30 @@
 	{
 		return False;
 	}
+
 	if (BIND_IS_MOUSE_BINDING(type) &&
-	    (b->Button_Key == button_keycode || b->Button_Key == 0))
+	    (b->Button_Key != button_keycode && b->Button_Key != 0))
 	{
-		return True;
+		return False;
 	}
-	if (BIND_IS_KEY_BINDING(type) &&
-	    b->Button_Key == button_keycode)
+	else if (BIND_IS_KEY_BINDING(type) && b->Button_Key != button_keycode)
 	{
-		return True;
+		return False;
 	}
-	if (0 STROKE_CODE(|| ((BIND_IS_STROKE_BINDING(type) &&
-			       (strcmp(b->Stroke_Seq,stroke) == 0) &&
-			       b->Button_Key == button_keycode))))
+#ifdef HAVE_STROKE
+	else if (BIND_IS_STROKE_BINDING(type) &&
+		(strcmp(b->Stroke_Seq, stroke) != 0) &&
+		 b->Button_Key != button_keycode)
 	{
-		return True;
+		return False;
 	}
+#endif
 
-	return False;
+	if (!bindingAppliesToWindow(b, winClass, winName))
+	{
+		return False;
+	}
+	return True;
 }
 
 /* Check if something is bound to a key or button press and return the action
@@ -381,84 +442,68 @@
 void *CheckBinding(
 	Binding *blist, STROKE_ARG(char *stroke)
 	int button_keycode, unsigned int modifier,unsigned int dead_modifiers,
-	int Context, binding_t type)
+	int Context, binding_t type, const XClassHint *winClass,
+	const char *winName)
 {
 	Binding *b;
 	unsigned int used_modifiers = ~dead_modifiers;
+	void *action = NULL;
 
 	modifier &= (used_modifiers & ALL_MODIFIERS);
 	for (b = blist; b != NULL; b = b->NextBinding)
 	{
 		if (__compare_binding(
-			    b, STROKE_ARG(stroke) button_keycode, modifier,
-			    used_modifiers, Context, type) == True)
+				b, STROKE_ARG(stroke) button_keycode, modifier,
+			    used_modifiers, Context, type, winClass, winName) == True)
 		{
-			return b->Action;
+			action = b->Action;
+			/* If this is a global binding, keep searching <blist> in the
+			 * hope of finding a window-specific binding. */
+			if (b->windowName)
+				break;
 		}
 	}
 
-	return NULL;
+	return action;
 }
 
 void *CheckTwoBindings(
 	Bool *ret_is_second_binding, Binding *blist, STROKE_ARG(char *stroke)
 	int button_keycode, unsigned int modifier,unsigned int dead_modifiers,
-	int Context, binding_t type, int Context2, binding_t type2)
+	int Context, binding_t type, const XClassHint *winClass,
+	const char *winName, int Context2, binding_t type2,
+	const XClassHint *winClass2, const char *winName2)
 {
 	Binding *b;
 	unsigned int used_modifiers = ~dead_modifiers;
+	void *action = NULL;
 
 	modifier &= (used_modifiers & ALL_MODIFIERS);
 	for (b = blist; b != NULL; b = b->NextBinding)
 	{
 		if (__compare_binding(
 			    b, STROKE_ARG(stroke) button_keycode, modifier,
-			    used_modifiers, Context, type) == True)
+			    used_modifiers, Context, type, winClass, winName) == True)
 		{
 			*ret_is_second_binding = False;
-			return b->Action;
+			action = b->Action;
+			if (b->windowName)
+				break;
 		}
 		if (__compare_binding(
 			    b, STROKE_ARG(stroke) button_keycode, modifier,
-			    used_modifiers, Context2, type2) == True)
+			    used_modifiers, Context2, type2, winClass2, winName2) == True)
 		{
+			if (action && !b->windowName)
+				continue;
 			*ret_is_second_binding = True;
-			return b->Action;
+			action = b->Action;
+			if (b->windowName)
+				break;
 		}
 	}
-	*ret_is_second_binding = False;
 
-	return NULL;
-}
-
-/* same as above, but only returns exactly matching bindings, i.e. wildcards for
- * mouse buttons and modifiers must match exactly. */
-Bool MatchBindingExactly(
-	Binding *b, STROKE_ARG(void *stroke)
-	int button, KeyCode keycode, unsigned int modifier, int Context,
-	binding_t type)
-{
-	modifier &= ALL_MODIFIERS;
-	if (b->type != type || b->Context != Context || b->Modifier != modifier)
-	{
-		return False;
-	}
-	if (BIND_IS_KEY_BINDING(type) && b->Button_Key != keycode)
-	{
-		return False;
-	}
-	if (BIND_IS_MOUSE_BINDING(type) && b->Button_Key != button)
-	{
-		return False;
-	}
-	if (0 STROKE_CODE(|| ((BIND_IS_STROKE_BINDING(type) &&
-			       (b->Button_Key != button ||
-				(strcmp(b->Stroke_Seq,stroke) != 0))))))
-	{
-		return False;
-	}
-
-	return True;
+	return action;
 }
 
 /*
@@ -658,8 +703,8 @@
 	is_grabbing_everything = True;
 	for ( ; blist != NULL; blist = blist->NextBinding)
 	{
-		GrabWindowButton(
-			dpy, w, blist, contexts, dead_modifiers, cursor, fGrab);
+		GrabWindowButton(dpy, w, blist, contexts, dead_modifiers,
+			cursor, fGrab);
 	}
 	is_grabbing_everything = False;
 	MyXUngrabServer(dpy);
Index: libs/Bindings.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/libs/Bindings.h,v
retrieving revision 1.6
diff -u -r1.6 Bindings.h
--- libs/Bindings.h	5 Jul 2003 01:37:47 -0000	1.6
+++ libs/Bindings.h	2 Mar 2004 16:49:13 -0000
@@ -1,5 +1,7 @@
 /* -*-c-*- */
 
+#include <X11/Xutil.h>	/* XClassHint */
+
 #ifndef FVWMLIB_BINDINGS_H_H
 #define FVWMLIB_BINDINGS_H_H
 
@@ -32,12 +34,14 @@
 {
 	binding_t_t type;       /* Is it a mouse, key, or stroke binding */
 	STROKE_CODE(void *Stroke_Seq;) /* stroke sequence */
-		int Button_Key;         /* Mouse Button number or Keycode */
+	int Button_Key;         /* Mouse Button number or Keycode */
 	char *key_name;         /* In case of keycode, give the key_name too */
 	int Context;            /* Fvwm context, ie titlebar, frame, etc */
 	int Modifier;           /* Modifiers for keyboard state */
 	void *Action;           /* What to do? */
 	void *Action2;          /* This one can be used too */
+	char *windowName;		/* Name of window (regex pattern) this binding
+							   applies to. NULL means all windows. */
 	struct Binding *NextBinding;
 } Binding;
 
@@ -46,11 +50,11 @@
 void CollectBindingList(
 	Display *dpy, Binding **pblist_src, Binding **pblist_dest,
 	binding_t type, STROKE_ARG(void *stroke) int button, KeySym keysym,
-	int modifiers, int contexts);
+	int modifiers, int contexts, char *windowName);
 int AddBinding(
 	Display *dpy, Binding **pblist, binding_t type,
 	STROKE_ARG(void *stroke) int button, KeySym keysym, char *key_name,
-	int modifiers, int contexts, void *action, void *action2);
+	int modifiers, int contexts, void *action, void *action2, char *windowName);
 void FreeBindingStruct(Binding *b);
 void FreeBindingList(Binding *b);
 void RemoveBinding(Binding **pblist, Binding *b, Binding *prev);
@@ -61,14 +65,13 @@
 void *CheckBinding(
 	Binding *blist, STROKE_ARG(char *stroke) int button_keycode,
 	unsigned int modifier, unsigned int dead_modifiers, int Context,
-	binding_t type);
+	binding_t type, const XClassHint *winClass, const char *winName);
 void *CheckTwoBindings(
 	Bool *ret_is_second_binding, Binding *blist, STROKE_ARG(char *stroke)
 	int button_keycode, unsigned int modifier,unsigned int dead_modifiers,
-	int Context, binding_t type, int Context2, binding_t type2);
-Bool MatchBindingExactly(
-	Binding *b, STROKE_ARG(void *stroke) int button, KeyCode keycode,
-	unsigned int modifier, int Context, binding_t type);
+	int Context, binding_t type, const XClassHint *winClass,
+	const char *winName, int Context2, binding_t type2,
+	const XClassHint *winClass2, const char *winName2);
 void GrabWindowKey(
 	Display *dpy, Window w, Binding *binding, unsigned int contexts,
 	unsigned int dead_modifiers, Bool fGrab);
@@ -88,5 +91,8 @@
 	Display *dpy, Window w, Binding *binding, unsigned int contexts,
 	unsigned int dead_modifiers, Cursor cursor, Bool fGrab);
 KeySym FvwmStringToKeysym(Display *dpy, char *key);
+Bool bindingAppliesToWindow(Binding *binding, const XClassHint *winClass,
+	const char *winName);
+
 
 #endif /* FVWMLIB_BINDINGS_H_H */
Index: libs/fvwmlib.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/libs/fvwmlib.h,v
retrieving revision 1.119
diff -u -r1.119 fvwmlib.h
--- libs/fvwmlib.h	7 Aug 2003 17:23:27 -0000	1.119
+++ libs/fvwmlib.h	2 Mar 2004 16:49:15 -0000
@@ -109,7 +109,7 @@
 #include "envvar.h"
 #include "safemalloc.h"
 
-int matchWildcards(char *pattern, char *string);
+extern int matchWildcards(const char *pattern, const char *string);
 
 #define MAX_MODULE_INPUT_TEXT_LEN 1000
 
Index: libs/wild.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/libs/wild.c,v
retrieving revision 1.8
diff -u -r1.8 wild.c
--- libs/wild.c	29 Jun 2003 19:53:24 -0000	1.8
+++ libs/wild.c	2 Mar 2004 16:49:15 -0000
@@ -34,7 +34,7 @@
  *      It is public domain, no strings attached. No guarantees either.
  *
  */
-int matchWildcards(char *pattern, char *string)
+int matchWildcards(const char *pattern, const char *string)
 {
 	if(string == NULL)
 	{
Index: libs/wild.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/libs/wild.h,v
retrieving revision 1.4
diff -u -r1.4 wild.h
--- libs/wild.h	29 Jun 2003 19:53:24 -0000	1.4
+++ libs/wild.h	2 Mar 2004 16:49:15 -0000
@@ -9,4 +9,4 @@
  *      It is public domain, no strings attached. No guarantees either.
  *
  */
-int matchWildcards(char *pattern, char *string);
+int matchWildcards(const char *pattern, const char *string);
Index: modules/FvwmIconBox/FvwmIconBox.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/modules/FvwmIconBox/FvwmIconBox.h,v
retrieving revision 1.29
diff -u -r1.29 FvwmIconBox.h
--- modules/FvwmIconBox/FvwmIconBox.h	6 Jul 2003 14:34:07 -0000	1.29
+++ modules/FvwmIconBox/FvwmIconBox.h	2 Mar 2004 16:49:16 -0000
@@ -63,7 +63,7 @@
 extern Bool SortItem(struct icon_info *item);
 extern struct icon_info *MinItem(struct icon_info *head);
 extern void ExecuteKey(XEvent event);
-extern int matchWildcards(char *pattern, char *string);
+extern int matchWildcards(const char *pattern, const char *string);
 extern Bool GetBackPixmap(void);
 extern int LookInList(struct icon_info *item);
 extern char *stripcpy2(char *source);
Index: modules/FvwmScript/FvwmScript.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/modules/FvwmScript/FvwmScript.c,v
retrieving revision 1.70
diff -u -r1.70 FvwmScript.c
--- modules/FvwmScript/FvwmScript.c	4 Jan 2004 12:39:30 -0000	1.70
+++ modules/FvwmScript/FvwmScript.c	2 Mar 2004 16:49:20 -0000
@@ -857,6 +857,7 @@
   Bool find = False;
   char *action;
   int ex, ey, ex2, ey2;
+  XClassHint tmp;
 
   while (FEventsQueued(dpy, QueuedAfterReading))
   {
@@ -936,14 +937,17 @@
 	XKeysymToKeycode(dpy,XKeycodeToKeysym(dpy,event.xkey.keycode,0));
 
       /* check for bindings defined by the Key instruction */
+	  tmp.res_class = tmp.res_name = "root";
       if ((action = CheckBinding(
 		   BindingsList, STROKE_ARG(0) event.xkey.keycode,
-		   event.xkey.state, LockMask, C_WINDOW, BIND_KEYPRESS)) !=
+		   event.xkey.state, LockMask, C_WINDOW, BIND_KEYPRESS,
+		   &tmp, tmp.res_class)) !=
 	  NULL)
       {
 	SendMsgAndString(action, "CheckBinding");
 	break;
       }
+
       if (ks == XK_Tab) {
 	isTab = 1;
 	if (event.xkey.state & ShiftMask) {
Index: modules/FvwmScript/Instructions.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/modules/FvwmScript/Instructions.c,v
retrieving revision 1.50
diff -u -r1.50 Instructions.c
--- modules/FvwmScript/Instructions.c	5 Jul 2003 01:37:47 -0000	1.50
+++ modules/FvwmScript/Instructions.c	2 Mar 2004 16:49:23 -0000
@@ -1828,7 +1828,7 @@
 
   AddBinding(
 	  dpy, &BindingsList, BIND_KEYPRESS, STROKE_ARG(0) 0, keysym,
-	  key_string, modifier, C_WINDOW, (void *)action, NULL);
+	  key_string, modifier, C_WINDOW, (void *)action, NULL, NULL);
   free(key_string);
   free(in_modifier);
 }

---

From: scott <[email-masked]>
Date: Wed, 3 Mar 2004 04:11:26 +1100

Final patch - removes some unused code I left lying around.

It's 4am - time for bed!

SCoTT. :)
+++ fvwm/bindings.c	2 Mar 2004 17:08:19 -0000
@@ -237,8 +237,8 @@
@@ -361,7 +361,34 @@
@@ -437,7 +464,7 @@
@@ -502,7 +529,7 @@
 
 	return rc;
 }
Index: fvwm/builtins.c
+++ fvwm/builtins.c	2 Mar 2004 17:08:26 -0000
 
 	/* execute the action */
+++ fvwm/events.c	2 Mar 2004 17:08:36 -0000
 
 	PressedW = None;
 
 	PressedW = None;

+++ fvwm/fvwm.1.in	2 Mar 2004 17:09:13 -0000
 
+++ fvwm/module_interface.c	2 Mar 2004 17:09:23 -0000
+++ libs/Bindings.c	2 Mar 2004 17:09:25 -0000
@@ -52,6 +52,10 @@
 	{
 		free(b->Action2);
 	}
+	if (b->windowName)
+	{
+		free(b->windowName);
+	}
 	free(b);
 
 	return;
@@ -128,7 +132,7 @@

+/*
 
-	return False;

 
-	return NULL;
+	return action;
 }
 
 void *CheckTwoBindings(

-	return NULL;
-}
-
 
 /*
@@ -658,8 +703,8 @@
+++ libs/Bindings.h	2 Mar 2004 17:09:25 -0000
@@ -1,5 +1,7 @@
 /* -*-c-*- */

@@ -32,12 +34,14 @@
 {
 
@@ -46,11 +50,11 @@
 void CollectBindingList(
 
+++ libs/fvwmlib.h	2 Mar 2004 17:09:27 -0000
@@ -109,7 +109,7 @@
 #include "envvar.h"
 #include "safemalloc.h"

 
Index: libs/wild.c
+++ libs/wild.c	2 Mar 2004 17:09:27 -0000
+++ libs/wild.h	2 Mar 2004 17:09:27 -0000
+++ modules/FvwmIconBox/FvwmIconBox.h	2 Mar 2004 17:09:28 -0000
+++ modules/FvwmScript/FvwmScript.c	2 Mar 2004 17:09:32 -0000
@@ -857,6 +857,7 @@
   Bool find = False;
   char *action;
   int ex, ey, ex2, ey2;
+  XClassHint tmp;

+++ modules/FvwmScript/Instructions.c	2 Mar 2004 17:09:35 -0000
@@ -1828,7 +1828,7 @@
 
   AddBinding(