/* * x2x: Uses the XTEST extension to forward keystrokes from a window on * one display to another display. Useful for desks * with multiple keyboards. * * Copyright (c) 1997 * Digital Equipment Corporation. All rights reserved. * * By downloading, installing, using, modifying or distributing this * software, you agree to the following: * * 1. CONDITIONS. Subject to the following conditions, you may download, * install, use, modify and distribute this software in source and binary * forms: * * a) Any source code, binary code and associated documentation * (including the online manual) used, modified or distributed must * reproduce and retain the above copyright notice, this list of * conditions and the following disclaimer. * * b) No right is granted to use any trade name, trademark or logo of * Digital Equipment Corporation. Neither the "Digital Equipment * Corporation" name nor any trademark or logo of Digital Equipment * Corporation may be used to endorse or promote products derived from * this software without the prior written permission of Digital * Equipment Corporation. * * 2. DISCLAIMER. THIS SOFTWARE IS PROVIDED BY DIGITAL "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Cygwin version with -fromwin to allow source to be a Windows * machine that is not running a X server. * Adapted by Mark Hayter 2003 using win2vnc ClientConnect.cpp code * * Original win2vnc copyright follows: */ // win2vnc, adapted from vncviewer by Fredrik Hubinette 2001 // // Original copyright follows: // // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. // // This file is part of the VNC system. // // The VNC system is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // If the source code for the VNC system is not available from the place // whence you received this file, check http://www.uk.research.att.com/vnc or contact // the authors on vnc@uk.research.att.com for information on obtaining it. #include #include #include #include #include #include #include #include /* for selection */ #include #include #include #include "format.h" extern void fake_motion(int x,int y); extern void fake_button_event(int b,int ud); extern void fake_key_event(int k,int ud); extern void fake_init(Display *d); /*#define DEBUG*/ /********** * definitions for edge **********/ #define EDGE_NONE 0 /* don't transfer between edges of screens */ #define EDGE_EAST 1 /* from display is on the east side of to display */ #define EDGE_WEST 2 /* from display is on the west side of to display */ /********** * functions **********/ static void ParseCommandLine(); static Display *OpenAndCheckDisplay(); static Bool CheckTestExtension(); static void DoX2X(); static void InitDpyInfo(); static void DoConnect(); static void DoDisconnect(); static void RegisterEventHandlers(); static Bool ProcessEvent(); static Bool ProcessMotionNotify(); static Bool ProcessExpose(); static Bool ProcessEnterNotify(); static Bool ProcessButtonPress(); static Bool ProcessButtonRelease(); static Bool ProcessKeyEvent(); static Bool ProcessConfigureNotify(); static Bool ProcessClientMessage(); static Bool ProcessPropertyNotify(); static Bool ProcessVisibility(); static Bool ProcessMapping(); static void FakeThingsUp(); static void FakeAction(); static void RefreshPointerMapping(); static void Usage(); /********** * text formatting instructions **********/ #define toDpyFormatLength (sizeof(toDpyFormat) / sizeof(Format)) static Format toDpyFormat[] = { FormatMeasureText, FormatSetLeft, 0, FormatSetTop, 0, FormatAddHalfTextX, 1, FormatAddHalfTextY, 3, FormatString, (Format)"unknown", FormatAddHalfTextX, 1, FormatAddHalfTextY, 1 }; /* indexes of values to be filled in at runtime */ #define toDpyLeftIndex 2 #define toDpyTopIndex 4 #define toDpyStringIndex 10 /********** * stuff for selection forwarding **********/ typedef struct _dpyxtra { Display *otherDpy; int sState; Atom pingAtom; Bool pingInProg; Window propWin; } DPYXTRA, *PDPYXTRA; /********** * structures for recording state of buttons and keys **********/ typedef struct _fakestr { struct _fakestr *pNext; int type; unsigned int thing; } FAKE, *PFAKE; #define FAKE_KEY 0 #define FAKE_BUTTON 1 #define N_BUTTONS 5 #define GETDPYXTRA(DPY,PDPYINFO)\ (((DPY) == (PDPYINFO)->fromDpy) ?\ &((PDPYINFO)->fromDpyXtra) : &((PDPYINFO)->toDpyXtra)) /* values for sState */ #define SELSTATE_ON 0 #define SELSTATE_OFF 1 #define SELSTATE_WAIT 2 /* special values for translated coordinates */ #define COORD_INCR -1 #define COORD_DECR -2 #define SPECIAL_COORD(COORD) (((COORD) < 0) ? (COORD) : 0) /* max unreasonable coordinates before accepting it */ #define MAX_UNREASONABLES 10 /********** * display information **********/ typedef struct { /* stuff on "from" display */ Display *fromDpy; Window root; Window trigger; Window big; GC textGC; Atom wmpAtom, wmdwAtom; Cursor grabCursor; XFS *font; int twidth, theight; int lastFromX; int unreasonableDelta; /* stuff on "to" display */ Window selWin; unsigned int inverseMap[N_BUTTONS + 1]; /* inverse of button mapping */ /* state of connection */ int mode; /* connection */ int eventMask; /* trigger */ /* coordinate conversion stuff */ int toScreen; int nScreens; short **xTables; /* precalculated conversion tables */ short **yTables; int fromXConn, fromXDisc; /* location of cursor after conn/disc ops */ int fromXIncr, fromXDecr; /* location of cursor after incr/decr ops */ /* selection forwarding info */ DPYXTRA fromDpyXtra; DPYXTRA toDpyXtra; Display *sDpy; Time sTime; /* for recording state of buttons and keys */ PFAKE pFakeThings; } DPYINFO, *PDPYINFO; /* shadow displays */ typedef struct _shadow { struct _shadow *pNext; char *name; } SHADOW, *PSHADOW; /* sticky keys */ typedef struct _sticky { struct _sticky *pNext; KeySym keysym; } STICKY, *PSTICKY; typedef int (*HANDLER)(); /* event handler function */ /********** * top-level variables **********/ static char *programStr = "x2x"; static char *fromDpyName = NULL; static char *toDpyName = NULL; static char *defaultFN = "-*-times-bold-r-*-*-*-180-*-*-*-*-*-*"; static char *fontName = "-*-times-bold-r-*-*-*-180-*-*-*-*-*-*"; static char *pingStr = "PING"; /* atom for ping request */ static char *geomStr = NULL; static Bool waitDpy = False; static Bool doBig = False; static Bool doMouse = True; static int doEdge = EDGE_NONE; static Bool doAutoUp = True; static Bool doResurface = False; static PSHADOW shadows = NULL; static int triggerw = 2; static Bool doPointerMap = True; static PSTICKY stickies = NULL; static Bool doBtnBlock = False; static Bool doCapsLkHack = False; static Bool doClipCheck = False; /********** * main **********/ int main(argc, argv) int argc; char **argv; { Display *fromDpy; PSHADOW pShadow; XrmInitialize(); ParseCommandLine(argc, argv); fromDpyName = XDisplayName(fromDpyName); #if 0 toDpyName = XDisplayName(toDpyName); if (!strcasecmp(toDpyName, fromDpyName)) { fprintf(stderr, "%s: display names are both %s\n", programStr, toDpyName); exit(1); } #endif /* no OS independent wat to stop Xlib from complaining via stderr, but can always pipe stdout/stderr to /dev/null */ /* convert to real name: */ while ((fromDpy = XOpenDisplay(fromDpyName)) == NULL) { if (!waitDpy) { fprintf(stderr, "%s - error: can not open display %s\n", programStr, fromDpyName); exit(2); } /* END if */ sleep(10); } /* END while fromDpy */ /* toDpy is always the first shadow */ pShadow = (PSHADOW)malloc(sizeof(SHADOW)); pShadow->name = toDpyName; /* link into the global list */ pShadow->pNext = shadows; shadows = pShadow; #if 0 /* initialize all of the shadows, including the toDpy */ for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) if (!(pShadow->dpy = OpenAndCheckDisplay(pShadow->name))) exit(3); #endif fake_init(fromDpy); /* run the x2x loop */ DoX2X(fromDpy ); /* shut down gracefully */ XCloseDisplay(fromDpy); #if 0 for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) XCloseDisplay(pShadow->dpy); #endif exit(0); } /* END main */ #if 0 static Display *OpenAndCheckDisplay(name) char *name; { Display *openDpy; /* convert to real name: */ name = XDisplayName(name); while ((openDpy = XOpenDisplay(name)) == NULL) { if (!waitDpy) { fprintf(stderr, "%s - error: can not open display %s\n", programStr, name); return NULL; } /* END if */ sleep(10); } /* END while openDpy */ if (!CheckTestExtension(openDpy)) { fprintf(stderr, "%s - error: display %s does not support the test extension\n", programStr, name); return NULL; } return (openDpy); } /* END OpenAndCheckDisplay */ #endif /********** * use standard X functions to parse the command line **********/ static void ParseCommandLine(argc, argv) int argc; char **argv; { int arg; PSHADOW pShadow; extern char *lawyerese; PSTICKY pNewSticky; KeySym keysym; #ifdef DEBUG printf ("programStr = %s\n", programStr); #endif for (arg = 1; arg < argc; ++arg) { if (!strcasecmp(argv[arg], "-from")) { if (++arg >= argc) Usage(); fromDpyName = argv[arg]; #ifdef DEBUG printf ("fromDpyName = %s\n", fromDpyName); #endif } else if (!strcasecmp(argv[arg], "-to")) { if (++arg >= argc) Usage(); toDpyName = argv[arg]; #ifdef DEBUG printf ("toDpyName = %s\n", toDpyName); #endif } else if (!strcasecmp(argv[arg], "-font")) { if (++arg >= argc) Usage(); fontName = argv[arg]; #ifdef DEBUG printf ("fontName = %s\n", fontName); #endif } else if (!strcasecmp(argv[arg], "-geometry")) { if (++arg >= argc) Usage(); geomStr = argv[arg]; #ifdef DEBUG printf ("geometry = %s\n", geomStr); #endif } else if (!strcasecmp(argv[arg], "-wait")) { waitDpy = True; #ifdef DEBUG printf("will wait for displays\n"); #endif } else if (!strcasecmp(argv[arg], "-big")) { doBig = True; #ifdef DEBUG printf("will create big window on from display\n"); #endif } else if (!strcasecmp(argv[arg], "-nomouse")) { doMouse = False; #ifdef DEBUG printf("will not capture mouse (eek!)\n"); #endif } else if (!strcasecmp(argv[arg], "-nopointermap")) { doPointerMap = False; #ifdef DEBUG printf("will not do pointer mapping\n"); #endif } else if (!strcasecmp(argv[arg], "-east")) { doEdge = EDGE_EAST; #ifdef DEBUG printf("\"from\" is on the east side of \"to\"\n"); #endif } else if (!strcasecmp(argv[arg], "-west")) { doEdge = EDGE_WEST; #ifdef DEBUG printf("\"from\" is on the west side of \"to\"\n"); #endif } else if (!strcasecmp(argv[arg], "-noautoup")) { doAutoUp = False; #ifdef DEBUG printf("will not automatically lift keys and buttons\n"); #endif } else if (!strcasecmp(argv[arg], "-buttonblock")) { doBtnBlock = True; #ifdef DEBUG printf("mouse buttons down will block disconnects\n"); #endif } else if (!strcasecmp(argv[arg], "-capslockhack")) { doCapsLkHack = True; #ifdef DEBUG printf("behavior of CapsLock will be hacked\n"); #endif } else if (!strcasecmp(argv[arg], "-clipcheck")) { doClipCheck = True; #ifdef DEBUG printf("Clipboard type will be checked for XA_STRING\n"); #endif } else if (!strcasecmp(argv[arg], "-nocapslockhack")) { doCapsLkHack = False; #ifdef DEBUG printf("behavior of CapsLock will not be hacked\n"); #endif } else if (!strcasecmp(argv[arg], "-sticky")) { if (++arg >= argc) Usage(); if ((keysym = XStringToKeysym(argv[arg])) != NoSymbol) { pNewSticky = (PSTICKY)malloc(sizeof(STICKY)); pNewSticky->pNext = stickies; pNewSticky->keysym = keysym; stickies = pNewSticky; #ifdef DEBUG printf("will press/release sticky key: %s\n", argv[arg]); #endif } else { printf("x2x: warning: can't translate %s\n", argv[arg]); } } else if (!strcasecmp(argv[arg], "-resurface")) { doResurface = True; #ifdef DEBUG printf("will resurface the trigger window when obscured\n"); #endif } else if (!strcasecmp(argv[arg], "-shadow")) { if (++arg >= argc) Usage(); pShadow = (PSHADOW)malloc(sizeof(SHADOW)); pShadow->name = argv[arg]; /* into the global list of shadows */ pShadow->pNext = shadows; shadows = pShadow; } else if (!strcasecmp(argv[arg], "-triggerw")) { if (++arg >= argc) Usage(); triggerw = atoi(argv[arg]); } else if (!strcasecmp(argv[arg], "-copyright")) { printf(lawyerese); } else { Usage(); } /* END if... */ } /* END for */ } /* END ParseCommandLine */ static void Usage() { printf("Usage: x2x [-to | -from ] options...\n"); printf(" -copyright\n"); printf(" -font \n"); printf(" -geometry \n"); printf(" -wait\n"); printf(" -big\n"); printf(" -buttonblock\n"); printf(" -nomouse\n"); printf(" -east\n"); printf(" -west\n"); printf(" -nosel\n"); printf(" -noautoup\n"); printf(" -resurface\n"); printf(" -capslockhack\n"); printf(" -nocapslockhack\n"); printf(" -clipcheck\n"); printf(" -shadow \n"); printf(" -sticky \n"); exit(4); } /* END Usage */ /********** * call the library to check for the test extension **********/ #if 0 static Bool CheckTestExtension(dpy) Display *dpy; { int eventb, errorb; int vmajor, vminor; return (XTestQueryExtension(dpy, &eventb, &errorb, &vmajor, &vminor)); } /* END CheckTestExtension */ #endif #define X2X_DISCONNECTED 0 #define X2X_AWAIT_RELEASE 1 #define X2X_CONNECTED 2 #define X2X_CONN_RELEASE 3 static void DoX2X(fromDpy ) Display *fromDpy; { DPYINFO dpyInfo; int nfds; fd_set fdset; Bool fromPending; int fromConn, toConn; /* set up displays */ dpyInfo.fromDpy = fromDpy; InitDpyInfo(&dpyInfo); RegisterEventHandlers(&dpyInfo); /* set up for select */ fromConn = XConnectionNumber(fromDpy); #if 0 toConn = XConnectionNumber(toDpy); nfds = (fromConn > toConn ? fromConn : toConn) + 1; #else nfds=fromConn+1; #endif while (True) { /* FOREVER */ if ((fromPending = XPending(fromDpy))) if (ProcessEvent(fromDpy, &dpyInfo)) /* done! */ break; FD_ZERO(&fdset); FD_SET(fromConn, &fdset); #if 0 FD_SET(toConn, &fdset); #endif if (!XPending(fromDpy)) select(nfds, &fdset, NULL, NULL, NULL); } /* END FOREVER */ } /* END DoX2X() */ static void InitDpyInfo(pDpyInfo) PDPYINFO pDpyInfo; { Display *fromDpy, *toDpy; Screen *fromScreen; long black, white; int fromHeight, fromWidth, toHeight, toWidth; Pixmap nullPixmap; XColor dummyColor; Window root, trigger, big, rret, toRoot, propWin; short *xTable, *yTable; /* short: what about dimensions > 2^15? */ int *heights, *widths; int counter; int nScreens, screenNum; int twidth, theight; /* text dimensions */ int xoff, yoff; /* window offsets */ unsigned int width, height; /* window width, height */ int geomMask; /* mask returned by parse */ int gravMask; int gravity; int xret, yret, wret, hret, bret, dret; XSetWindowAttributes xswa; XSizeHints *xsh; int eventMask; GC textGC; char *windowName; XFS *font; PSHADOW pShadow; int triggerLoc; /* cache commonly used variables */ fromDpy = pDpyInfo->fromDpy; fromScreen = XDefaultScreenOfDisplay(fromDpy); black = XBlackPixelOfScreen(fromScreen); white = XWhitePixelOfScreen(fromScreen); fromHeight = XHeightOfScreen(fromScreen); fromWidth = XWidthOfScreen(fromScreen); /* values also in dpyinfo */ root = pDpyInfo->root = XDefaultRootWindow(fromDpy); nScreens = pDpyInfo->nScreens = 1; /* other dpyinfo values */ pDpyInfo->mode = X2X_DISCONNECTED; pDpyInfo->unreasonableDelta = fromWidth / 2; pDpyInfo->pFakeThings = NULL; /* window init structures */ xswa.override_redirect = True; xsh = XAllocSizeHints(); eventMask = KeyPressMask | KeyReleaseMask; /* cursor locations for moving between screens */ pDpyInfo->fromXIncr = triggerw; pDpyInfo->fromXDecr = fromWidth - triggerw - 1; if (doEdge) { /* edge triggers x2x */ nullPixmap = XCreatePixmap(fromDpy, root, 1, 1, 1); eventMask |= EnterWindowMask; pDpyInfo->grabCursor = XCreatePixmapCursor(fromDpy, nullPixmap, nullPixmap, &dummyColor, &dummyColor, 0, 0); if (doEdge == EDGE_EAST) { /* trigger window location */ triggerLoc = fromWidth - triggerw; toHeight = 2048; pDpyInfo->fromXConn = 1; pDpyInfo->fromXDisc = fromWidth - triggerw - 1; } else { /* trigger window location */ triggerLoc = 0; toHeight = 2048; toWidth = 2048; pDpyInfo->fromXConn = fromWidth - triggerw - 1; pDpyInfo->fromXDisc = triggerw; } /* END if doEdge == ... */ xswa.background_pixel = black; fprintf(stderr,"x=%d y=%d w=%d h=%d\n",triggerLoc,0,triggerw,fromHeight); /* fromWidth - 1 doesn't seem to work for some reason */ trigger = pDpyInfo->trigger = XCreateWindow(fromDpy, root, triggerLoc, 0, triggerw, fromHeight, 0, 0, InputOutput, 0, CWBackPixel | CWOverrideRedirect, &xswa); font = NULL; } else { /* normal window for text: do size grovelling */ pDpyInfo->grabCursor = XCreateFontCursor(fromDpy, XC_exchange); eventMask |= StructureNotifyMask | ExposureMask; if (doMouse) eventMask |= ButtonPressMask | ButtonReleaseMask; /* determine size of text */ if (((font = XLoadQueryFont(fromDpy, fontName)) != NULL) || ((font = XLoadQueryFont(fromDpy, defaultFN)) != NULL) || ((font = XLoadQueryFont(fromDpy, "fixed")) != NULL)) { /* have a font */ toDpyFormat[toDpyStringIndex] = (Format)toDpyName; formatText(NULL, NULL, NULL, font, toDpyFormatLength, toDpyFormat, &twidth, &theight); textGC = pDpyInfo->textGC = XCreateGC(fromDpy, root, 0, NULL); XSetState(fromDpy, textGC, black, white, GXcopy, AllPlanes); XSetFont(fromDpy, textGC, font->fid); } else { /* should not have to execute this clause: */ twidth = theight = 100; /* default window size */ } /* END if have a font ... else ... */ /* determine size of window */ xoff = yoff = 0; width = twidth; height = theight; geomMask = XParseGeometry(geomStr, &xoff, &yoff, &width, &height); switch (gravMask = (geomMask & (XNegative | YNegative))) { case (XNegative | YNegative): gravity = SouthEastGravity; break; case XNegative: gravity = NorthEastGravity; break; case YNegative: gravity = SouthWestGravity; break; default: gravity = NorthWestGravity; break; } if (gravMask) { XGetGeometry(fromDpy, root, &rret, &xret, &yret, &wret, &hret, &bret, &dret); if ((geomMask & (XValue | XNegative)) == (XValue | XNegative)){ xoff = wret - width + xoff; } if ((geomMask & (YValue | YNegative)) == (YValue | YNegative)) { yoff = hret - height + yoff; } } /* END if geomMask */ trigger = pDpyInfo->trigger = XCreateSimpleWindow(fromDpy, root, xoff, yoff, width, height, 0, black, white); } /* END if doEdge ... else ...*/ /* size hints stuff: */ xsh->x = xoff; xsh->y = yoff; xsh->base_width = width; xsh->base_height = height; xsh->win_gravity = gravity; xsh->flags = (PPosition|PBaseSize|PWinGravity); XSetWMNormalHints(fromDpy, trigger, xsh); windowName = (char *)malloc(strlen(programStr) + strlen(toDpyName) + 2); strcpy(windowName, programStr); strcat(windowName, " "); strcat(windowName, toDpyName); XStoreName(fromDpy, trigger, windowName); XSetIconName(fromDpy, trigger, windowName); /* register for WM_DELETE_WINDOW protocol */ pDpyInfo->wmpAtom = XInternAtom(fromDpy, "WM_PROTOCOLS", True); pDpyInfo->wmdwAtom = XInternAtom(fromDpy, "WM_DELETE_WINDOW", True); XSetWMProtocols(fromDpy, trigger, &(pDpyInfo->wmdwAtom), 1); /* mdh - Put in Chaiken's change to make this InputOnly */ if (doBig) { big = pDpyInfo->big = XCreateWindow(fromDpy, root, 0, 0, fromWidth, fromHeight, 0, 0, InputOnly, 0, CWOverrideRedirect, &xswa); /* size hints stuff: */ xsh->x = 0; xsh->y = 0; xsh->base_width = fromWidth; xsh->base_height = fromHeight; xsh->min_width = fromWidth; xsh->min_height = fromHeight; xsh->flags = (PMinSize|PPosition|PBaseSize); XSetWMNormalHints(fromDpy, big, xsh); XStoreName(fromDpy, big, windowName); XSetIconName(fromDpy, big, windowName); } else { pDpyInfo->big = None; } XFree((char *) xsh); free(windowName); /* conversion stuff */ pDpyInfo->toScreen = (doEdge == EDGE_WEST) ? (nScreens - 1) : 0; /* construct table lookup for screen coordinate conversion */ pDpyInfo->xTables = (short **)malloc(sizeof(short *) * nScreens); pDpyInfo->yTables = (short **)malloc(sizeof(short *) * nScreens); heights = (int *)malloc(sizeof(int *) * nScreens); widths = (int *)malloc(sizeof(int *) * nScreens); for (screenNum = 0; screenNum < nScreens; ++screenNum) { widths[screenNum] = toWidth = 2048; heights[screenNum] = toHeight = 2048; pDpyInfo->xTables[screenNum] = xTable = (short *)malloc(sizeof(short) * fromWidth); pDpyInfo->yTables[screenNum] = yTable = (short *)malloc(sizeof(short) * fromHeight); /* vertical conversion table */ for (counter = 0; counter < fromHeight; ++counter) yTable[counter] = (counter * toHeight) / fromHeight; /* horizontal conversion table entries */ for (counter = 0; counter < fromWidth; ++counter) xTable[counter] = (counter * toWidth) / fromWidth; /* adjustment for boundaries */ if ((screenNum != 0) || (doEdge == EDGE_EAST)) xTable[0] = COORD_DECR; if (((screenNum + 1) < nScreens) || (doEdge == EDGE_WEST)) { xTable[fromWidth - 1] = COORD_INCR; /* work-around for bug: on at least one tested screen, cursor never moved past fromWidth - 2 */ xTable[fromWidth - 2] = COORD_INCR; } } /* END for screenNum */ free(heights); free(widths); if (doResurface) /* get visibility events */ eventMask |= VisibilityChangeMask; XSelectInput(fromDpy, trigger, eventMask); pDpyInfo->eventMask = eventMask; /* save for future munging */ XMapRaised(fromDpy, trigger); if ((pDpyInfo->font = font)) { /* paint text */ /* position text */ pDpyInfo->twidth = twidth; pDpyInfo->theight = theight; toDpyFormat[toDpyLeftIndex] = MAX(0,((width - twidth) / 2)); toDpyFormat[toDpyTopIndex] = MAX(0,((height - theight) / 2)); formatText(fromDpy, trigger, &(textGC), font, toDpyFormatLength, toDpyFormat, NULL, NULL); } /* END if font */ } /* END InitDpyInfo */ static void DoConnect(pDpyInfo) PDPYINFO pDpyInfo; { Display *fromDpy = pDpyInfo->fromDpy; Window trigger = pDpyInfo->trigger; #ifdef DEBUG printf("connecting\n"); #endif pDpyInfo->mode = X2X_CONNECTED; if (pDpyInfo->big != None) XMapRaised(fromDpy, pDpyInfo->big); XGrabPointer(fromDpy, trigger, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, pDpyInfo->grabCursor, CurrentTime); XGrabKeyboard(fromDpy, trigger, True, GrabModeAsync, GrabModeAsync, CurrentTime); XSelectInput(fromDpy, trigger, pDpyInfo->eventMask | PointerMotionMask); XFlush(fromDpy); } /* END DoConnect */ static void DoDisconnect(pDpyInfo) PDPYINFO pDpyInfo; { Display *fromDpy = pDpyInfo->fromDpy; PDPYXTRA pDpyXtra; #ifdef DEBUG printf("disconnecting\n"); #endif pDpyInfo->mode = X2X_DISCONNECTED; if (pDpyInfo->big != None) XUnmapWindow(fromDpy, pDpyInfo->big); XUngrabKeyboard(fromDpy, CurrentTime); XUngrabPointer(fromDpy, CurrentTime); XSelectInput(fromDpy, pDpyInfo->trigger, pDpyInfo->eventMask); XFlush(fromDpy); /* force normal state on to display: */ if (doAutoUp) FakeThingsUp(pDpyInfo); } /* END DoDisconnect */ static void RegisterEventHandlers(pDpyInfo) PDPYINFO pDpyInfo; { Display *fromDpy = pDpyInfo->fromDpy; Window trigger = pDpyInfo->trigger; Window propWin; #define XSAVECONTEXT(A, B, C, D) XSaveContext(A, B, C, (XPointer)(D)) XSAVECONTEXT(fromDpy, trigger, MotionNotify, ProcessMotionNotify); XSAVECONTEXT(fromDpy, trigger, Expose, ProcessExpose); XSAVECONTEXT(fromDpy, trigger, EnterNotify, ProcessEnterNotify); XSAVECONTEXT(fromDpy, trigger, ButtonPress, ProcessButtonPress); XSAVECONTEXT(fromDpy, trigger, ButtonRelease, ProcessButtonRelease); XSAVECONTEXT(fromDpy, trigger, KeyPress, ProcessKeyEvent); XSAVECONTEXT(fromDpy, trigger, KeyRelease, ProcessKeyEvent); XSAVECONTEXT(fromDpy, trigger, ConfigureNotify, ProcessConfigureNotify); XSAVECONTEXT(fromDpy, trigger, ClientMessage, ProcessClientMessage); XSAVECONTEXT(fromDpy, trigger, ClientMessage, ProcessClientMessage); XSAVECONTEXT(fromDpy, trigger, ClientMessage, ProcessClientMessage); XSAVECONTEXT(fromDpy, None, MappingNotify, ProcessMapping); if (doResurface) XSAVECONTEXT(fromDpy, trigger, VisibilityNotify, ProcessVisibility); } /* END RegisterEventHandlers */ static Bool ProcessEvent(dpy, pDpyInfo) Display *dpy; PDPYINFO pDpyInfo; { XEvent ev; XAnyEvent *pEv = (XAnyEvent *)&ev; HANDLER handler; #define XFINDCONTEXT(A, B, C, D) XFindContext(A, B, C, (XPointer *)(D)) XNextEvent(dpy, &ev); handler = 0; if ((!XFINDCONTEXT(dpy, pEv->window, pEv->type, &handler)) || (!XFINDCONTEXT(dpy, None, pEv->type, &handler))) { /* have handler */ return ((*handler)(dpy, pDpyInfo, &ev)); } else { #ifdef DEBUG printf("no handler for window 0x%x, event type %d\n", (unsigned int)pEv->window, pEv->type); #endif } /* END if/else */ return False; } /* END ProcessEvent */ static Bool ProcessMotionNotify(unused, pDpyInfo, pEv) Display *unused; PDPYINFO pDpyInfo; XMotionEvent *pEv; /* caution: might be pseudo-event!!! */ { /* Note: ProcessMotionNotify is sometimes called from inside x2x to * simulate a motion event. Any new references to pEv fields * must be checked carefully! */ int toScreenNum; PSHADOW pShadow; int toX, fromX, delta; Display *fromDpy; Bool bAbortedDisconnect; /* find the screen */ toScreenNum = pDpyInfo->toScreen; fromX = pEv->x_root; /* check to make sure the cursor is still on the from screen */ if (!(pEv->same_screen)) { toX = (pDpyInfo->lastFromX < fromX) ? COORD_DECR : COORD_INCR; } else { toX = pDpyInfo->xTables[toScreenNum][fromX]; /* sanity check motion: necessary for nondeterminism surrounding warps */ delta = pDpyInfo->lastFromX - fromX; if (delta < 0) delta = -delta; if (delta > pDpyInfo->unreasonableDelta) return False; } if (SPECIAL_COORD(toX) != 0) { /* special coordinate */ bAbortedDisconnect = False; if (toX == COORD_INCR) { if (toScreenNum != (pDpyInfo->nScreens - 1)) { /* next screen */ toScreenNum = ++(pDpyInfo->toScreen); fromX = pDpyInfo->fromXIncr; toX = pDpyInfo->xTables[toScreenNum][fromX]; } else { /* disconnect! */ if (doBtnBlock && (pEv->state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask))) bAbortedDisconnect = True; else { DoDisconnect(pDpyInfo); fromX = pDpyInfo->fromXDisc; } toX = pDpyInfo->xTables[toScreenNum][pDpyInfo->fromXConn]; } } else { /* DECR */ if (toScreenNum != 0) { /* previous screen */ toScreenNum = --(pDpyInfo->toScreen); fromX = pDpyInfo->fromXDecr; toX = pDpyInfo->xTables[toScreenNum][fromX]; } else { /* disconnect! */ if (doBtnBlock && (pEv->state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask))) bAbortedDisconnect = True; else { DoDisconnect(pDpyInfo); fromX = pDpyInfo->fromXDisc; } toX = pDpyInfo->xTables[toScreenNum][pDpyInfo->fromXConn]; } } /* END if toX */ if (!bAbortedDisconnect) { fromDpy = pDpyInfo->fromDpy; XWarpPointer(fromDpy, None, pDpyInfo->root, 0, 0, 0, 0, fromX, pEv->y_root); XFlush(fromDpy); } } /* END if SPECIAL_COORD */ pDpyInfo->lastFromX = fromX; for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) { fake_motion(toX,pDpyInfo->yTables[toScreenNum][pEv->y_root]); } /* END for */ return False; } /* END ProcessMotionNotify */ static Bool ProcessExpose(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XExposeEvent *pEv; { XClearWindow(pDpyInfo->fromDpy, pDpyInfo->trigger); if (pDpyInfo->font) formatText(pDpyInfo->fromDpy, pDpyInfo->trigger, &(pDpyInfo->textGC), pDpyInfo->font, toDpyFormatLength, toDpyFormat, NULL, NULL); return False; } /* END ProcessExpose */ static Bool ProcessEnterNotify(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XCrossingEvent *pEv; { Display *fromDpy = pDpyInfo->fromDpy; XMotionEvent xmev; if ((pEv->mode == NotifyNormal) && (pDpyInfo->mode == X2X_DISCONNECTED) && (dpy == pDpyInfo->fromDpy)) { DoConnect(pDpyInfo); XWarpPointer(fromDpy, None, pDpyInfo->root, 0, 0, 0, 0, pDpyInfo->fromXConn, pEv->y_root); xmev.x_root = pDpyInfo->lastFromX = pDpyInfo->fromXConn; xmev.y_root = pEv->y_root; xmev.same_screen = True; ProcessMotionNotify(NULL, pDpyInfo, &xmev); } /* END if NotifyNormal... */ return False; } /* END ProcessEnterNotify */ static Bool ProcessButtonPress(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XButtonEvent *pEv; { int state; PSHADOW pShadow; unsigned int toButton; switch (pDpyInfo->mode) { case X2X_DISCONNECTED: pDpyInfo->mode = X2X_AWAIT_RELEASE; #ifdef DEBUG printf("awaiting button release before connecting\n"); #endif break; case X2X_CONNECTED: if (pEv->button <= N_BUTTONS) { // toButton = pDpyInfo->inverseMap[pEv->button]; toButton = pEv->button; for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) { fake_button_event(toButton,True); #ifdef DEBUG printf("from button %d down, to button %d down\n", pEv->button,toButton); #endif } /* END for */ if (doAutoUp) FakeAction(pDpyInfo, FAKE_BUTTON, toButton, True); if (doEdge) break; } /* check if more than one button pressed */ state = pEv->state; switch (pEv->button) { case Button1: state &= ~Button1Mask; break; case Button2: state &= ~Button2Mask; break; case Button3: state &= ~Button3Mask; break; case Button4: state &= ~Button4Mask; break; case Button5: state &= ~Button5Mask; break; default: #ifdef DEBUG printf("unknown button %d\n", pEv->button); #endif break; } /* END switch button */ if (state) { /* then more than one button pressed */ #ifdef DEBUG printf("awaiting button release before disconnecting\n"); #endif pDpyInfo->mode = X2X_CONN_RELEASE; } break; } /* END switch mode */ return False; } /* END ProcessButtonPress */ static Bool ProcessButtonRelease(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XButtonEvent *pEv; { int state; PSHADOW pShadow; XMotionEvent xmev; unsigned int toButton; if ((pDpyInfo->mode == X2X_CONNECTED) || (pDpyInfo->mode == X2X_CONN_RELEASE)) { if (pEv->button <= N_BUTTONS) { toButton = pEv->button; // toButton = pDpyInfo->inverseMap[pEv->button]; for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) { fake_button_event(toButton,False); #ifdef DEBUG printf("from button %d up, to button %d up\n", pEv->button, toButton); #endif } /* END for */ if (doAutoUp) FakeAction(pDpyInfo, FAKE_BUTTON, toButton, False); } } /* END if */ if (doEdge) return False; if ((pDpyInfo->mode == X2X_AWAIT_RELEASE) || (pDpyInfo->mode == X2X_CONN_RELEASE)) { /* make sure that all buttons are released */ state = pEv->state; switch (pEv->button) { case Button1: state &= ~Button1Mask; break; case Button2: state &= ~Button2Mask; break; case Button3: state &= ~Button3Mask; break; case Button4: state &= ~Button4Mask; break; case Button5: state &= ~Button5Mask; break; default: #ifdef DEBUG printf("unknown button %d\n", pEv->button); #endif break; } /* END switch button */ if (!state) { /* all buttons up: time to (dis)connect */ if (pDpyInfo->mode == X2X_AWAIT_RELEASE) { /* connect */ DoConnect(pDpyInfo); xmev.x_root = pDpyInfo->lastFromX = pEv->x_root; xmev.y_root = pEv->y_root; xmev.same_screen = True; ProcessMotionNotify(NULL, pDpyInfo, &xmev); } else { /* disconnect */ DoDisconnect(pDpyInfo); } /* END if mode */ } /* END if !state */ } /* END if mode */ return False; } /* END ProcessButtonRelease */ static Bool ProcessKeyEvent(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XKeyEvent *pEv; { // KeyCode keycode; KeySym keysym; PSHADOW pShadow; Bool bPress; PSTICKY pSticky; Bool DoFakeShift = False; KeyCode toShiftCode; KeyCode keycode; XLookupString(pEv, NULL, 0, &keysym, NULL); bPress = (pEv->type == KeyPress); /* If CapsLock is on, we need to do some funny business to make sure the */ /* "to" display does the right thing */ if(doCapsLkHack && (pEv->state & 0x2)) { /* Throw away any explicit shift events (they're faked as neccessary) */ if((keysym == XK_Shift_L) || (keysym == XK_Shift_R)) return False; /* If the shift key is pressed, do the shift, unless the keysym */ /* is an alpha key, in which case we invert the shift logic */ DoFakeShift = (pEv->state & 0x1); if(((keysym >= XK_A) && (keysym <= XK_Z)) || ((keysym >= XK_a) && (keysym <= XK_z))) DoFakeShift = !DoFakeShift; } for (pSticky = stickies; pSticky; pSticky = pSticky->pNext) if (keysym == pSticky->keysym) break; if (pSticky) { for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) { #if 1 toShiftCode = XKeysymToKeycode(dpy, XK_Shift_L); if ((keycode = XKeysymToKeycode(dpy, keysym))) { if(DoFakeShift) fake_key_event(toShiftCode, True); fake_key_event(keycode,True); fake_key_event(keycode,False); if(DoFakeShift) fake_key_event(toShiftCode, False); } /* END if */ #else if(DoFakeShift) fake_key_event(XK_Shift_L, True); fake_key_event(keysym,True); fake_key_event(keysym,False); if(DoFakeShift) fake_key_event(XK_Shift_L, False); #endif } /* END for */ } else { for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) { #if 1 toShiftCode = XKeysymToKeycode(dpy, XK_Shift_L); if ((keycode = XKeysymToKeycode(dpy, keysym))) { if(DoFakeShift) fake_key_event(toShiftCode, True); fake_key_event(keycode,bPress); if(DoFakeShift) fake_key_event(toShiftCode, False); } /* END if */ #else if(DoFakeShift) fake_key_event(XK_Shift_L, True); fake_key_event(keysym,bPress); if(DoFakeShift) fake_key_event(XK_Shift_L, False); #endif } /* END for */ if (doAutoUp) FakeAction(pDpyInfo, FAKE_KEY, keysym, bPress); } return False; } /* END ProcessKeyEvent */ static Bool ProcessConfigureNotify(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XConfigureEvent *pEv; { if (pDpyInfo->font) { /* reposition text */ toDpyFormat[toDpyLeftIndex] = MAX(0,((pEv->width - pDpyInfo->twidth) / 2)); toDpyFormat[toDpyTopIndex] = MAX(0,((pEv->height - pDpyInfo->theight) / 2)); } /* END if font */ return False; } /* END ProcessConfigureNotify */ static Bool ProcessClientMessage(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XClientMessageEvent *pEv; { /* terminate if atoms match! */ return ((pEv->message_type == pDpyInfo->wmpAtom) && (pEv->data.l[0] == pDpyInfo->wmdwAtom)); } /* END ProcessClientMessage */ /********** * process a visibility event **********/ static Bool ProcessVisibility(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XVisibilityEvent *pEv; { /* might want to qualify, based on other messages. otherwise, this code might cause a loop if two windows decide to fight it out for the top of the stack */ if (pEv->state != VisibilityUnobscured) XRaiseWindow(dpy, pEv->window); return False; } /* END ProcessVisibility */ /********** * process a keyboard mapping event **********/ static Bool ProcessMapping(dpy, pDpyInfo, pEv) Display *dpy; PDPYINFO pDpyInfo; XMappingEvent *pEv; { #ifdef DEBUG printf("mapping\n"); #endif switch (pEv->request) { case MappingModifier: case MappingKeyboard: XRefreshKeyboardMapping(pEv); break; case MappingPointer: RefreshPointerMapping(dpy, pDpyInfo); break; } /* END switch */ return False; } /* END ProcessMapping */ static void FakeAction(pDpyInfo, type, thing, bDown) PDPYINFO pDpyInfo; unsigned int thing; Bool bDown; { PFAKE *ppFake; PFAKE pFake; /* find the associated button, or the last record, whichever comes first */ for (ppFake = &(pDpyInfo->pFakeThings); (*ppFake && (((*ppFake)->type != type) || ((*ppFake)->thing != thing))); ppFake = &((*ppFake)->pNext)); if (bDown) { /* key down */ if (*ppFake == NULL) { /* need a new record */ pFake = (PFAKE)malloc(sizeof(FAKE)); pFake->pNext = NULL; /* always at the end of the list */ pFake->type = type; pFake->thing = thing; *ppFake = pFake; } /* END if */ } else { /* key up */ if (*ppFake != NULL) { /* get rid of the record */ /* splice out of the list */ pFake = *ppFake; *ppFake = pFake->pNext; free(pFake); /* blam! */ } /* END if */ } /* END if */ } /* END FakeAction */ static void FakeThingsUp(pDpyInfo) PDPYINFO pDpyInfo; { PFAKE pFake, pNext; PSHADOW pShadow; unsigned int type; KeyCode keycode; if (pDpyInfo->pFakeThings) { /* everything goes up! */ for (pFake = pDpyInfo->pFakeThings; pFake; pFake = pNext) { type = pFake->type; /* send up to all shadows */ for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) { if (type == FAKE_KEY) { /* key goes up */ #if 1 if ((keycode = XKeysymToKeycode(pDpyInfo->fromDpy, pFake->thing))) { fake_key_event(keycode,False); #ifdef DEBUG printf("key 0x%x up\n", pFake->thing); #endif } /* END if */ #else fake_key_event(pFake->thing,False); #endif } else { /* button goes up */ fake_button_event(pFake->thing,False); #ifdef DEBUG printf("button %d up\n", pFake->thing); #endif } /* END if/else */ } /* END for */ /* flush everything at once */ /* get next and free current */ pNext = pFake->pNext; free(pFake); } /* END for */ pDpyInfo->pFakeThings = NULL; } /* END if */ } /* END FakeThingsUp */ static void RefreshPointerMapping(dpy, pDpyInfo) Display *dpy; PDPYINFO pDpyInfo; { unsigned int buttCtr; unsigned char buttonMap[N_BUTTONS]; int nButtons; for (buttCtr = 1; buttCtr <= N_BUTTONS; ++buttCtr) { pDpyInfo->inverseMap[buttCtr] = buttCtr; } /* END for */ #if 0 if (dpy == pDpyInfo->toDpy) { /* only care about toDpy */ /* straightforward mapping */ for (buttCtr = 1; buttCtr <= N_BUTTONS; ++buttCtr) { pDpyInfo->inverseMap[buttCtr] = buttCtr; } /* END for */ nButtons = MIN(N_BUTTONS, XGetPointerMapping(dpy, buttonMap, N_BUTTONS)); if (doPointerMap) { for (buttCtr = 0; buttCtr < nButtons; ++buttCtr) { #ifdef DEBUG printf("button %d -> %d\n", buttCtr + 1, buttonMap[buttCtr]); #endif if (buttonMap[buttCtr] <= N_BUTTONS) pDpyInfo->inverseMap[buttonMap[buttCtr]] = buttCtr + 1; } /* END for */ } /* END if */ } /* END if toDpy */ #endif } /* END RefreshPointerMapping */