Subject: Re: Patch to allow multiple window names in conditions

From: Norman Yarvin <[email-masked]>
Date: Sun, 4 Jul 2004 15:43:47 -0400

About a month ago, I sent in a patch to allow multiple window names in
conditions.  It was developed for my own use, but since I am a lazy guy,
I was hoping the fvwm workers would pick it up, so I wouldn't have to
keep maintaining it.  That doesn't seem to have happened.  Perhaps I
should have mentioned that the patch is backward-compatible with old
fvwm2rc files.  It is mildly half-assed in the sense that it doesn't
implement a full boolean expression parser, but in exchange it is small
and simple.  It assumes that the boolean operator the user wants is "OR",
or in the case of a window name prefixed with '!', "AND NOT".  The
following is a rehash of the patch against the current CVS tree.
ChangeLog and man page updates are included.  If something's wrong with
it, I'd appreciate hearing what.

Index: ChangeLog
===================================================================
RCS file: /home/cvs/fvwm/fvwm/ChangeLog,v
retrieving revision 1.2512
diff -u -u -r1.2512 ChangeLog
--- ChangeLog	1 Jul 2004 14:34:22 -0000	1.2512
+++ ChangeLog	4 Jul 2004 19:23:31 -0000
@@ -1,3 +1,11 @@
+2004-07-04  Norman Yarvin  <[email-masked]>
+
+	* fvwm/fvwm.1.in:
+	* fvmw/fvwm.h:
+	* fvwm/conditional.c:
+	modified the code for conditions so that multiple window names may
+	be specified in each condition
+	
 2004-07-01  Dominik Vogt  <[email-masked]>
 
 	* fvwm/move_resize.c (GetResizeArguments):
Index: fvwm/conditional.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/conditional.c,v
retrieving revision 1.93
diff -u -u -r1.93 conditional.c
--- fvwm/conditional.c	9 May 2004 12:59:55 -0000	1.93
+++ fvwm/conditional.c	4 Jul 2004 19:23:33 -0000
@@ -382,13 +382,14 @@
  */
 void FreeConditionMask(WindowConditionMask *mask)
 {
-	if (mask->my_flags.needs_name)
-	{
-		free(mask->name);
-	}
-	else if (mask->my_flags.needs_not_name)
-	{
-		free(mask->name - 1);
+	struct namelist *p,*p2;
+
+	for (p=mask->namelist; p;)
+	{ 
+		free(p->name);
+		p2=p->next;
+		free(p);
+		p=p2;
 	}
 
 	return;
@@ -614,21 +615,16 @@
 			}
 			mask->my_flags.needs_same_layer = on;
 		}
