[comp.windows.x] XSendEvent question

frivold@san18.erg.sri.com (Thane J. Frivold) (11/06/90)

	I am currently working on an application (a modified `twm' to
be exact) that allows for a certain amount of `remote control' of X11
applications.  Once a common application has been determined, a
controlling user's host replicates the pertinent X11 events to all the
listening hosts.  All event delivery to the selected application is
done via calls to XSendEvent().

	The event capture is done by creating an InputOnly `capture
window' whose parent is the selected application (target) window and
whose event mask has all bits set, except `PointerMotionHintMask'.

	The intent is to intercept all events normally destined for
the selected application in order to decide, as a function of control
state, whether or not the event should be propogated to the other
hosts (as well as delivered locally).

	Currently, the application *does* work for client applications
that have a *single* X11 window, but I have been unable to deliver the
events to *multiple* window applications (e.g. xcalc or xwebster).
	
	Could someone please help me see either 1) the futility of my
aspirations or preferably 2) the flaw in my approach and the required
solution.

	
	Here is the code I am using to select the application window:


/* NOTE: Select_Window() is taken, as is, from the file `dsimple.c'
 * 	 found with the standard application `xwd'.
 */

Window
SelectWindowFromUser(display)

Display *display;
 
{
    Window      target = Select_Window(display);
 
    if( target != None )
    {
        Window          root;
        int             result;
        int             idummy;
        unsigned int    udummy;
         
        result = XGetGeometry(display,
                              target,
                              &root,
                              &idummy, &idummy,
                              &udummy, &udummy,
                              &udummy, &udummy);
 
        /* Look for the true `client' window */
        if( result && target != root)
            target = XmuClientWindow(display, target);

        /* Make sure we didn't get the `root' window lest we render
         * the window manager itself useless by intercepting all events.
         */
        if( target == root )
            target = None;
    }

    return target;
}

	The code fragment that handles the control case is:

    .
    .
    .

    /* Was this event `captured' from the target application? */
    if( Event.xany.window == captureWindow )
    {
        Bool ProcessRemoteXEvent();

	/* Should this event be propogated to the listeners? */
        if (ProcessRemoteXEvent(&Event) )
        {
	    /* If so, deliver the event locally as well */
            XSendEvent(dpy, targetWindow, False, 0L, &Event);
        }
    }    

    /* Otherwise handle the event as usual */
    else
        DispatchEvent();

    .
    .
    .

	And the code to handle the listen case is:

    .
    .
    .

    /* Insert the display for the local host */
    Event.xany.display = dpy;

    XSendEvent(dpy, targetWindow, False, 0L, &Event);
  
    .
    .
    .

	Thank you in advance for any light you can shed on my problem.


					-Thane J. Frivold
					 Software Engineer
					 Geoscience & Engineering Center
		
					 SRI International
					 333 Ravenswood Avenue
					 Menlo Park, CA  94025
					 Mail Stop: EJ329

					 Office: (415) 859-2786
					 FAX:    (415) 325-4812	
					 EMAIL:	 frivold@erg.sri.com  -or-
					 	 frivold@unix.sri.com
					-Thane J. Frivold
					 frivold@erg.sri.com

mouse@LIGHTNING.MCRCIM.MCGILL.EDU (11/08/90)

