Subject: Synthetic Button Press Patch

From: Greg Badros <[email-masked]>
Date: 11 Sep 1997 13:35:06 -0700

Below is a patch derived from GWM code which creates a new command
"SendButtonPress" that creates a synthetic button press in the window
that the pointer is in.  I use this in conjunction with keybindings that
move the mouse pointer around so that I need not reach for my mouse.
Better for CTS, general comfort, ease of use, and in case you don't have
a mouse.  Lots more could be done to make the patch even nicer,
including:

Arguments for whether you want to send just a press, just a release, or
both (with a specified delay);  currently sends both w/ a fixed delay.

My use of the patch is like this:

#------------
AddToFunc "KeyMouseMoves" 
+ "I" Key $0 A $4 CursorMove  -$5   0
+ "I" Key $1 A $4 CursorMove    0  $5
+ "I" Key $2 A $4 CursorMove    0 -$5
+ "I" Key $3 A $4 CursorMove   $5   0

KeyMouseMoves h j k l CSM 7
KeyMouseMoves y u i o CSM 1

# This stuff requires a [email-masked] patch
Key 7 A CSM SendButtonPress 1 0 0
Key 8 A CSM SendButtonPress 2 0 0
Key 9 A CSM SendButtonPress 3 0 0
#--------------

Patch is below, and technically is relative to fvwm-2.0.45 w/ my
animated menus and gperf command hashing patch, though the changes you
need are all to builtins.c, except for the addition of the new command
name and #define, etc.

Also available at 
http://www.cs.washington.edu/homes/gjb/patches/fvwm-SendButtonPress.patch

Feel free to provide feedback (esp. bugs!)  Use at your own risk, etc.

Greg J. Badros
[email-masked]
Seattle, WA  USA
http://www.cs.washington.edu/homes/gjb

Index: builtins.c
===================================================================
RCS file: /homes/gws/gjb/.CVS/fvwm/fvwm/builtins.c,v
retrieving revision 1.16
retrieving revision 1.18
diff -u -r1.16 -r1.18
--- builtins.c	1997/05/07 23:09:15	1.16
+++ builtins.c	1997/09/11 20:07:22	1.18
@@ -909,6 +909,96 @@
   XFlush (dpy);
 }
 
+/* Stolen from GWM 1.8c --gjb */
+void
+fill_x_button_event(XButtonEvent *evt, int type, int button, int modifier, 
+		    int x, int y, int x_root, int y_root, Window child, Window sub_window)
+{
+    evt -> type = type;
+    evt -> display = dpy;
+    evt -> window = child;
+    evt -> subwindow = sub_window;
+    evt -> root = Scr.Root;
+    evt -> time = lastTimestamp + (type == ButtonPress? 0 : 5);
+    evt -> x = x;
+    evt -> y = y;
+    evt -> x_root = x_root;
+    evt -> y_root = y_root;
+    evt -> same_screen = 1;
+    evt -> button = button;
+    evt -> state = modifier;
+}
+
+Window
+WindowGettingButtonEvent(Window w, int x, int y)
+{
+    int x2, y2;
+    Window child, w2 = w;
+    XWindowAttributes wa;
+
+ find_window:
+    XTranslateCoordinates(dpy, w, w2, x, y, &x2, &y2, &child);
+    if (child) {
+	x = x2;
+	y = y2;
+	w = w2;
+	w2 = child;
+	goto find_window;
+    }
+    x = x2;
+    y = y2;
+    w = w2;
+
+ find_listener:
+    XGetWindowAttributes(dpy, w, &wa);
+    if (!(wa.all_event_masks & (ButtonPressMask | ButtonReleaseMask))) {
+	Window d1, *d3, parent;
+	unsigned int d4;
+	
+	XQueryTree(dpy, w, &d1, &parent, &d3, &d4);
+	if (d3) XFree(d3);
+	if (parent) {
+	    w = parent;
+	    goto find_listener;
+	}
+    }
+    return w;
+}
+
+/* Inspired by GWM 1.8c --gjb */
+void SendButtonPress(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+		     unsigned long context, char *action, int *Module)
+{
+  char *szButton = strtok(action," \t\n");
+  char *szModifier = strtok(NULL," \t\n");
+  char *szPropagate = strtok(NULL," \t\n");
+  Window child;
+  XButtonEvent event;
+  int button = atoi(szButton);
+  int modifier = atoi(szModifier);
+  int propagate = atoi(szPropagate);
+  int x, y, x_root, y_root, x2, y2;
+  int JunkMask;
+  Window JunkRoot;
+  Window pointer_win, dummy;
+  XQueryPointer( dpy, w, &JunkRoot, &pointer_win,
+                 &x_root,&y_root,&x, &y, &JunkMask);
+
+  child = WindowGettingButtonEvent(pointer_win,x,y);
+  x2 = x; y2 = y;
+
+  XTranslateCoordinates(dpy, pointer_win, child, x2, y2,
+			  &x, &y, &dummy);
+  fill_x_button_event(&event, ButtonPress, button, modifier, 
+		      x, y, x_root, y_root, child, 0);
+  XSendEvent(dpy, PointerWindow, propagate, ButtonPressMask, 
+	     (XEvent *) &event);
+  fill_x_button_event(&event, ButtonRelease, button, modifier | (1 << (button+7)),
+		      x, y, x_root, y_root, child, 0);
+  XSendEvent(dpy, PointerWindow, propagate, ButtonReleaseMask, 
+	     (XEvent *) &event);
+  /*  fprintf(stderr,"New Sent button press of %d at %d, %d; time = %ld\n",button,x,y,lastTimestamp); */
+}
 
 void stick_function(XEvent *eventp,Window w,FvwmWindow *tmp_win,
 		    unsigned long context, char *action, int *Module)
