From 8907955471eb1e6507b3e1a7a2ca2ce0849b7766 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 25 May 2015 17:59:50 +0100 Subject: fish --- x2x.c | 1440 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1440 insertions(+) create mode 100644 x2x.c (limited to 'x2x.c') diff --git a/x2x.c b/x2x.c new file mode 100644 index 0000000..2dae613 --- /dev/null +++ b/x2x.c @@ -0,0 +1,1440 @@ +/* + * 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 */ -- cgit v1.2.3