> I am currently working on an application (a modified `twm' to be
> exact) that allows for a certain amount of `remote control' of X11
> applications.  Once a common application has been determined, a
> controlling user's host replicates the pertinent X11 events to all
> the listening hosts.  All event delivery to the selected application
> is done via calls to XSendEvent().

Note that not all applications listen to events sent with XSendEvent.
(Not that I'm saying this is your problem; see below.)

> The event capture is done by creating an InputOnly `capture window'
> whose parent is the selected application (target) window and whose
> event mask has all bits set, except `PointerMotionHintMask'.

> Currently, the application *does* work for client applications that
> have a *single* X11 window, but I have been unable to deliver the
> events to *multiple* window applications (e.g. xcalc or xwebster).

Well, I think I can perhaps explain the flaw.  Suppose we have an
application whose window structure is

toplevel
    menu
	button 1
	button 2
	button 3
	button 4
    main graphics window

Now, you add your blanket InputOnly window:

toplevel
    blanket
    menu
	button 1
	button 2
	button 3
	button 4
    main graphics window

This is perhaps not a great idea.  Why?

- This breaks if the application has set different pointer cursors on
  its various windows, because the pointer cursor will always be that
  of your blanket window.

- The application may raise children of its main window with
  XRaiseWindow or equivalent; this will unexpectedly put them in front
  of your blanket.

- If the application grabs the pointer, you lose your control.

Similar problems exist for keyboard events, but let's not get into that.

But let's ignore possible problems like those for the moment.

Now, suppose the user puts the cursor over "button 1" and clicks a
button.  If it weren't for your blanket, the application would see a
ButtonPress event generated on the "button 1" window.  (Depending on
the event masks, it might be received through the "menu" or "toplevel"
windows, but the "event window" will be the "button 1" window.)  But
your blanket window is present, so the event window of the generated
event is "blanket".  Your program receives the event and sends it to
"toplevel" with XSendEvent.  If the application ignores ButtonPress
events on "toplevel", expecting to receive them through "button 1" or
"menu" instead, it won't see it at all.  If it does see it, it will
have no way of telling that the click was "over" button 1; instead, the
event window will be a window it's never heard of and has no idea how
to deal with.  It will probably drop the event on the floor.

Either way, it doesn't work right.

In order to do this right, you will need to duplicate the whole window
tree in a shadow tree of InputOnly windows, select for ConfigureNotify
and StructureNotify (or perhaps SubstructureNotify) on the
application's windows, and keep the window trees in sync (and there
will always be race conditions).

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

exl@proteon.COM (Eugene Lumelsky) (11/09/90)

This message is empty.

vp01@bunny.gte.com (Vincent Phuah) (05/08/91)

Keywords:XSendEvent, PointerWindow


Hi there,

Wonder if someone could help me. I'm trying to simulate
button press/release events using XSendEvent with the
PointerWindow option, but having a lot of trouble getting
it to work. Would appreciate 
if you could take a look at my code and tell me what I'm doing
wrong. Instead of using the PointerWindow option,
I tried using  the  window id of a command button
in the event structure and the XSendEvent routine,
but that didn't work either.

Thanks for your help in advance.

-----
/*     
 *
 *      Code to send an button press/release event to
 *      a window(likely a command button window)
        specified by the pointer.
 */


#include  "xbook.h"

int SendButtonPress();

SendButtonPress( display, window, state)

Display *display;
Window  window;
int     state; /* unused */

{       
        XButtonEvent       event;
        int                status;

        event.type        = ButtonPress;
        event.display     = display;
        event.root        = RootWindow( display, DefaultScreen( display ));
        event.same_screen = True;
        event.x           = 0;
        event.y           = 0;   
        event.x_root      = 0;   
        event.y_root      = 0;   
        event.subwindow   = (Window)None;
        event.time         = CurrentTime;
        event.state       = 0x00; 


       status = XSendEvent( display, PointerWindow, False ,
ButtonPressMask, &event );



        sleep(1);
        if ( status != 0 )
                {
                event.type     = ButtonRelease;
                event.state    = Button1Mask; 
                event.time     = CurrentTime;

                status = XSendEvent( display, PointerWindow, False, 
                                ButtonReleaseMask, &event );
                }

        XFlush(display);
        return( status );


}       

main(argc,argv)
int argc;
char **argv;

{
   Display *display;
   
   if (argc != 2)
   {
      printf("Must specify display name.\n");
      exit(0);
   }

   if ((display = XOpenDisplay(argv[1])) == NULL)
   {
      printf("Cannot  open display\n");
      exit(-1);
   }
   if (SendButtonPress(display, PointerWindow, 0x0100) > 0)
        printf("Send Button Event ok\n");
   else printf("SentEvent Not OK\n");

   exit(0);
}      


--

+-+-+-+-+-+-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*    Vincent Phuah                                       *
*    GTE Laboratories              Email: vphuah@gte.com *
*    40 Sylvan Road                Tel:   (617) 466-4130 *
*    Waltham, MA 02254             FAX:   (617) 890-9320 *
+-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+