[comp.windows.x] MOUSE and other INPUT sharing MULTIPLE DISPLAY

fcolin@cenaath.cena.dgac.fr (Francois-Regis COLIN) (03/14/91)

here is a simple program to make access to an adjacent screen
with the mouse. The purpose of this program is to make a reconfigurable
working position: we need to have 3 screens manipulated by a various
number of operator form 1 to 3 . so we need 3 displays NOT 3 screens
because of the mouse. The program is not yiet finished but it begins to work
with a mouse on any window allowing sendevent as normal event.
problems :
 - major difficulties is to build the event correctly ( depending on the grag
of the other screens ). we need to redo the job of the adjacent server .
 - response time may be slow !
 - we need to establish a priority for accepting other mouse inputs 
 
questions :
 - should I continue to work on this program using X11R4 ?
 - should I wait for better support of Xinput ?
 - in fact should I wait for X11R5 ?
 - will R5 offer me better solution for my problem ?

--------------- cut here (:->) --------------------
/*
 * Sharing mouse pointer to adjacent screen.
 * Purpose : controlling adjacent display not screen with a same mouse.
 * 		we need different display because there are two different operators 
 * 		in front of two displays
 * Author : fcolin@cenaath.cena.dgac.fr 
 * 
 */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>

#include <stdio.h>

static Display *display = NULL;		/* display on wich is the mouse */
static Display *otherDisplay = NULL;  	/* display on wich is the pointeur */
static Display *leftDisplay = NULL;  	/* display on left of display */
static Display *rightDisplay = NULL;  	/* display on right of display */

static Window otherRootWindow = 0;
static Window leftRootWindow = 0;
static Window rightRootWindow = 0;

static int savedAccel_numerator, savedAccel_denominator, savedThreshold;
#define NB_MAPPING 4
static int savedNbMapping;
static unsigned char savedMapping[NB_MAPPING];
static unsigned char zeroMapping[NB_MAPPING] = { 0,0,0,0};

static Pixmap NoCursorPixmap;
static char NoCursor_bits[] = {
   0x00};
static	XColor color = { 0,0,0,0,0,0};

static unsigned int display_width, display_height;
static int screen;

/* external for otion parsing */
extern char *optarg;
extern int optind, opterr;
extern int getopt(int argc, char **argv, char *optstring);

/*
 * Activate the mouse by settings accelerator and mapping to saved one
 */
static activate_mouse( Display *other )
{
XChangePointerControl( otherDisplay, True, True, savedAccel_numerator, savedAccel_denominator, savedThreshold);
XSetPointerMapping( otherDisplay, savedMapping, savedNbMapping );
}
/*
 * Deactivate the mouse by settings accelerator and mapping to zero
 */
static deactivate_mouse( Display *other )
{
XChangePointerControl( other, True, True, 0, 2, 0);
XSetPointerMapping( other, zeroMapping, savedNbMapping );
}
/*
 * Send Event to the specified window recalculating the new event
 */
static SimulateMouseEvent( Window destWindow,  XEvent *event )
{
	/* recalculate the new coordinates relatives to the new display */
if ( !XTranslateCoordinates(	otherDisplay,
					otherRootWindow,
					event->xbutton.window,
					event->xbutton.x_root,
					event->xbutton.y_root,
					&event->xbutton.x,
					&event->xbutton.y,
					&event->xbutton.subwindow
					)) printf("error Translate Coordinates\n");
					
	/* update the event with new data */
event->xbutton.display = otherDisplay;
event->xbutton.root = otherRootWindow;
event->xbutton.serial = LastKnownRequestProcessed( otherDisplay );

if ( !XSendEvent( otherDisplay, destWindow, True, NULL, event ))
			printf("error sendind event\n");
}