@@ -1106,6 +1196,9 @@
 {
   if (action && *action)
   {
+    int len = strlen(action);
+    if (action[len-1] == '\n')
+      action[len-1] = '\0'; /* drop the closing carraige return */
     fvwm_msg(INFO,"Echo",action);
   }
 }
@@ -2820,7 +2913,6 @@
     char *szVar = NULL;
     char *szValue = NULL;
     char *szNoOverwrite = NULL;
-    char *szPutenv = NULL;
     Bool fOverwrite = TRUE;
     action = GetNextToken(action,&szVar);
     action = GetNextToken(action,&szValue);
@@ -2830,12 +2922,15 @@
 #ifdef USE_SETENV_CALL /* this call exists on linux */
     setenv(szVar,szValue,fOverwrite?1:0);
 #else
+    {
+    char *szPutenv = NULL;
     if (fOverwrite == FALSE) {
       fvwm_msg(WARN,"SetEnv","Ignoring NoOverwrite directive. Recompile with -DUSE_SETENV_CALL");
     }
     szPutenv = safemalloc(strlen(szVar)+strlen(szValue)+2);
     sprintf(szPutenv,"%s=%s",szVar,szValue);
     putenv(szPutenv);
+    }
 #endif
 }
 
Index: functions.c
===================================================================
RCS file: /homes/gws/gjb/.CVS/fvwm/fvwm/functions.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- functions.c	1997/04/21 06:03:32	1.6
+++ functions.c	1997/09/11 18:53:43	1.7
@@ -133,6 +133,7 @@
   {"Restart",      restart_function, F_RESTART,             FUNC_NO_WINDOW},
   {"KillModule",   module_zapper,    F_ZAP,                 FUNC_NO_WINDOW},
   {"SendToModule", SendStrToModule,  F_SEND_STRING,         FUNC_NO_WINDOW},
+  {"SendButtonPress", SendButtonPress, F_SEND_BUTTONPRESS,  FUNC_NEEDS_WINDOW},
   {"Scroll",       scroll,           F_SCROLL,              FUNC_NO_WINDOW},
   {"Stick",        stick_function,   F_STICK,               FUNC_NEEDS_WINDOW},
   {"Send_WindowList",send_list_func, F_SEND_WINDOW_LIST,    FUNC_NO_WINDOW},
Index: misc.h
===================================================================
RCS file: /homes/gws/gjb/.CVS/fvwm/fvwm/misc.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- misc.h	1997/05/07 23:11:16	1.10
+++ misc.h	1997/09/11 18:53:48	1.11
@@ -510,6 +510,8 @@
 void SetBorderStyle(XEvent *eventp,Window w,FvwmWindow *tmp_win,
 		    unsigned long context, char *action,int* Module);
 #endif
+void SendButtonPress(XEvent *eventp,Window w,FvwmWindow *tmp_win,
+		     unsigned long context, char *action, int *Module);
 void SetMenuStyle(XEvent *eventp,Window w,FvwmWindow *tmp_win,
 		unsigned long context, char *action,int* Module);
 void SetTitleStyle(XEvent *eventp,Window w,FvwmWindow *tmp_win,
Index: parse.h
===================================================================
RCS file: /homes/gws/gjb/.CVS/fvwm/fvwm/parse.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- parse.h	1997/04/21 06:22:09	1.5
+++ parse.h	1997/09/11 18:53:50	1.6
@@ -75,6 +75,7 @@
     F_COLORMAP_FOCUS,
     F_TITLESTYLE,
     F_EXEC_SETUP,
+    F_SEND_BUTTONPRESS,
     F_SETENV,
     F_SET_ANIMATION,
     F_SET_MENUDELAY,
--
Visit the official FVWM web page at <URL:http://www.hpc.uh.edu/fvwm/>.
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].