-		else if (!mask->my_flags.needs_name &&
-			 !mask->my_flags.needs_not_name)
-		{
-			/* only 1st name to avoid mem leak */
-			mask->name = condition;
-			condition = NULL;
-			if (on == 0)
-			{
-				mask->my_flags.needs_not_name = 1;
-				mask->name++;
-			}
-			else
-			{
-				mask->my_flags.needs_name = 1;
-			}
+		else
+		{ 
+			struct namelist *p;
+
+			p = (struct namelist *)
+				safemalloc(sizeof(struct namelist));
+			p->name = condition;
+			p->next = mask->namelist;
+			mask->namelist = p;
+			condition = NULL; 
 		}
 
 		if (prev_condition)
@@ -663,6 +659,9 @@
 	int is_on_page;
 	int is_on_global_page;
 	FvwmWindow *sf = get_focus_window();
+	struct namelist *p;
+	char *name;
+	Bool invert,tried,matched;
 
 	/* match FixedSize conditional */
 	/* special treatment for FixedSize, because more than just
@@ -822,27 +821,52 @@
 		}
 	}
 
-	/* Yes, I know this could be shorter, but it's hard to understand then
-	 */
-	does_name_match = matchWildcards(mask->name, fw->name.name);
-	does_icon_name_match = matchWildcards(mask->name, fw->icon_name.name);
-	does_class_match =
-		(fw->class.res_class && matchWildcards(
-			mask->name,fw->class.res_class));
-	does_resource_match =
-		(fw->class.res_name && matchWildcards(
-			mask->name, fw->class.res_name));
-	does_match =
-		(does_name_match || does_icon_name_match || does_class_match ||
-		 does_resource_match);
-	if (mask->my_flags.needs_name && !does_match)
-	{
-		return False;
-	}
-	if (mask->my_flags.needs_not_name && does_match)
-	{
-		return False;
-	}
+	tried = False;
+	matched = False;
+
+	for(p=mask->namelist; p; p=p->next)
+	{ 
+		name = p->name;
+		invert = False;
+		if(*name == '!')
+		{
+			name++;
+			invert = True;
+		}
+
+		/* Yes, I know this could be shorter, but it's hard to
+		 * understand then
+		 */
+		does_name_match = matchWildcards(name, fw->name.name);
+		does_icon_name_match = 
+				  matchWildcards(name, fw->icon_name.name);
+		does_class_match =                    (fw->class.res_class && 
+				  matchWildcards(name, fw->class.res_class));
+		does_resource_match =                 (fw->class.res_name &&
+				  matchWildcards(name, fw->class.res_name));
+		does_match =
+			(does_name_match || does_icon_name_match ||
+			 does_class_match || does_resource_match);
+
+		if(invert && does_match)
+		{
+			return False;		/* "!window" takes priority */
+		}
+		if(!invert && does_match)
+		{
+			matched = True;		/* this condition satisfied */
+		}
+		if(!invert)
+		{
+			tried = True;
+		}
+	} 
+
+	if(tried && !matched)
+	{ 
+		return False; 
+	}	
+
 	if (mask->layer == -1 && sf)
 	{
 		int is_same_layer;
Index: fvwm/fvwm.1.in
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/fvwm.1.in,v
retrieving revision 1.141
diff -u -u -r1.141 fvwm.1.in
--- fvwm/fvwm.1.in	1 Jul 2004 14:34:23 -0000	1.141
+++ fvwm/fvwm.1.in	4 Jul 2004 19:23:46 -0000
@@ -10366,15 +10366,42 @@
 
 In addition, the
 .I conditions
-may include one window name to match to.  The window name may
-include the wildcards '*' and '?'.  The window name, icon name,
-class, and resource are considered when attempting to find a
-match.  The window name can begin with '!' which prevents
+may include one or more window names to match to.  The window
+names may include the wildcards '*' and '?'.  The window name,
+icon name, class, and resource are considered when attempting to
+find a match.  If more than one window name is given, the
+implicit operator is a boolean OR -- that is, the condition will
+be satisfied if the window has any of those names.  Window names
+to avoid may also be specified: each window
+name can begin with '!' which prevents
 .I command
 if any of the window name, icon name, class or resource match.
+If a mixture of names beginning with '!' and names
+without '!' is specified, then the implicit operator is an OR
+on the names without '!' and an AND NOT on the names with '!';
+that is, the condition is satisfied if none of the names with '!'
+match, and if one or more of the names without '!' match.
 
-Any condition can be negated by using a an exclamation mark ('!')
-directly in front of its name.
+Examples:
+.EX
+	Next (Netscape konqueror Mozilla*) WarpToWindow 99 90
+.EE
+This goes to the next web browser window, no matter which of the three
+named web browsers is being used.
+.EX
+	Next (!Netscape !konqueror !Mozilla*) WarpToWindow 99 90
+.EE
+This goes to the next non-browser window; if the above two commands are
+bound to two different keys, the first key pages through browser windows
+and the second through non-browser windows.
+.EX
+	Next (XTerm !console) WarpToWindow 99 90
+.EE
+This goes to the next xterm window, except that the one named
+"console" (with the -name option to xterm) is excluded.
+
+Any of the other conditions, which follow, can also be negated by using a
+an exclamation mark ('!')  directly in front of its name.
 
 .IR AcceptsFocus ,
 .IR CirculateHit ,
Index: fvwm/fvwm.h
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/fvwm.h,v
retrieving revision 1.231
diff -u -u -r1.231 fvwm.h
--- fvwm/fvwm.h	30 May 2004 14:05:48 -0000	1.231
+++ fvwm/fvwm.h	4 Jul 2004 19:23:47 -0000
@@ -400,6 +400,12 @@
 } window_flags;
 
 /* Window mask for Circulate and Direction functions */
+struct namelist
+{
+	char *name;
+	struct namelist *next;
+};
+
 typedef struct WindowConditionMask
 {
 	struct
@@ -419,8 +425,6 @@
 #define NEEDS_TRUE  1
 #define NEEDS_FALSE 2
 		unsigned needs_focus : 2;
-		unsigned needs_name : 1;
-		unsigned needs_not_name : 1;
 		unsigned needs_pointer : 2;
 		unsigned needs_same_layer : 1;
 		unsigned use_circulate_hit : 1;
@@ -430,7 +434,7 @@
 	} my_flags;
 	window_flags flags;
 	window_flags flag_mask;
-	char *name;
+	struct namelist *namelist;
 	int layer;
 } WindowConditionMask;
 
--
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, 7 Jul 2004 02:28:23 +1000

Hi Norman,

> conditions.

I think being able to specify multiple windows in conditions is very
useful.

It's 2:30am ... let me see if I understand your patch correctly:

If you have:

Next (mozilla dillo netscape) Function ...

The relationship b/w mozilla, dillo & netscape is a LOGICAL OR.

but if I have:

Next (!mozilla !dillo !netscape) Function ...

the relationship is a LOGICAL AND.

Have I got that right?

If I have, I'm concerned users might get confused. (I'm sure other
people edit their .fvwm2rc at 2:30am too! :)

I'll sleep on it.

SCoTT. :)

---

From: Norman Yarvin <[email-masked]>
Date: Tue, 6 Jul 2004 16:19:17 -0400

>the relationship is a LOGICAL AND.

Yes.

>people edit their .fvwm2rc at 2:30am too! :)

If you think that's confusing, think about what happens if there's
a mixture of inverted (with '!') and non-inverted window names.
The line:

Next (a !b c !d) Function ...

means "(a OR c) AND NOT b AND NOT d".

This isn't anything I'd plan to use; it's just a side effect of
the way I implemented it.

In any case, point taken: it'd be easier to do this right than to
repeatedly explain to users why it was done in a half-assed way.
To me, "right" means a full-blown boolean expression parser, with
AND, OR, NOT, and parentheses.  In coding, it'd probably be best to
make the individual conditions (the leaf nodes of the parse tree)
be the entire WindowConditionMask structure, not just the window
name, so that conditions like "CurrentScreen" and "Raised" could be
used as part of the boolean expression.  I think that could still
be backward-compatible with current .fvwm2rc files; the current
implicit operator for conditions like "CurrentScreen" is "AND", and
that could be preserved.  I don't think parentheses inside the
expression have any current meaning, nor do "&" (AND) or "|" (OR);
and "!" (NOT) could keep its current meaning.  A recursive-descent
parser for boolean expressions isn't that big a deal to write.  But
as my current patch satisfies my own needs, I'd write such a parser
only if it'd be accepted into the mainline fvwm code.
--
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]