void main( int argc, char **argv )
{
	Window winLeft,winRight,simulatedgrabWindow;
	char *window_name = "CursorSharing";
	XEvent report;
	int root_x, root_y,new_x;
	Window grabbingWindow;
	XSetWindowAttributes attributs;
	Cursor NoCursor;
	Bool pointerGrabbed = False;
	int c,i,grab;
	unsigned int dummy;
	
	if ( argc < 2 ) {
			(void)fprintf(stderr, "usage: %s -l leftDisplay -r rightDisplay\n",argv[0]);
                  exit (-2);
               }

	/* connect to X server */

	if ( (display=XOpenDisplay(NULL)) == NULL )
	{
		(void) fprintf( stderr, "cannot connect to local X server\n");
		exit( -1 );
	}
	while ((c = getopt(argc, argv, "l:r:--")) != -1)
		switch (c) {
			case 'l':
				if ( (leftDisplay=XOpenDisplay(optarg)) == NULL )
					{
					(void) fprintf( stderr, "cannot connect to X server %s\n", XDisplayName(optarg));
					exit( -1 );
					}
				leftRootWindow = RootWindow(leftDisplay,DefaultScreen(leftDisplay));
				break;
			case 'r':
				if ( (rightDisplay=XOpenDisplay(optarg)) == NULL )
					{
					(void) fprintf( stderr, "cannot connect to X server %s\n", XDisplayName(optarg));
					exit( -1 );
					}
				rightRootWindow = RootWindow(rightDisplay,DefaultScreen(rightDisplay));
				break;
			case '?':
				(void)fprintf(stderr, "usage: %s -l leftDisplay -r rightDisplay\n",argv[0]);
                    	exit (-2);
               }
	
	/* get screen size from display structure macro all screens a supposed to be identical */
	
	screen = DefaultScreen(display);
	display_width = DisplayWidth(display, screen);
	display_height = DisplayHeight(display, screen);

	/* create the invisible cursor to use when the pointer is on an other screen */
	
	NoCursorPixmap = XCreateBitmapFromData( display,RootWindow(display,screen),
		NoCursor_bits, 1,1);
	NoCursor = XCreatePixmapCursor( display, NoCursorPixmap, NoCursorPixmap,
		&color,&color, 0,0);

	/* create small 1 pixel input only window width on the left edge of the screen */
	
	attributs.override_redirect = True;
	winLeft = XCreateWindow(display, RootWindow(display,screen), 0, 0, 1, display_height,0,0,
			InputOnly,CopyFromParent,
			CWOverrideRedirect, &attributs);
			
	/* create small 1 pixel input only window width on the right edge of the screen */
	
	attributs.override_redirect = True;
	winRight = XCreateWindow(display, RootWindow(display,screen), display_width - 1, 0, 1, display_height,0,0,
			InputOnly,CopyFromParent,
			CWOverrideRedirect, &attributs);

	/* Select event types wanted */
	
	XSelectInput(display, winRight, EnterWindowMask );
	XSelectInput(display, winLeft, EnterWindowMask );

	/* Display window */
	XMapWindow(display, winLeft);
	XMapWindow(display, winRight);
	
	pointerGrabbed = False;
	simulatedgrabWindow = -1;
	/* save the mouse control and mapping supposed identical on each display */
	XGetPointerControl( display,
					&savedAccel_numerator,
					&savedAccel_denominator,
					&savedThreshold);

	savedNbMapping = XGetPointerMapping( display, savedMapping,NB_MAPPING);

	while (1)  {
		if ( otherDisplay != NULL ) XFlush( otherDisplay );
		XNextEvent(display, &report);
		switch  (report.type) {
			
			/* when pointer is grabbed we simulate the mouse mouvement on the other display */
			
		case MotionNotify:
			if ( pointerGrabbed ) {
				XWarpPointer(otherDisplay,
						None,
						otherRootWindow,
			 			0,0,
			 			display_width,display_height,
			 			report.xmotion.x_root, report.xmotion.y_root );

			 /* send motion report if simulated grab is active */
			 
			if ( simulatedgrabWindow != -1) {
				report.xmotion.window = simulatedgrabWindow;
				SimulateMouseEvent ( simulatedgrabWindow, &report );
				}
			}
			break;
			
			/* check crossing edge of the screen */
			
		case EnterNotify:
			if (report.xcrossing.window == grabbingWindow ) {
				/* discard EnterNotify generated by WrapPointer */
				grabbingWindow = 0;
				break;
				}
			if ( !pointerGrabbed ) {
				/* we are leaving our screen determine which display to use */
				if (report.xcrossing.window == winLeft) {
					if ( leftDisplay == NULL ) break;
					otherDisplay = leftDisplay;
					otherRootWindow = leftRootWindow;
					grabbingWindow = 	winRight;	/* grabbing opposite window to detect croosing back to out screen */
					}
					else {
					if ( rightDisplay == NULL ) break;
					otherDisplay = rightDisplay;
					otherRootWindow = rightRootWindow;
					grabbingWindow = 	winLeft;	/* grabbing opposite window to detect croosing back to out screen */
					}
				/* grabbing our pointer to get all the futher events */
				grab = XGrabPointer( display, grabbingWindow, False, 
					PointerMotionMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask,
					GrabModeAsync, 
					GrabModeAsync,
					None,
					NoCursor,
					CurrentTime );
				if ( grab != GrabSuccess )
					printf("grab result %d \n",grab);
					else {
						pointerGrabbed = True;
						/* freeze the other mouse
						 * we can't grab this mouse because
						 *  we want normal event to take place
						 */
						 deactivate_mouse( otherDisplay );
					}
				}
				else {
				/* we are getting back to our display */
				XUngrabPointer( display, CurrentTime );
				pointerGrabbed = False;
				/* unfreeze the other mouse  */
				activate_mouse( otherDisplay );
				}
			/* Warp the pointer to the opposite edge of our screen 
			 * so we got good motion event not limited to the edge
			 */
			new_x = abs((display_width - report.xcrossing.x_root) -2);
			XWarpPointer( display, None, RootWindow(display,DefaultScreen(display)), 0,0, 
				display_width,display_height, new_x, report.xcrossing.y_root );
			break;
		case ButtonPress:
			if ( pointerGrabbed ) {
				report.xbutton.subwindow = otherRootWindow;
				/* lookup for the window under the cursor */
				do {
					report.xbutton.window = report.xbutton.subwindow;
					printf("search grab %lx\n",report.xbutton.window);
					if ( !XTranslateCoordinates(	otherDisplay,
							otherRootWindow,
							report.xbutton.window,
							report.xbutton.x_root,
							report.xbutton.y_root,
							&report.xbutton.x,
							&report.xbutton.y,
							&report.xbutton.subwindow
						)) printf("error Translate Coordinates\n");
					
				} while ( report.xbutton.subwindow != None );
				if ( report.xbutton.window == otherRootWindow )
					simulatedgrabWindow = PointerWindow;
					else simulatedgrabWindow = report.xbutton.window;
				printf("simul grab %lx\n",simulatedgrabWindow);
				SimulateMouseEvent( simulatedgrabWindow, &report );
				}
			break;
		case ButtonRelease :
			if ( pointerGrabbed ) {
				report.xbutton.window = simulatedgrabWindow;
				SimulateMouseEvent( simulatedgrabWindow, &report );
				simulatedgrabWindow = -1;
				}
			break;
		} /* end switch */
	} /* end while */
}

--------------- cut here (:-<) --------------------