1 /*
2  * x2x: Uses the XTEST extension to forward mouse movements and
3  * keystrokes from a window on one display to another display.  Useful
4  * for desks with multiple keyboards.
5  *
6  * Copyright (c) 1997
7  * Digital Equipment Corporation.  All rights reserved.
8  *
9  * By downloading, installing, using, modifying or distributing this
10  * software, you agree to the following:
11  *
12  * 1. CONDITIONS. Subject to the following conditions, you may download,
13  * install, use, modify and distribute this software in source and binary
14  * forms:
15  *
16  * a) Any source code, binary code and associated documentation
17  * (including the online manual) used, modified or distributed must
18  * reproduce and retain the above copyright notice, this list of
19  * conditions and the following disclaimer.
20  *
21  * b) No right is granted to use any trade name, trademark or logo of
22  * Digital Equipment Corporation.  Neither the "Digital Equipment
23  * Corporation" name nor any trademark or logo of Digital Equipment
24  * Corporation may be used to endorse or promote products derived from
25  * this software without the prior written permission of Digital
26  * Equipment Corporation.
27  *
28  * 2.  DISCLAIMER.  THIS SOFTWARE IS PROVIDED BY DIGITAL "AS IS" AND ANY
29  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY
32  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
34  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
36  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
37  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
38  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 /*
42  * Modified on 3 Oct 1998 by Charles Briscoe-Smith:
43  *   added options -north and -south
44  */
45 
46 /* Cygwin version with -fromwin to allow source to be a Windows
47  * machine that is not running a X server.
48  * Adapted by Mark Hayter 2003 using win2vnc ClientConnect.cpp code
49  *
50  * Original win2vnc copyright follows:
51  */
52 // win2vnc, adapted from vncviewer by Fredrik Hubinette 2001
53 //
54 // Original copyright follows:
55 //
56 //  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
57 //
58 //  This file is part of the VNC system.
59 //
60 //  The VNC system is free software; you can redistribute it and/or modify
61 //  it under the terms of the GNU General Public License as published by
62 //  the Free Software Foundation; either version 2 of the License, or
63 //  (at your option) any later version.
64 //
65 //  This program is distributed in the hope that it will be useful,
66 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
67 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
68 //  GNU General Public License for more details.
69 //
70 //  You should have received a copy of the GNU General Public License
71 //  along with this program; if not, write to the Free Software
72 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
73 //  USA.
74 //
75 // If the source code for the VNC system is not available from the place
76 // whence you received this file, check http://www.uk.research.att.com/vnc or contact
77 // the authors on vnc@uk.research.att.com for information on obtaining it.
78 
79 
80 
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <unistd.h>
85 #include <signal.h>
86 #include <errno.h>
87 #include <X11/Xlib.h>
88 #include <X11/Xresource.h>
89 #include <X11/Xutil.h>
90 #include <X11/cursorfont.h>
91 #include <X11/Xatom.h> /* for selection */
92 #include <X11/Xos.h>
93 #include <X11/extensions/XTest.h>
94 #include <X11/extensions/dpms.h>
95 #include <X11/keysym.h>
96 #include <X11/XKBlib.h>
97 
98 #ifdef WIN_2_X
99 #define _WIN32_WINNT 0x0500
100 #include <windows.h>
101 #include <windowsx.h>
102 #include <assert.h>
103 #include "keymap.h"
104 #include "resource.h"
105 
106 #define X2X_MSG_HOTKEY (WM_USER + 1)
107 
108 #define DPMSModeOn        0
109 extern Status DPMSForceLevel(Display *, unsigned short);
110 /* We always support this: */
111 #define DPMSQueryExtension(DPY, EVBASE, ERBASE) TRUE
112 #else
113 #include <X11/extensions/dpms.h>
114 #endif
115 
116 
117 
118 
119 /*#define DEBUG*/
120 
121 #ifndef MIN
122 #define MIN(x,y) (((x) < (y)) ? (x) : (y))
123 #endif
124 #ifndef MAX
125 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
126 #endif
127 
128 #define UTF8_STRING "UTF8_STRING"
129 
130 /**********
131  * definitions for edge
132  **********/
133 #define EDGE_NONE   0 /* don't transfer between edges of screens */
134 #define EDGE_NORTH  1 /* from display is on the north side of to display */
135 #define EDGE_SOUTH  2 /* from display is on the south side of to display */
136 #define EDGE_EAST   3 /* from display is on the east side of to display */
137 #define EDGE_WEST   4 /* from display is on the west side of to display */
138 
139 /**********
140  * functions
141  **********/
142 static void    ParseCommandLine();
143 static Display *OpenAndCheckDisplay();
144 static Bool    CheckTestExtension();
145 #ifndef WIN_2_X
146 static int     ErrorHandler();
147 #endif
148 static void    DoDPMSForceLevel();
149 static void    DoX2X();
150 static void    InitDpyInfo();
151 static void    DoConnect();
152 static void    DoDisconnect();
153 static void    RegisterEventHandlers();
154 static Bool    ProcessEvent();
155 static Bool    ProcessMotionNotify();
156 static Bool    ProcessExpose();
157 static void    DrawWindowText();
158 static Bool    ProcessEnterNotify();
159 static Bool    ProcessButtonPress();
160 static Bool    ProcessButtonRelease();
161 static Bool    ProcessKeyEvent();
162 static Bool    ProcessConfigureNotify();
163 static Bool    ProcessClientMessage();
164 static Bool    ProcessSelectionRequest();
165 static void    SendPing();
166 static Bool    ProcessPropertyNotify();
167 static Bool    ProcessSelectionNotify();
168 static void    SendSelectionNotify();
169 static Bool    ProcessSelectionClear();
170 static Bool    ProcessVisibility();
171 static Bool    ProcessMapping();
172 static void    FakeThingsUp();
173 static void    FakeAction();
174 static void    RefreshPointerMapping();
175 static void    Usage();
176 static void    *xmalloc();
177 
178 
179 /**********
180  * stuff for selection forwarding
181  **********/
182 typedef struct _dpyxtra {
183   Display *otherDpy;
184   int  sState;
185   Atom pingAtom;
186   Bool pingInProg;
187   Window propWin;
188 } DPYXTRA, *PDPYXTRA;
189 
190 /**********
191  * structures for recording state of buttons and keys
192  **********/
193 typedef struct _fakestr {
194   struct _fakestr *pNext;
195   int type;
196   KeySym thing;
197   KeyCode code;
198 } FAKE, *PFAKE;
199 
200 #define FAKE_KEY    0
201 #define FAKE_BUTTON 1
202 
203 #define N_BUTTONS   20
204 
205 #define MAX_BUTTONMAPEVENTS 20
206 
207 #define GETDPYXTRA(DPY,PDPYINFO)\
208    (((DPY) == (PDPYINFO)->fromDpy) ?\
209     &((PDPYINFO)->fromDpyXtra) : &((PDPYINFO)->toDpyXtra))
210 
211 /* values for sState */
212 #define SELSTATE_ON     0
213 #define SELSTATE_OFF    1
214 #define SELSTATE_WAIT   2
215 
216 /* special values for translated coordinates */
217 #define COORD_INCR     -1
218 #define COORD_DECR     -2
219 #define SPECIAL_COORD(COORD) (((COORD) < 0) ? (COORD) : 0)
220 
221 /* max unreasonable coordinates before accepting it */
222 #define MAX_UNREASONABLES 10
223 
224 /**********
225  * display information
226  **********/
227 typedef struct {
228   /* stuff on "from" display */
229   Display *fromDpy;
230   Atom    fromDpyUtf8String;
231   Window  root;
232   Window  trigger;
233   Window  big;
234   Window  selWinFrom;
235   int     selRevFrom;
236   GC      textGC;
237   Atom    wmpAtom, wmdwAtom;
238   Atom    netWmWindowTypeAtom, netWmWindowTypeDockAtom;
239   Atom    netWmStrutAtom;
240   Cursor  grabCursor;
241   Font    fid;
242   int     width, height, twidth, theight, tascent;
243   Bool    vertical;
244   int     lastFromCoord;
245   int     unreasonableDelta;
246 
247 #ifdef WIN_2_X
248   int     unreasonableCount;
249   /* From display info for Windows */
250   HWND    bigwindow;
251   HWND    edgewindow;
252   int     onedge;
253   int     fromWidth, fromHeight;
254   int     wdelta;
255   int     lastFromY;
256   HWND    hwndNextViewer;
257   // int     initialClipboardSeen;
258   char    *winSelText;
259   int     owntoXsel;
260   int     expectSelNotify;
261   int     expectOwnClip;
262   int     winSSave;
263   int     nXbuttons;
264   RECT    monitorRect;
265   RECT    screenRect;
266   int     screenHeight;
267   int     screenWidth;
268   int     lastFromX;
269 #endif /* WIN_2_X */
270 
271   /* stuff on "to" display */
272   Display *toDpy;
273   Atom    toDpyUtf8String;
274   Atom    toDpyTargets;
275   Window  selWinTo;
276   int     selRevTo;
277   unsigned int inverseMap[N_BUTTONS + 1]; /* inverse of button mapping */
278 
279   /* state of connection */
280   int     signal;		/* gort signal? */
281   int     mode;			/* connection */
282   int     eventMask;		/* trigger */
283 
284   /* coordinate conversion stuff */
285   int     toScreen;
286   int     nScreens;
287   short   **xTables; /* precalculated conversion tables */
288   short   **yTables;
289   int      fromConnCoord; /* location of cursor after conn/disc ops */
290   int     fromDiscCoord;
291   int     fromIncrCoord; /* location of cursor after incr/decr ops */
292   int     fromDecrCoord;
293 
294   /* selection forwarding info */
295   DPYXTRA fromDpyXtra;
296   DPYXTRA toDpyXtra;
297   Display *sDpy;
298   XSelectionRequestEvent sEv;
299   Time    sTime;
300 
301   /* for recording state of buttons and keys */
302   PFAKE   pFakeThings;
303 
304 } DPYINFO, *PDPYINFO;
305 
306 static DPYINFO dpyInfo;
307 
308 /* shadow displays */
309 typedef struct _shadow {
310   struct _shadow *pNext;
311   char    *name;
312   Display *dpy;
313   long    led_mask;
314   Bool    flush;
315   int     DPMSstatus; /* -1: not queried, 0: not supported, 1: supported */
316 } SHADOW, *PSHADOW;
317 
318 /* sticky keys */
319 typedef struct _sticky {
320   struct _sticky *pNext;
321   KeySym keysym;
322 } STICKY, *PSTICKY;
323 
324 typedef int  (*HANDLER)(); /* event handler function */
325 
326 /* These prototypes need the typedefs */
327 #ifdef WIN_2_X
328 static void MoveWindowToEdge(PDPYINFO);
329 static int MoveWindowToScreen(PDPYINFO);
330 static void DoWinConnect(PDPYINFO, int, int);
331 static void DoWinDisconnect(PDPYINFO, int, int);
332 static void SendButtonClick(PDPYINFO, int);
333 LRESULT CALLBACK WinProcessMessage (HWND, UINT, WPARAM, LPARAM);
334 void WinPointerEvent(PDPYINFO, int, int, DWORD, UINT);
335 void WinKeyEvent(PDPYINFO, int, DWORD);
336 void SendKeyEvent(PDPYINFO, KeySym, int, int, int);
337 
338 static Bool    ProcessSelectionRequestW();
339 static Bool    ProcessSelectionNotifyW();
340 static Bool    ProcessSelectionClearW();
341 
342 #ifdef DEBUGCHATTY
343 char *msgtotext(int);
344 #endif
345 
346 #endif /* WIN_2_X */
347 
348 /**********
349  * top-level variables
350  **********/
351 static char    *programStr = "x2x";
352 static char    *fromDpyName = NULL;
353 static char    *toDpyName   = NULL;
354 static char    *defaultFN   = "-*-times-bold-r-*-*-*-180-*-*-*-*-*-*";
355 static char    *fontName    = "-*-times-bold-r-*-*-*-180-*-*-*-*-*-*";
356 static char    *label       = NULL;
357 static char    *title       = NULL;
358 static char    *pingStr     = "PING"; /* atom for ping request */
359 static char    *geomStr     = NULL;
360 static Bool    waitDpy      = False;
361 static Bool    doBig        = False;
362 static Bool    doMouse      = True;
363 static int     doEdge       = EDGE_NONE;
364 static Bool    doSel        = True;
365 static Bool    doAutoUp     = True;
366 static Bool    doResurface  = False;
367 static Bool    winTransparent = False;
368 static Bool    doInputOnly  = True;
369 static PSHADOW shadows      = NULL;
370 static int     triggerw     = 2;
371 static Bool    doPointerMap = True;
372 static PSTICKY stickies     = NULL;
373 static Bool    doBtnBlock   = False;
374 static Bool    doCapsLkHack = False;
375 static Bool    doClipCheck  = False;
376 static Bool    doDpmsMouse  = False;
377 static int     logicalOffset= 0;
378 static int     nButtons     = 0;
379 static KeySym  buttonmap[N_BUTTONS + 1][MAX_BUTTONMAPEVENTS + 1];
380 static Bool    noScale      = False;
381 static int     compRegLeft  = 0;
382 static int     compRegRight = 0;
383 static int     compRegUp    = 0;
384 static int     compRegLow   = 0;
385 static Bool    useStruts    = False;
386 
387 #ifdef WIN_2_X
388 /* These are used to allow pointer comparisons */
389 static char *fromWinName = "x2xFromWin";
390 static int dummy;
391 static Display *fromWin = (Display *)&dummy;
392 static HWND hWndSave;
393 static HINSTANCE m_instance;
394 #endif
395 
396 #ifdef DEBUG_COMPLREG
397 #define debug_cmpreg printf
398 #else
debug_cmpreg(const char * fmt,...)399 void debug_cmpreg(const char *fmt, ...)
400 {
401 }
402 #endif
403 
404 #ifdef DEBUG
405 #define debug printf
406 #else
debug(const char * fmt,...)407 void debug(const char* fmt, ...)
408 {
409 }
410 #endif
411 
412 #ifdef WIN_2_X
413 
414 #define MAX_WIN_ARGS 40
415 
416 int STDCALL
WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR lpCmd,int nShow)417 WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow)
418 {
419   Display *fromDpy;
420   PSHADOW pShadow;
421   int argc;
422   char *argv[MAX_WIN_ARGS];
423   char *ap;
424 
425   if (lpCmd[0] == ' ')
426   {
427     lpCmd++;
428   }
429 
430   argv[0] = programStr;
431   argc = 1;
432   ap = lpCmd;
433 
434   /* XXX mdh - Should probably deal with quotes properly too */
435   while (*ap && (argc < MAX_WIN_ARGS)) {
436     argv[argc++] = ap;
437     while (*ap && (*ap != ' ')) ap++;
438     if (*ap == ' ') *ap++ = 0;
439   }
440   m_instance = hInst;
441 
442 #else /* Not WIN_2_X */
443 /**********
444  * main
445  **********/
446 int main(argc, argv)
447 int  argc;
448 char **argv;
449 {
450   Display *fromDpy;
451   PSHADOW pShadow;
452 
453 #endif /* WIN_2_X */
454 #ifdef DEBUG
455   setvbuf(stdout, NULL, _IONBF, 0);
456 #endif
457 
458   XrmInitialize();
459   ParseCommandLine(argc, argv);
460 
461 #ifdef WIN_2_X
462   if (fromDpyName != fromWinName)
463     fromDpyName = XDisplayName(fromDpyName);
464 #else
465   fromDpyName = XDisplayName(fromDpyName);
466 #endif
467 
468   toDpyName   = XDisplayName(toDpyName);
469   if (!strcasecmp(toDpyName, fromDpyName)) {
470     fprintf(stderr, "%s: display names are both %s\n", programStr, toDpyName);
471     exit(1);
472   }
473 
474   /* no OS independent way to stop Xlib from complaining via stderr,
475      but can always pipe stdout/stderr to /dev/null */
476   /* convert to real name: */
477 #ifdef WIN_2_X
478   if (fromDpyName == fromWinName) {
479     /* From is Windows, don't need to open */
480     fromDpy = fromWin;
481   } else
482     /* This ugly hanging else... */
483 #endif /* WIN_2_X */
484     /* ... qualifies this while in WIN_2_X case with an X source */
485   while ((fromDpy = XOpenDisplay(fromDpyName)) == NULL) {
486     if (!waitDpy) {
487       fprintf(stderr, "%s - error: can not open display %s\n",
488               programStr, fromDpyName);
489       exit(2);
490     } /* END if */
491     sleep(10);
492   } /* END while fromDpy */
493   (void)XSynchronize(fromDpy, True);
494 
495   /* toDpy is always the first shadow */
496   pShadow = (PSHADOW)xmalloc(sizeof(SHADOW));
497   pShadow->DPMSstatus = -1;
498   pShadow->name = toDpyName;
499   /* link into the global list */
500   pShadow->pNext = shadows;
501   shadows = pShadow;
502 
503   /* initialize all of the shadows, including the toDpy */
504   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
505     pShadow->led_mask = 0;
506     pShadow->flush = False;
507     if (!(pShadow->dpy = OpenAndCheckDisplay(pShadow->name)))
508       exit(3);
509   }
510   (void)XSynchronize(shadows->dpy, True);
511 
512 #ifndef WIN_2_X
513   /* set error handler,
514      so that program does not abort on non-critcal errors */
515   XSetErrorHandler(ErrorHandler);
516 #endif
517 
518     /* run the x2x loop */
519   DoX2X(fromDpy, shadows->dpy);
520 
521   /* shut down gracefully */
522 
523 #ifdef WIN_2_X
524   /* Only close if it is a real X from display */
525   if (fromDpy != fromWin)
526     XCloseDisplay(fromDpy);
527 #else
528   XCloseDisplay(fromDpy);
529 #endif
530 
531   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext)
532     XCloseDisplay(pShadow->dpy);
533   exit(0);
534 
535 } /* END main */
536 
537 static Display *OpenAndCheckDisplay(name)
538 char *name;
539 {
540   Display *openDpy;
541 
542   /* convert to real name: */
543   name = XDisplayName(name);
544   while ((openDpy = XOpenDisplay(name)) == NULL) {
545     if (!waitDpy) {
546       fprintf(stderr, "%s - error: can not open display %s\n",
547               programStr, name);
548       return NULL;
549     } /* END if */
550     sleep(10);
551   } /* END while openDpy */
552 
553   if (!CheckTestExtension(openDpy)) {
554     fprintf(stderr,
555             "%s - error: display %s does not support the test extension\n",
556             programStr, name);
557     return NULL;
558   }
559   return (openDpy);
560 
561 } /* END OpenAndCheckDisplay */
562 
563 /**********
564  * use standard X functions to parse the command line
565  **********/
566 static void ParseCommandLine(argc, argv)
567 int  argc;
568 char **argv;
569 {
570   int     arg;
571   PSHADOW pShadow;
572   extern  char *lawyerese;
573   PSTICKY pNewSticky;
574   KeySym  keysym;
575   int     button;
576   int     eventno;
577   char    *keyname, *argptr;
578 
579   debug("programStr = %s\n", programStr);
580 
581   /* Clear button map */
582   for (button = 0; button <= N_BUTTONS; button++)
583     buttonmap[button][0] = NoSymbol;
584 
585   for (arg = 1; arg < argc; ++arg) {
586 #ifdef WIN_2_X
587     if (!strcasecmp(argv[arg], "-fromWin")) {
588       fromDpyName = fromWinName;
589       /* XXX mdh - For now only support edge windows getting big */
590       /* Note: -east will override correctly (even if earlier on the line) */
591       doBig = True;
592       if (doEdge == EDGE_NONE) doEdge = EDGE_WEST;
593       doCapsLkHack = True;
594 
595       debug("fromDpyName = %s\n", fromDpyName);
596     } else
597       /* Note this else will qualify the if below... */
598 #endif /* WIN_2_X */
599     if (!strcasecmp(argv[arg], "-from")) {
600       if (++arg >= argc) Usage();
601       fromDpyName = argv[arg];
602 
603       debug("fromDpyName = %s\n", fromDpyName);
604     } else if (!strcasecmp(argv[arg], "-to")) {
605       if (++arg >= argc) Usage();
606       toDpyName = argv[arg];
607 
608       debug("toDpyName = %s\n", toDpyName);
609     } else if (!strcasecmp(argv[arg], "-font")) {
610       if (++arg >= argc) Usage();
611       fontName = argv[arg];
612 
613       debug("fontName = %s\n", fontName);
614     } else if (!strcasecmp(argv[arg], "-label")) {
615       if (++arg >= argc) Usage();
616       label = argv[arg];
617 
618       debug("label = %s\n", label);
619     } else if (!strcasecmp(argv[arg], "-title")) {
620       if (++arg > argc) Usage();
621       title = argv[arg];
622 
623       debug("title = %s\n", title);
624     } else if (!strcasecmp(argv[arg], "-geometry")) {
625       if (++arg >= argc) Usage();
626       geomStr = argv[arg];
627 
628       debug("geometry = %s\n", geomStr);
629     } else if (!strcasecmp(argv[arg], "-wait")) {
630       waitDpy = True;
631 
632       debug("will wait for displays\n");
633     } else if (!strcasecmp(argv[arg], "-big")) {
634       doBig = True;
635 
636       debug("will create big window on from display\n");
637     } else if (!strcasecmp(argv[arg], "-nomouse")) {
638       doMouse = False;
639 
640       debug("will not capture mouse (eek!)\n");
641     } else if (!strcasecmp(argv[arg], "-nopointermap")) {
642       doPointerMap = False;
643 
644       debug("will not do pointer mapping\n");
645     } else if (!strcasecmp(argv[arg], "-north")) {
646       doEdge = EDGE_NORTH;
647 
648       debug("\"from\" is on the north side of \"to\"\n");
649     } else if (!strcasecmp(argv[arg], "-south")) {
650       doEdge = EDGE_SOUTH;
651 
652       debug("\"from\" is on the south side of \"to\"\n");
653     } else if (!strcasecmp(argv[arg], "-east")) {
654       doEdge = EDGE_EAST;
655 
656       debug("\"from\" is on the east side of \"to\"\n");
657     } else if (!strcasecmp(argv[arg], "-west")) {
658       doEdge = EDGE_WEST;
659 
660       debug("\"from\" is on the west side of \"to\"\n");
661     } else if (!strcasecmp(argv[arg], "-nosel")) {
662       doSel = False;
663 
664       debug("will not transmit X selections between displays\n");
665     } else if (!strcasecmp(argv[arg], "-noautoup")) {
666       doAutoUp = False;
667 
668       debug("will not automatically lift keys and buttons\n");
669     } else if (!strcasecmp(argv[arg], "-buttonblock")) {
670       doBtnBlock = True;
671 
672       debug("mouse buttons down will block disconnects\n");
673     } else if (!strcasecmp(argv[arg], "-capslockhack")) {
674       doCapsLkHack = True;
675 
676       debug("behavior of CapsLock will be hacked\n");
677     } else if (!strcasecmp(argv[arg], "-dpmsmouse")) {
678       doDpmsMouse = True;
679 
680       debug("mouse movement wakes monitor\n");
681     } else if (!strcasecmp(argv[arg], "-offset")) {
682       if (++arg >= argc) Usage();
683       logicalOffset = atoi(argv[arg]);
684 
685       debug("logicalOffset %d\n", logicalOffset);
686     } else if (!strcasecmp(argv[arg], "-clipcheck")) {
687       doClipCheck = True;
688 
689       debug("Clipboard type will be checked for XA_STRING\n");
690     } else if (!strcasecmp(argv[arg], "-nocapslockhack")) {
691       doCapsLkHack = False;
692 
693       debug("behavior of CapsLock will not be hacked\n");
694     } else if (!strcasecmp(argv[arg], "-sticky")) {
695       if (++arg >= argc) Usage();
696       if ((keysym = XStringToKeysym(argv[arg])) != NoSymbol) {
697         pNewSticky = (PSTICKY)xmalloc(sizeof(STICKY));
698         pNewSticky->pNext  = stickies;
699         pNewSticky->keysym = keysym;
700         stickies = pNewSticky;
701 
702         debug("will press/release sticky key: %s\n", argv[arg]);
703       } else {
704         printf("x2x: warning: can't translate %s\n", argv[arg]);
705       }
706     } else if (!strcasecmp(argv[arg], "-buttonmap")) {
707       if (++arg >= argc) Usage();
708       button = atoi(argv[arg]);
709 
710       if ((button < 1) || (button > N_BUTTONS))
711         printf("x2x: warning: invalid button %d\n", button);
712       else if (++arg >= argc)
713         Usage();
714       else
715       {
716         debug("will map button %d to keysyms '%s'\n", button, argv[arg]);
717 
718         argptr  = argv[arg];
719         eventno = 0;
720         while ((keyname = strtok(argptr, " \t\n\r")) != NULL)
721         {
722           if ((keysym = XStringToKeysym(keyname)) == NoSymbol)
723             printf("x2x: warning: can't translate %s\n", keyname);
724           else if (eventno + 1 >= MAX_BUTTONMAPEVENTS)
725             printf("x2x: warning: too many keys mapped to button %d\n",
726                    button);
727           else
728             buttonmap[button][eventno++] = keysym;
729           argptr = NULL;
730         }
731         buttonmap[button][eventno] = NoSymbol;
732       }
733     } else if (!strcasecmp(argv[arg], "-resurface")) {
734       doResurface = True;
735 
736       debug("will resurface the trigger window when obscured\n");
737     } else if (!strcasecmp(argv[arg], "-win-output")) {
738       doInputOnly = False;
739       debug("trigger window will be an InputOutput window\n");
740     } else if (!strcasecmp(argv[arg], "-win-transparent")) {
741       winTransparent = True;
742       debug("trigger window will be transparent\n");
743     } else if (!strcasecmp(argv[arg], "-shadow")) {
744       if (++arg >= argc) Usage();
745       pShadow = (PSHADOW)xmalloc(sizeof(SHADOW));
746       pShadow->DPMSstatus = -1;
747       pShadow->name = argv[arg];
748 
749       /* into the global list of shadows */
750       pShadow->pNext = shadows;
751       shadows = pShadow;
752 
753     } else if (!strcasecmp(argv[arg], "-triggerw")) {
754       if (++arg >= argc) Usage();
755       triggerw = atoi(argv[arg]);
756     } else if (!strcasecmp(argv[arg], "-copyright")) {
757       puts(lawyerese);
758     } else if (!strcasecmp(argv[arg], "-noscale")) {
759       noScale = True;
760     } else if (!strcasecmp(argv[arg], "-completeregionleft")) {
761       if (++arg >= argc) Usage();
762       compRegLeft = atoi(argv[arg]);
763     } else if (!strcasecmp(argv[arg], "-completeregionright")) {
764       if (++arg >= argc) Usage();
765       compRegRight = atoi(argv[arg]);
766     } else if (!strcasecmp(argv[arg], "-completeregionup")) {
767       if (++arg >= argc) Usage();
768       compRegUp = atoi(argv[arg]);
769     } else if (!strcasecmp(argv[arg], "-completeregionlow")) {
770       if (++arg >= argc) Usage();
771       compRegLow = atoi(argv[arg]);
772 
773     } else if (!strcasecmp(argv[arg], "-struts")) {
774       useStruts = True;
775 
776       debug("will advertise struts in _NET_WM_STRUT\n");
777     } else {
778       Usage();
779     } /* END if... */
780   } /* END for */
781 
782 } /* END ParseCommandLine */
783 
784 static void Usage()
785 {
786 #ifdef WIN_2_X
787   printf("Usage: x2x [-fromwin | -from <DISPLAY>][-to <DISPLAY>] options..\n");
788 #else
789   printf("Usage: x2x [-to <DISPLAY> | -from <DISPLAY>] options...\n");
790 #endif
791   printf("       -copyright\n");
792   printf("       -font <FONTNAME>\n");
793   printf("       -geometry <GEOMETRY>\n");
794   printf("       -wait\n");
795   printf("       -big\n");
796   printf("       -buttonblock\n");
797   printf("       -nomouse\n");
798   printf("       -nopointermap\n");
799   printf("       -north\n");
800   printf("       -south\n");
801   printf("       -east\n");
802   printf("       -west\n");
803   printf("       -nosel\n");
804   printf("       -noautoup\n");
805   printf("       -resurface\n");
806   printf("       -win-output\n");
807   printf("       -win-transparent\n");
808   printf("       -capslockhack\n");
809   printf("       -nocapslockhack\n");
810   printf("       -clipcheck\n");
811   printf("       -shadow <DISPLAY>\n");
812   printf("       -sticky <sticky key>\n");
813   printf("       -label <LABEL>\n");
814   printf("       -title <TITLE>\n");
815   printf("       -buttonmap <button#> \"<keysym> ...\"\n");
816   printf("       -struts\n");
817 #ifdef WIN_2_X
818   printf("       -offset [-]<pixel offset of \"to\">\n");
819   printf("WIN_2_X build allows Windows or X as -from display\n");
820   printf("Note that -fromwin sets default to -big -west -capslockhack\n");
821   printf("A Windows shortcut of the form:\n");
822   printf("C:\\cygwin\\usr\\X11R6\\bin\\run.exe /usr/X11R6/bin/x2x -fromwin -to <to> -east\n");
823   printf("Should work to start the app from the desktop or start menu, but\n");
824   printf("c:\\cygwin\\bin may need to be added to the PATH to get cygwin1.dll\n");
825 #endif
826   exit(4);
827 
828 } /* END Usage */
829 
830 /**********
831  * call the library to check for the test extension
832  **********/
833 static Bool CheckTestExtension(dpy)
834 Display  *dpy;
835 {
836   int eventb, errorb;
837   int vmajor, vminor;
838 
839   return (XTestQueryExtension(dpy, &eventb, &errorb, &vmajor, &vminor));
840 
841 } /* END CheckTestExtension */
842 
843 #ifndef WIN_2_X
844 int ErrorHandler(Display *disp, XErrorEvent *event) {
845 
846   int bufflen = 1024;
847   char *buff;
848   buff = malloc(bufflen);
849 
850   XGetErrorText(disp,event->error_code,buff,bufflen);
851   debug("x2x:ErrHandler(): Display:`%s` \t error_code:`%i` \n",
852         XDisplayName(NULL),event->error_code);
853   printf(" %s \n",buff);
854   free(buff);
855   return True;
856 }
857 #endif
858 
859 #define X2X_DISCONNECTED    0
860 #define X2X_AWAIT_RELEASE   1
861 #define X2X_CONNECTED       2
862 #define X2X_CONN_RELEASE    3
863 
864 static void signal_handler(int sig)
865 {
866   if (dpyInfo.mode == X2X_CONNECTED)
867     DoDisconnect(&dpyInfo);
868   dpyInfo.signal = sig;
869 }
870 
871 static void DoX2X(fromDpy, toDpy)
872 Display *fromDpy;
873 Display *toDpy;
874 {
875   int       nfds;
876   fd_set    fdset;
877   Bool      fromPending;
878   int       fromConn, toConn;
879 
880   /* set up displays */
881   dpyInfo.fromDpy = fromDpy;
882   dpyInfo.toDpy = toDpy;
883   InitDpyInfo(&dpyInfo);
884   RegisterEventHandlers(&dpyInfo);
885 
886   signal(SIGINT,  signal_handler);
887   signal(SIGTERM, signal_handler);
888 
889   /* set up for select */
890 #ifdef WIN_2_X
891   fromConn = (fromDpy == fromWin) ? 0 : XConnectionNumber(fromDpy);
892 #else
893   fromConn = XConnectionNumber(fromDpy);
894 #endif /* WIN_2_X */
895 
896   toConn   = XConnectionNumber(toDpy);
897   nfds = (fromConn > toConn ? fromConn : toConn) + 1;
898 
899 #ifdef WIN_2_X
900   if (fromDpy == fromWin) {
901     MSG                msg;                /* A Win32 message structure. */
902     int         nowQuit;
903 
904     nowQuit = 0;
905 
906     /* XXX mdh - This is not quite right becaue it only does To events */
907     /* XXX mdh - when there are From events                            */
908     /* This is mostly ok, but can cause windows->X paste to take a while */
909     /* As a compromise, try a 1 second tick to cause polling */
910 
911     SetTimer(dpyInfo.bigwindow, 1, 1000 /* in ms */, NULL);
912     /* GetMessage blocks until the next Windows event */
913     /* It returns 0 if the app should quit */
914     while (!nowQuit && GetMessage (&msg, NULL, 0, 0)) {
915       /* XXX mdh - Translate may not be needed if all Key processing is */
916       /* XXX mdh - done using the Key events rather than CHAR events    */
917       TranslateMessage (&msg);
918       DispatchMessage (&msg);
919       /* Done a Windows event, now loop while -to has something */
920       while (XPending(toDpy) || dpyInfo.expectSelNotify) {
921         if (ProcessEvent(toDpy, &dpyInfo)) { /* done! */
922           nowQuit = 1;
923           break;
924         }
925         /* PeekMessage is a non-blocking version of GetMessage */
926         /* But it returns 0 for no messages rather than for quit */
927         if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
928           /* need explicit check for quit */
929           if (msg.message == WM_QUIT) {
930             nowQuit = 1;
931             break; /* from the while(XPending... )*/
932           }
933           /* XXX mdh - see above */
934           TranslateMessage (&msg);
935           DispatchMessage (&msg);
936         }
937       }
938     }
939   } else
940     /* Again, the else qualifies the while below */
941 #endif /* WIN_2_X */
942   while (dpyInfo.signal == 0) { /* FOREVER */
943     if ((fromPending = XPending(fromDpy)))
944       if (ProcessEvent(fromDpy, &dpyInfo)) /* done! */
945         break;
946 
947     if (XPending(toDpy)) {
948       if (ProcessEvent(toDpy, &dpyInfo)) /* done! */
949         break;
950     } else if (!fromPending) {
951       FD_ZERO(&fdset);
952       FD_SET(fromConn, &fdset);
953       FD_SET(toConn, &fdset);
954       select(nfds, &fdset, NULL, NULL, NULL);
955     }
956 
957   } /* END FOREVER */
958 
959 } /* END DoX2X() */
960 
961 static void InitDpyInfo(pDpyInfo)
962 PDPYINFO pDpyInfo;
963 {
964   Display   *fromDpy, *toDpy;
965   Screen    *fromScreen;
966   long      black, white;
967   int       fromHeight, fromWidth, toHeight, toWidth;
968   Pixmap    nullPixmap;
969   XColor    dummyColor;
970   Window    root, trigger, big, rret, toRoot, propWin;
971   short     *xTable, *yTable; /* short: what about dimensions > 2^15? */
972   int       *heights, *widths;
973   int       counter;
974   int       nScreens, screenNum;
975   int       twidth, theight, tascent; /* text dimensions */
976   int       xoff, yoff; /* window offsets */
977   unsigned int width, height; /* window width, height */
978   int       geomMask;                /* mask returned by parse */
979   int       gravMask;
980   int       gravity;
981   int       xret, yret;
982   unsigned int wret, hret, bret, dret;
983   XSetWindowAttributes xswa;
984   XSizeHints *xsh;
985   int       eventMask;
986   GC        textGC;
987   char      *windowName;
988   Font      fid;
989   PSHADOW   pShadow;
990   int       triggerLoc;
991   Bool      vertical;
992 
993   /* cache commonly used variables */
994   fromDpy = pDpyInfo->fromDpy;
995   toDpy   = pDpyInfo->toDpy;
996   pDpyInfo->toDpyXtra.propWin = (Window) 0;
997 
998   gravity = NorthWestGravity;   /* Default gravity of window. */
999   pDpyInfo->fid = 0; /* Default is no text. */
1000 
1001 #ifdef WIN_2_X
1002   pDpyInfo->unreasonableCount = 0;
1003 
1004   if (fromDpy == fromWin) {
1005 
1006     fromWidth=GetSystemMetrics(SM_CXSCREEN);
1007     fromHeight=GetSystemMetrics(SM_CYSCREEN);
1008 
1009     POINT pt;
1010 
1011     pDpyInfo->screenRect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
1012     pDpyInfo->screenRect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
1013     pDpyInfo->screenRect.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
1014     pDpyInfo->screenRect.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
1015     pDpyInfo->screenHeight = pDpyInfo->screenRect.bottom - pDpyInfo->screenRect.top;
1016     pDpyInfo->screenWidth = pDpyInfo->screenRect.right - pDpyInfo->screenRect.left;
1017 
1018 
1019     // Guess a a point at or near the monitor we want.
1020     if (doEdge == EDGE_NORTH || doEdge == EDGE_SOUTH)
1021     {
1022       pt.x = (pDpyInfo->screenRect.right - pDpyInfo->screenRect.left) / 2;
1023       pt.y = (doEdge == EDGE_SOUTH) ? pDpyInfo->screenRect.bottom - 1 : pDpyInfo->screenRect.top;;
1024     }
1025     else
1026     {
1027       pt.x = (doEdge == EDGE_EAST) ? pDpyInfo->screenRect.right - 1 : pDpyInfo->screenRect.left;
1028       pt.y = (pDpyInfo->screenRect.bottom -  pDpyInfo->screenRect.top) / 2;
1029     }
1030 
1031     HMONITOR mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
1032 
1033     MONITORINFO monInfo;
1034     monInfo.cbSize = sizeof(MONITORINFO);
1035     GetMonitorInfo(mon, &monInfo);
1036 
1037     pDpyInfo->monitorRect = monInfo.rcMonitor;
1038 
1039     /* these should not be used, but keep compiler happy */
1040     fromScreen = (Screen *) 0;
1041     black = white = 0;
1042     root = pDpyInfo->root = None;
1043   } else {
1044     fromScreen = XDefaultScreenOfDisplay(fromDpy);
1045     black      = XBlackPixelOfScreen(fromScreen);
1046     white      = XWhitePixelOfScreen(fromScreen);
1047     fromHeight = XHeightOfScreen(fromScreen);
1048     fromWidth  = XWidthOfScreen(fromScreen);
1049     root       = pDpyInfo->root      = XDefaultRootWindow(fromDpy);
1050   }
1051   toRoot     = XDefaultRootWindow(toDpy);
1052   nScreens   = pDpyInfo->nScreens  = XScreenCount(toDpy);
1053   vertical   = pDpyInfo->vertical = (doEdge == EDGE_NORTH
1054                                       || doEdge == EDGE_SOUTH);
1055 #else
1056   gravity = NorthWestGravity;   /* keep compliler happy */
1057   fromScreen = XDefaultScreenOfDisplay(fromDpy);
1058   black      = XBlackPixelOfScreen(fromScreen);
1059   white      = XWhitePixelOfScreen(fromScreen);
1060   fromHeight = XHeightOfScreen(fromScreen);
1061   fromWidth  = XWidthOfScreen(fromScreen);
1062   toRoot     = XDefaultRootWindow(toDpy);
1063 
1064   /* values also in dpyinfo */
1065   root       = pDpyInfo->root      = XDefaultRootWindow(fromDpy);
1066   nScreens   = pDpyInfo->nScreens  = XScreenCount(toDpy);
1067   vertical   = pDpyInfo->vertical = (doEdge == EDGE_NORTH
1068                                       || doEdge == EDGE_SOUTH);
1069 #endif
1070 
1071 #ifdef WIN_2_X
1072   if (fromDpy != fromWin) {
1073 #endif
1074     pDpyInfo->fromDpyUtf8String = XInternAtom(fromDpy, UTF8_STRING, False);
1075 #ifdef WIN_2_X
1076   }
1077 #endif
1078   pDpyInfo->toDpyUtf8String = XInternAtom(toDpy, UTF8_STRING, False);
1079 
1080   /* other dpyinfo values */
1081   pDpyInfo->mode        = X2X_DISCONNECTED;
1082   pDpyInfo->unreasonableDelta = (vertical ? fromHeight : fromWidth) / 2;
1083   pDpyInfo->pFakeThings = NULL;
1084 
1085   /* window init structures */
1086   xswa.override_redirect = True;
1087   xsh = XAllocSizeHints();
1088   eventMask = KeyPressMask | KeyReleaseMask;
1089 
1090   /* cursor locations for moving between screens */
1091   pDpyInfo->fromIncrCoord = triggerw;
1092   pDpyInfo->fromDecrCoord = (vertical ? fromHeight : fromWidth) - triggerw - 1;
1093   if (doEdge) { /* edge triggers x2x */
1094 #ifdef WIN_2_X
1095     if (fromDpy == fromWin) {
1096       /* keep compiler happy */
1097       nullPixmap = 0;
1098       pDpyInfo->grabCursor = 0;
1099     } else {
1100 #endif
1101     nullPixmap = XCreatePixmap(fromDpy, root, 1, 1, 1);
1102     eventMask |= EnterWindowMask;
1103     pDpyInfo->grabCursor =
1104       XCreatePixmapCursor(fromDpy, nullPixmap, nullPixmap,
1105                           &dummyColor, &dummyColor, 0, 0);
1106 #ifdef WIN_2_X
1107     }
1108 #endif
1109 
1110     /* trigger window location */
1111     if (doEdge == EDGE_NORTH) {
1112       triggerLoc = 0;
1113       pDpyInfo->fromConnCoord = fromHeight - triggerw - 1;
1114       pDpyInfo->fromDiscCoord = triggerw;
1115     } else if (doEdge == EDGE_SOUTH) {
1116       triggerLoc = fromHeight - triggerw;
1117       pDpyInfo->fromConnCoord = 1;
1118       pDpyInfo->fromDiscCoord = triggerLoc - 1;
1119     } else if (doEdge == EDGE_EAST) {
1120       triggerLoc = fromWidth - triggerw;
1121       pDpyInfo->fromConnCoord = 1;
1122       pDpyInfo->fromDiscCoord = triggerLoc - 1;
1123     } else /* doEdge == EDGE_WEST */ {
1124       triggerLoc = 0;
1125       pDpyInfo->fromConnCoord = fromWidth - triggerw - 1;
1126       pDpyInfo->fromDiscCoord = triggerw;
1127     } /* END if doEdge == ... */
1128 
1129     xswa.background_pixel = black;
1130 
1131 #ifdef WIN_2_X
1132     if (fromDpy == fromWin) {
1133 
1134       /* Create both trigger and big windows here */
1135       /* This code is based on Win2VBC/vncviewer ClientConnection.cpp */
1136       WNDCLASS wndclass;
1137       const DWORD winstyle = 0;
1138 
1139       wndclass.style		= 0;
1140       wndclass.lpfnWndProc	= WinProcessMessage;
1141       wndclass.cbClsExtra	= 0;
1142       wndclass.cbWndExtra	= 0;
1143       wndclass.hInstance	= m_instance;
1144       /* XXX mdh - For now just use system provided resources */
1145       wndclass.hIcon		= LoadIcon(NULL, IDI_APPLICATION);
1146       // wndclass.hCursor	= LoadCursor(NULL, IDC_NO);
1147       wndclass.hCursor		= LoadCursor(m_instance, MAKEINTRESOURCE(IDC_NOCURSOR));
1148       wndclass.hbrBackground	= (HBRUSH) GetStockObject(BLACK_BRUSH);
1149       wndclass.lpszMenuName	= (const TCHAR *) NULL;
1150       wndclass.lpszClassName	= fromWinName;
1151 
1152       RegisterClass(&wndclass);
1153 
1154 
1155      pDpyInfo->bigwindow = CreateWindowEx(
1156          WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
1157          fromWinName,
1158          "x2x_big",
1159          winstyle,
1160          0,
1161          0,
1162          fromWidth,
1163          fromHeight,
1164          NULL,                // Parent handle
1165          NULL,                // Menu handle
1166          m_instance,
1167          NULL);
1168 
1169       pDpyInfo->edgewindow = CreateWindowEx(
1170          WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
1171          fromWinName,
1172          "x2x_edge",
1173          winstyle,
1174          /* Next 4 are x, y, width, height */
1175          (doEdge == EDGE_EAST) ? pDpyInfo->monitorRect.right -1: pDpyInfo->monitorRect.left,
1176          (doEdge == EDGE_SOUTH) ? pDpyInfo->monitorRect.bottom - 1 : pDpyInfo->monitorRect.top,
1177          (pDpyInfo->vertical) ? pDpyInfo->monitorRect.right - pDpyInfo->monitorRect.left : 1,
1178          (pDpyInfo->vertical) ? 1 : pDpyInfo->monitorRect.bottom - pDpyInfo->monitorRect.top,
1179          pDpyInfo->bigwindow, // Parent handle
1180          NULL,                // Menu handle
1181          m_instance,
1182          NULL);
1183 
1184 
1185       ShowWindow(pDpyInfo->bigwindow, SW_HIDE);
1186       ShowWindow(pDpyInfo->edgewindow, SW_HIDE);
1187       pDpyInfo->fromWidth = fromWidth;
1188       pDpyInfo->fromHeight = fromHeight;
1189       pDpyInfo->wdelta = 0;
1190 
1191       // record which client created this window
1192       SetWindowLong(pDpyInfo->bigwindow, GWL_USERDATA, (LONG) pDpyInfo);
1193       SetWindowLong(pDpyInfo->edgewindow, GWL_USERDATA, (LONG) pDpyInfo);
1194 
1195       // Set up clipboard watching
1196       // We want to know when the clipboard changes, so
1197       // insert ourselves in the viewer chain. But doing
1198       // this will cause us to be notified immediately of
1199       // the current state.
1200       // We don't want to send that.
1201       pDpyInfo->expectOwnClip = 0;
1202       if (doSel) {
1203         // pDpyInfo->initialClipboardSeen = False;
1204         pDpyInfo->winSelText = NULL;
1205         pDpyInfo->hwndNextViewer = SetClipboardViewer(pDpyInfo->edgewindow);
1206       }
1207 
1208       pDpyInfo->onedge = 0;
1209       MoveWindowToEdge(pDpyInfo);
1210       /* Keep compile happy */
1211       trigger = 0;
1212     } else {
1213 #endif
1214     /* fromWidth - 1 doesn't seem to work for some reason */
1215     /* Use triggerw offsets so that if an x2x is running
1216        along the left edge and along the north edge, both with
1217        -resurface, we don't get a feedback loop of them each
1218        fighting to be on top.
1219         --09/27/99 Greg J. Badros <gjb@cs.washington.edu> */
1220     /* also, make it an InputOnly window so I don't lose
1221        screen real estate --09/29/99 gjb */
1222     /* make it InputOutput when argument -win-output presents,
1223        so to get window visibility change event and make -resurface work */
1224     trigger = pDpyInfo->trigger =
1225       XCreateWindow(fromDpy, root,
1226                     vertical ? triggerw : triggerLoc,
1227                     vertical ? triggerLoc : triggerw,
1228                     vertical ? fromWidth - (2*triggerw) : triggerw,
1229                     vertical ? triggerw : fromHeight - (2*triggerw),
1230                     0, 0, doInputOnly?InputOnly:InputOutput, 0,
1231                     CWOverrideRedirect, &xswa);
1232 
1233     pDpyInfo->netWmWindowTypeAtom = XInternAtom(fromDpy, "_NET_WM_WINDOW_TYPE", True);
1234     pDpyInfo->netWmWindowTypeDockAtom = XInternAtom(fromDpy, "_NET_WM_WINDOW_TYPE_DOCK", True);
1235     XChangeProperty(fromDpy, trigger, pDpyInfo->netWmWindowTypeAtom,
1236                     XA_ATOM, 32, PropModeReplace,
1237                     (unsigned char *)&pDpyInfo->netWmWindowTypeDockAtom, 1);
1238 
1239     if (useStruts) {
1240       unsigned long struts[4] = { doEdge == EDGE_WEST ? triggerw : 0
1241                                 , doEdge == EDGE_EAST ? triggerw : 0
1242                                 , doEdge == EDGE_NORTH ? triggerw : 0
1243                                 , doEdge == EDGE_SOUTH ? triggerw : 0
1244                                 };
1245       pDpyInfo->netWmStrutAtom = XInternAtom(fromDpy, "_NET_WM_STRUT", True);
1246       XChangeProperty(fromDpy, trigger, pDpyInfo->netWmStrutAtom,
1247                       XA_CARDINAL, 32, PropModeReplace,
1248                       (unsigned char *)&struts, 4);
1249     }
1250 
1251 #ifdef WIN_2_X
1252     }
1253 #endif
1254   } else { /* normal window for text: do size grovelling */
1255     pDpyInfo->grabCursor = XCreateFontCursor(fromDpy, XC_exchange);
1256     eventMask |= StructureNotifyMask | ExposureMask;
1257     if (doMouse) eventMask |= ButtonPressMask | ButtonReleaseMask;
1258 
1259     if (label == NULL)
1260       label = toDpyName;
1261     /* determine size of text */
1262     if (((fid = XLoadFont(fromDpy, fontName)) != 0) ||
1263         ((fid = XLoadFont(fromDpy, defaultFN)) != 0) ||
1264         ((fid = XLoadFont(fromDpy, "fixed")) != 0)) {
1265       /* have a font */
1266       int ascent, descent, direction;
1267       XCharStruct overall;
1268 
1269       XQueryTextExtents(fromDpy, fid, label, strlen(label),
1270                         &direction, &ascent, &descent, &overall);
1271       twidth = -overall.lbearing + overall.rbearing;
1272       theight = ascent + descent;
1273       tascent = ascent;
1274 
1275       textGC = pDpyInfo->textGC = XCreateGC(fromDpy, root, 0, NULL);
1276       XSetState(fromDpy, textGC, black, white, GXcopy, AllPlanes);
1277       XSetFont(fromDpy, textGC, fid);
1278 
1279       pDpyInfo->fid = fid;
1280       pDpyInfo->twidth = twidth;
1281       pDpyInfo->theight = theight;
1282       pDpyInfo->tascent = tascent;
1283 
1284     } else { /* should not have to execute this clause: */
1285       twidth = theight = 100; /* default window size */
1286     } /* END if have a font ... else ... */
1287 
1288     /* determine size of window */
1289     xoff = yoff = 0;
1290     width = twidth + 4; /* XXX gap around text -- should be configurable */
1291     height = theight + 4;
1292     geomMask = XParseGeometry(geomStr, &xoff, &yoff, &width, &height);
1293     switch (gravMask = (geomMask & (XNegative | YNegative))) {
1294     case (XNegative | YNegative): gravity = SouthEastGravity; break;
1295     case XNegative:               gravity = NorthEastGravity; break;
1296     case YNegative:               gravity = SouthWestGravity; break;
1297     default:                      gravity = NorthWestGravity; break;
1298     }
1299     pDpyInfo->width = width;
1300     pDpyInfo->height = height;
1301     if (gravMask) {
1302       XGetGeometry(fromDpy, root,
1303                    &rret, &xret, &yret, &wret, &hret, &bret, &dret);
1304       if ((geomMask & (XValue | XNegative)) == (XValue | XNegative)){
1305         xoff = wret - width + xoff;
1306       }
1307       if ((geomMask & (YValue | YNegative)) == (YValue | YNegative)) {
1308         yoff = hret - height + yoff;
1309       }
1310     } /* END if geomMask */
1311 
1312     trigger = pDpyInfo->trigger =
1313       XCreateSimpleWindow(fromDpy, root, xoff, yoff, width, height,
1314                           0, black, white);
1315   } /* END if doEdge ... else ...*/
1316 
1317 #ifdef WIN_2_X
1318   if (fromDpy != fromWin) {
1319 #endif
1320   /* size hints stuff: */
1321   xsh->x           = xoff;
1322   xsh->y           = yoff;
1323   xsh->base_width  = width;
1324   xsh->base_height = height;
1325   xsh->win_gravity = gravity;
1326   xsh->flags       = (PPosition|PBaseSize|PWinGravity);
1327   XSetWMNormalHints(fromDpy, trigger, xsh);
1328 
1329   if (title) {
1330     windowName = title;
1331   } else {
1332     windowName = (char *)xmalloc(strlen(programStr) + strlen(toDpyName) + 2);
1333     sprintf(windowName, "%s %s", programStr, toDpyName);
1334   }
1335 
1336   XStoreName(fromDpy, trigger, windowName);
1337   XSetIconName(fromDpy, trigger, windowName);
1338 
1339   /* register for WM_DELETE_WINDOW protocol */
1340   pDpyInfo->wmpAtom = XInternAtom(fromDpy, "WM_PROTOCOLS", True);
1341   pDpyInfo->wmdwAtom = XInternAtom(fromDpy, "WM_DELETE_WINDOW", True);
1342   XSetWMProtocols(fromDpy, trigger, &(pDpyInfo->wmdwAtom), 1);
1343 
1344   /* making the trigger window transparent */
1345   if (winTransparent) {
1346     u_int32_t cardinal_alpha = (u_int32_t) (0);
1347     XChangeProperty(fromDpy, trigger,
1348       XInternAtom(fromDpy, "_NET_WM_WINDOW_OPACITY", 0),
1349       XA_CARDINAL, 32, PropModeReplace, (u_int8_t*) &cardinal_alpha,1);
1350   };
1351 
1352   /* mdh - Put in Chaiken's change to make this InputOnly */
1353   if (doBig) {
1354     big = pDpyInfo->big =
1355       XCreateWindow(fromDpy, root, 0, 0, fromWidth, fromHeight, 0,
1356                     0, InputOnly, 0, CWOverrideRedirect, &xswa);
1357     /* size hints stuff: */
1358     xsh->x           = 0;
1359     xsh->y           = 0;
1360     xsh->base_width  = fromWidth;
1361     xsh->base_height = fromHeight;
1362     xsh->min_width   = fromWidth;
1363     xsh->min_height  = fromHeight;
1364     xsh->flags       = (PMinSize|PPosition|PBaseSize);
1365 
1366     XSetWMNormalHints(fromDpy, big, xsh);
1367     XStoreName(fromDpy, big, windowName);
1368     XSetIconName(fromDpy, big, windowName);
1369   } else {
1370     pDpyInfo->big = None;
1371   }
1372 
1373   XFree((char *) xsh);
1374 
1375   if (!title)
1376     free(windowName);
1377 #ifdef WIN_2_X
1378   }
1379 #endif
1380 
1381   /* conversion stuff */
1382   pDpyInfo->toScreen = (doEdge == EDGE_WEST || doEdge == EDGE_NORTH)
1383                         ? (nScreens - 1) : 0;
1384 
1385   /* construct table lookup for screen coordinate conversion */
1386   pDpyInfo->xTables = (short **)xmalloc(sizeof(short *) * nScreens);
1387   pDpyInfo->yTables = (short **)xmalloc(sizeof(short *) * nScreens);
1388   heights = (int *)xmalloc(sizeof(int *) * nScreens);
1389   widths  = (int *)xmalloc(sizeof(int *) * nScreens);
1390 
1391   for (screenNum = 0; screenNum < nScreens; ++screenNum) {
1392     widths[screenNum] = toWidth  =
1393       XWidthOfScreen(XScreenOfDisplay(toDpy, screenNum));
1394     heights[screenNum] = toHeight =
1395       XHeightOfScreen(XScreenOfDisplay(toDpy, screenNum));
1396 
1397     pDpyInfo->xTables[screenNum] = xTable =
1398       (short *)xmalloc(sizeof(short) * fromWidth);
1399     pDpyInfo->yTables[screenNum] = yTable =
1400       (short *)xmalloc(sizeof(short) * fromHeight);
1401 
1402     debug_cmpreg("fromWidth/Height: %d/%d, toWidth/Height: %d/%d\n",
1403 		    fromWidth, fromHeight, toWidth, toHeight);
1404     if (compRegRight == 0)
1405 	    compRegRight = fromWidth;
1406     if (compRegLow == 0)
1407 	    compRegLow = fromHeight;
1408     if (noScale) {
1409         /* TODO:
1410             - the fake tables should be built as "starting ignored", 1:1 map
1411               region and "ending ignored".  Then the rest of the code would
1412               need to be taught to disallow mouse movements in the two ignored
1413               areas.  This would stop the mouse wrap-around that the simple
1414               tables below result in.
1415         */
1416 
1417         /* fake vertical conversion table */
1418         for (counter = 0; counter < fromHeight; ++counter)
1419           yTable[counter] = counter % (toHeight - 1);
1420 
1421         /* fake horizontal conversion table entries */
1422         for (counter = 0; counter < fromWidth; ++counter)
1423           xTable[counter] = counter % (toWidth - 1);
1424     } else {
1425         /* vertical conversion table */
1426         for (counter = 0; counter < fromHeight; ++counter)
1427           yTable[counter] = (counter < compRegUp || counter > compRegLow) ?
1428                    100 :
1429                    (counter - compRegUp) * toHeight / (compRegLow - compRegUp);
1430 
1431         /* vertical conversion table */
1432         for (counter = 0; counter < fromWidth; ++counter)
1433           xTable[counter] = (counter < compRegLeft || counter > compRegRight) ?
1434                    100 :
1435                    (counter - compRegLeft) * toWidth /
1436                    (compRegRight - compRegLeft);
1437     }
1438 
1439     /* adjustment for boundaries */
1440     if (vertical) {
1441       if ((screenNum != 0) || (doEdge == EDGE_SOUTH))
1442         yTable[0] = COORD_DECR;
1443       if (((screenNum + 1) < nScreens) || (doEdge == EDGE_NORTH)) {
1444         yTable[fromHeight - 1] = COORD_INCR;
1445         /* work-around for bug: on at least one tested screen, cursor
1446            never moved past fromWidth - 2  (I'll assume this might apply
1447            in the vertical case, too. --cpbs) */
1448         yTable[fromHeight - 2] = COORD_INCR;
1449       }
1450     } else {
1451       if ((screenNum != 0) || (doEdge == EDGE_EAST))
1452         xTable[0] = COORD_DECR;
1453       if (((screenNum + 1) < nScreens) || (doEdge == EDGE_WEST)) {
1454         xTable[fromWidth - 1] = COORD_INCR;
1455         /* work-around for bug: on at least one tested screen, cursor
1456            never moved past fromWidth - 2 */
1457         xTable[fromWidth - 2] = COORD_INCR;
1458       }
1459     }
1460 
1461   } /* END for screenNum */
1462 
1463   free(heights);
1464   free(widths);
1465 
1466   /* always create propWin for events from toDpy */
1467   propWin = XCreateWindow(toDpy, toRoot, 0, 0, 1, 1, 0, 0, InputOutput,
1468                           CopyFromParent, 0, NULL);
1469   pDpyInfo->toDpyXtra.propWin = propWin;
1470   debug("Create window %x on todpy\n", (unsigned int)propWin);
1471   /* initialize pointer mapping */
1472   RefreshPointerMapping(toDpy, pDpyInfo);
1473 
1474   if (doSel) {
1475     pDpyInfo->sDpy = NULL;
1476     pDpyInfo->sTime = 0;
1477 
1478     pDpyInfo->fromDpyXtra.otherDpy   = toDpy;
1479     pDpyInfo->fromDpyXtra.sState     = SELSTATE_OFF;
1480 #ifdef WIN_2_X
1481   if (fromDpy != fromWin) {
1482 #endif
1483     pDpyInfo->fromDpyXtra.pingAtom   = XInternAtom(fromDpy, pingStr, False);
1484 #ifdef WIN_2_X
1485   }
1486 #endif
1487     pDpyInfo->fromDpyXtra.pingInProg = False;
1488     pDpyInfo->fromDpyXtra.propWin    = trigger;
1489     eventMask |= PropertyChangeMask;
1490 
1491     pDpyInfo->toDpyXtra.otherDpy     = fromDpy;
1492     pDpyInfo->toDpyXtra.sState       = SELSTATE_OFF;
1493     pDpyInfo->toDpyXtra.pingAtom     = XInternAtom(toDpy, pingStr, False);
1494     pDpyInfo->toDpyXtra.pingInProg   = False;
1495 #ifdef WIN_2_X
1496     if (fromDpy != fromWin)
1497 #endif
1498       XSelectInput(toDpy, propWin, PropertyChangeMask);
1499     XSetSelectionOwner(toDpy, XA_PRIMARY, propWin, CurrentTime);
1500 #ifdef WIN_2_X
1501     debug("SelectionOwner to propWin %x\n", (unsigned int)propWin);
1502     pDpyInfo->owntoXsel = 1;
1503     pDpyInfo->expectSelNotify = 0;
1504     pDpyInfo->expectOwnClip = 0;
1505     pDpyInfo->winSSave = 0;
1506 #endif
1507   } /* END if doSel */
1508 
1509   if (doResurface) /* get visibility events */
1510     eventMask |= VisibilityChangeMask;
1511 
1512 #ifdef WIN_2_X
1513   if (fromDpy != fromWin) {
1514 #endif
1515   XSelectInput(fromDpy, trigger, eventMask);
1516   pDpyInfo->eventMask = eventMask; /* save for future munging */
1517   if (doSel) XSetSelectionOwner(fromDpy, XA_PRIMARY, trigger, CurrentTime);
1518   XMapRaised(fromDpy, trigger);
1519   DrawWindowText(pDpyInfo);
1520 #ifdef WIN_2_X
1521   }
1522 #endif
1523 
1524   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext)
1525     XTestGrabControl(pShadow->dpy, True); /* impervious to grabs! */
1526 
1527   pDpyInfo->selWinTo = None;
1528   pDpyInfo->selRevTo = 0;
1529   pDpyInfo->selWinFrom = None;
1530   pDpyInfo->selRevFrom = 0;
1531   pDpyInfo->signal = 0;
1532 
1533 } /* END InitDpyInfo */
1534 
1535 static void DoWakeUp(Display *dpy)
1536 {
1537   CARD16 state;
1538   BOOL onoff;
1539   int dummy;
1540 
1541   if (!DPMSQueryExtension(dpy, &dummy, &dummy))
1542     return;
1543 
1544   if (!DPMSInfo(dpy, &state, &onoff))
1545     return;
1546 
1547   if (!onoff)
1548     return;
1549 
1550   switch (state) {
1551   case DPMSModeOn:
1552     return;
1553   default:
1554     break;
1555   }
1556 
1557 #ifdef DEBUG
1558   printf("DMPS Wakup\n");
1559 #endif
1560   DPMSForceLevel(dpy, DPMSModeOn);
1561 }
1562 
1563 /*
1564  * Be sure that on all displays the same keyboard state
1565  * is active, therefore check for CapsLock and NumLock,
1566  * compare, and if required change the state on the
1567  * shadowed displays.
1568  */
1569 static void KeyboardState(Display *dpy)
1570 {
1571   PSHADOW pShadow;
1572   XKeyboardState toState;
1573 
1574   XGetKeyboardControl(dpy, &toState);
1575 #ifdef DEBUG
1576   printf("  LED mask = %lx\n", toState.led_mask);
1577 #endif
1578 
1579   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
1580     KeyCode keycode;
1581     XKeyboardState shState;
1582 
1583     XGetKeyboardControl(pShadow->dpy, &shState);
1584 
1585     if (toState.led_mask == shState.led_mask)
1586       continue;
1587 
1588     pShadow->led_mask = shState.led_mask;
1589 
1590     if ((toState.led_mask & 1) != (shState.led_mask & 1) &&
1591 	(keycode = XKeysymToKeycode(pShadow->dpy, XK_Caps_Lock))) {
1592       XTestFakeKeyEvent(pShadow->dpy, keycode, True, 0);
1593       XTestFakeKeyEvent(pShadow->dpy, keycode, False, 0);
1594       pShadow->flush = True;
1595     }
1596 
1597     if ((toState.led_mask & 2) != (shState.led_mask & 2) &&
1598 	(keycode = XKeysymToKeycode(pShadow->dpy, XK_Num_Lock))) {
1599       XTestFakeKeyEvent(pShadow->dpy, keycode, True, 0);
1600       XTestFakeKeyEvent(pShadow->dpy, keycode, False, 0);
1601       pShadow->flush = True;
1602     }
1603 
1604     if (pShadow->flush) {
1605       XFlush(pShadow->dpy);
1606       pShadow->flush = False;
1607     }
1608   }
1609 }
1610 
1611 static void RestoreKeyboardState(void)
1612 {
1613   PSHADOW pShadow;
1614 
1615   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
1616     KeyCode keycode;
1617     XKeyboardState shState;
1618 
1619     XGetKeyboardControl(pShadow->dpy, &shState);
1620 
1621 #ifdef DEBUG
1622     printf("  LED mask = %lx(%lx)\n", shState.led_mask, pShadow->led_mask);
1623 #endif
1624 
1625     if (pShadow->led_mask == shState.led_mask)
1626       continue;
1627 
1628     if ((pShadow->led_mask & 1) != (shState.led_mask & 1) &&
1629 	(keycode = XKeysymToKeycode(pShadow->dpy, XK_Caps_Lock))) {
1630       XTestFakeKeyEvent(pShadow->dpy, keycode, True, 0);
1631       XTestFakeKeyEvent(pShadow->dpy, keycode, False, 0);
1632       pShadow->flush = True;
1633     }
1634 
1635     if ((pShadow->led_mask & 2) != (shState.led_mask & 2) &&
1636 	(keycode = XKeysymToKeycode(pShadow->dpy, XK_Num_Lock))) {
1637       XTestFakeKeyEvent(pShadow->dpy, keycode, True, 0);
1638       XTestFakeKeyEvent(pShadow->dpy, keycode, False, 0);
1639       pShadow->flush = True;
1640     }
1641 
1642     if (pShadow->flush) {
1643       XFlush(pShadow->dpy);
1644       pShadow->flush = False;
1645     }
1646   }
1647 }
1648 
1649 static int bad_window_handler(Display *disp, XErrorEvent *err)
1650 {
1651   return 0;
1652 }
1653 
1654 static void DoDPMSForceLevel(pShadow, level)
1655 PSHADOW pShadow;
1656 CARD16 level;
1657 {
1658   /* Do a DPMSForceLevel(), but only if the display supports it */
1659   if (pShadow->DPMSstatus == -1) {
1660     /* Need to see if this display supports the DPMS extension.
1661      * If it doesn't then trying DPMSForceLevel() will display
1662      * a spurious error message to stderr.
1663      */
1664     int t1, t2;
1665     if (DPMSQueryExtension(pShadow->dpy, &t1, &t2))
1666       pShadow->DPMSstatus = 1;
1667     else
1668       pShadow->DPMSstatus = 0;
1669   }
1670 
1671   if (pShadow->DPMSstatus != 0)
1672     DPMSForceLevel(pShadow->dpy, level);
1673 }
1674 
1675 static void DoConnect(pDpyInfo)
1676 PDPYINFO pDpyInfo;
1677 {
1678   Display *fromDpy = pDpyInfo->fromDpy;
1679   Window   trigger = pDpyInfo->trigger;
1680   PSHADOW pShadow;
1681 
1682   if (pDpyInfo->signal)
1683     return;
1684 
1685   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
1686     DoDPMSForceLevel(pShadow, DPMSModeOn);
1687     XFlush(pShadow->dpy);
1688   }
1689 
1690   debug("connecting\n");
1691   pDpyInfo->mode = X2X_CONNECTED;
1692 
1693 #ifdef WIN_2_X
1694   assert (fromDpy != fromWin);
1695 #endif
1696 
1697   XGetInputFocus(fromDpy, &pDpyInfo->selWinFrom, &pDpyInfo->selRevFrom);
1698   XSetInputFocus(fromDpy, PointerRoot, 0, CurrentTime);
1699   XSync(fromDpy, False);
1700 
1701   XFlush(pDpyInfo->toDpy);
1702 
1703   if (pDpyInfo->selWinTo != None) {
1704     Display  *toDpy = pDpyInfo->toDpy;
1705     Window selWinTo = pDpyInfo->selWinTo;
1706     int    selRevTo = pDpyInfo->selRevTo;
1707     XErrorHandler old_handler = XSetErrorHandler(bad_window_handler);
1708 
1709     XSetInputFocus(toDpy, selWinTo, selRevTo, CurrentTime);
1710     XSync (toDpy, False);
1711     (void) XSetErrorHandler(old_handler);
1712   }
1713 
1714   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext)
1715     DoWakeUp(pShadow->dpy);
1716 
1717   if (doAutoUp)
1718     KeyboardState(fromDpy);
1719 
1720   XSync(fromDpy, False);
1721 
1722   if (pDpyInfo->big != None) XMapRaised(fromDpy, pDpyInfo->big);
1723   XGrabPointer(fromDpy, trigger, True,
1724                PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
1725                GrabModeAsync, GrabModeAsync,
1726                None, pDpyInfo->grabCursor, CurrentTime);
1727   XGrabKeyboard(fromDpy, trigger, True,
1728                 GrabModeAsync, GrabModeAsync,
1729                 CurrentTime);
1730   XSelectInput(fromDpy, trigger, pDpyInfo->eventMask | PointerMotionMask);
1731 
1732   XSync(fromDpy, False);
1733 
1734 } /* END DoConnect */
1735 
1736 static void DoDisconnect(pDpyInfo)
1737 PDPYINFO pDpyInfo;
1738 {
1739   Display *fromDpy = pDpyInfo->fromDpy;
1740   Display   *toDpy = pDpyInfo->toDpy;
1741   PDPYXTRA pDpyXtra;
1742 
1743   debug("disconnecting\n");
1744   pDpyInfo->mode = X2X_DISCONNECTED;
1745 #ifdef WIN_2_X
1746   assert (fromDpy != fromWin);
1747 #endif
1748 
1749   XGetInputFocus(toDpy, &pDpyInfo->selWinTo, &pDpyInfo->selRevTo);
1750   XSetInputFocus(toDpy, PointerRoot, 0, CurrentTime);
1751   XSync(toDpy, False);
1752 
1753   XFlush(fromDpy);
1754 
1755   if (pDpyInfo->selWinFrom != None) {
1756     Display  *fromDpy = pDpyInfo->fromDpy;
1757     Window selWinFrom = pDpyInfo->selWinFrom;
1758     int    selRevFrom = pDpyInfo->selRevFrom;
1759     XErrorHandler old_handler = XSetErrorHandler(bad_window_handler);
1760 
1761     XSetInputFocus(fromDpy, selWinFrom, selRevFrom, CurrentTime);
1762     XSync (fromDpy, False);
1763     (void) XSetErrorHandler(old_handler);
1764   }
1765 
1766   if (pDpyInfo->big != None) XUnmapWindow(fromDpy, pDpyInfo->big);
1767   XUngrabKeyboard(fromDpy, CurrentTime);
1768   XUngrabPointer(fromDpy, CurrentTime);
1769   XSelectInput(fromDpy, pDpyInfo->trigger, pDpyInfo->eventMask);
1770 
1771   if (doSel) {
1772     pDpyXtra = GETDPYXTRA(fromDpy, pDpyInfo);
1773     if (pDpyXtra->sState == SELSTATE_ON) {
1774       XSetSelectionOwner(fromDpy, XA_PRIMARY, pDpyXtra->propWin, CurrentTime);
1775     }
1776   } /* END if */
1777 
1778   XSync(fromDpy, False);
1779 
1780   /* force normal state on to display: */
1781   if (doAutoUp) {
1782     FakeThingsUp(pDpyInfo);
1783     RestoreKeyboardState();
1784   }
1785 
1786 } /* END DoDisconnect */
1787 
1788 static void RegisterEventHandlers(pDpyInfo)
1789 PDPYINFO pDpyInfo;
1790 {
1791   Display *fromDpy = pDpyInfo->fromDpy;
1792   Window  trigger = pDpyInfo->trigger;
1793   Display *toDpy;
1794   Window  propWin;
1795 
1796 #define XSAVECONTEXT(A, B, C, D) XSaveContext(A, B, C, (XPointer)(D))
1797 
1798 #ifdef WIN_2_X
1799   if (fromDpy != fromWin) {
1800 #endif
1801   XSAVECONTEXT(fromDpy, trigger, MotionNotify,    ProcessMotionNotify);
1802   XSAVECONTEXT(fromDpy, trigger, Expose,          ProcessExpose);
1803   XSAVECONTEXT(fromDpy, trigger, EnterNotify,     ProcessEnterNotify);
1804   XSAVECONTEXT(fromDpy, trigger, ButtonPress,     ProcessButtonPress);
1805   XSAVECONTEXT(fromDpy, trigger, ButtonRelease,   ProcessButtonRelease);
1806   XSAVECONTEXT(fromDpy, trigger, KeyPress,        ProcessKeyEvent);
1807   XSAVECONTEXT(fromDpy, trigger, KeyRelease,      ProcessKeyEvent);
1808   XSAVECONTEXT(fromDpy, trigger, ConfigureNotify, ProcessConfigureNotify);
1809   XSAVECONTEXT(fromDpy, trigger, ClientMessage,   ProcessClientMessage);
1810   XSAVECONTEXT(fromDpy, None,    MappingNotify,   ProcessMapping);
1811 
1812 
1813   if (doResurface)
1814     XSAVECONTEXT(fromDpy, trigger, VisibilityNotify, ProcessVisibility);
1815 #ifdef WIN_2_X
1816   }
1817 #endif
1818 
1819   toDpy = pDpyInfo->toDpy;
1820   propWin = pDpyInfo->toDpyXtra.propWin;
1821   XSAVECONTEXT(toDpy, None, MappingNotify, ProcessMapping);
1822 
1823   if (doSel) {
1824 #ifdef WIN_2_X
1825     if (fromDpy != fromWin) {
1826 #endif
1827     XSAVECONTEXT(fromDpy, trigger, SelectionRequest, ProcessSelectionRequest);
1828     XSAVECONTEXT(fromDpy, trigger, PropertyNotify,   ProcessPropertyNotify);
1829     XSAVECONTEXT(fromDpy, trigger, SelectionNotify,  ProcessSelectionNotify);
1830     XSAVECONTEXT(fromDpy, trigger, SelectionClear,   ProcessSelectionClear);
1831     XSAVECONTEXT(toDpy,   propWin, SelectionRequest, ProcessSelectionRequest);
1832     XSAVECONTEXT(toDpy,   propWin, PropertyNotify,   ProcessPropertyNotify);
1833     XSAVECONTEXT(toDpy,   propWin, SelectionNotify,  ProcessSelectionNotify);
1834     XSAVECONTEXT(toDpy,   propWin, SelectionClear,   ProcessSelectionClear);
1835 #ifdef WIN_2_X
1836     } else {
1837       XSAVECONTEXT(toDpy, propWin, SelectionRequest, ProcessSelectionRequestW);
1838       // XSAVECONTEXT(toDpy, propWin, PropertyNotify,  ProcessPropertyNotifyW);
1839       XSAVECONTEXT(toDpy, propWin, SelectionNotify,ProcessSelectionNotifyW);
1840       XSAVECONTEXT(toDpy, propWin, SelectionClear,   ProcessSelectionClearW);
1841     }
1842 #endif
1843   } /* END if doSel */
1844 
1845 } /* END RegisterEventHandlers */
1846 
1847 static Bool ProcessEvent(dpy, pDpyInfo)
1848 Display  *dpy;
1849 PDPYINFO pDpyInfo;
1850 {
1851   XEvent    ev;
1852   XAnyEvent *pEv = (XAnyEvent *)&ev;
1853   HANDLER   handler;
1854 
1855 #define XFINDCONTEXT(A, B, C, D) XFindContext(A, B, C, (XPointer *)(D))
1856 
1857   XNextEvent(dpy, &ev);
1858   handler = 0;
1859   if ((!XFINDCONTEXT(dpy, pEv->window, pEv->type, &handler)) ||
1860       (!XFINDCONTEXT(dpy, None, pEv->type, &handler))) {
1861     /* have handler */
1862     return ((*handler)(dpy, pDpyInfo, &ev));
1863   } else {
1864     debug("no handler for window 0x%x, event type %d\n",
1865            (unsigned int)pEv->window, pEv->type);
1866   } /* END if/else */
1867 
1868   return False;
1869 
1870 } /* END ProcessEvent */
1871 
1872 static Bool ProcessMotionNotify(unused, pDpyInfo, pEv)
1873 Display  *unused;
1874 PDPYINFO pDpyInfo;
1875 XMotionEvent *pEv; /* caution: might be pseudo-event!!! */
1876 {
1877   /* Note: ProcessMotionNotify is sometimes called from inside x2x to
1878    *       simulate a motion event.  Any new references to pEv fields
1879    *       must be checked carefully!
1880    */
1881 
1882   int       toScreenNum;
1883   PSHADOW   pShadow;
1884   int       toCoord, fromCoord, delta;
1885   Display   *fromDpy;
1886   Bool      bAbortedDisconnect;
1887   Bool      vert;
1888 
1889   vert = pDpyInfo->vertical;
1890 
1891   /* find the screen */
1892   toScreenNum = pDpyInfo->toScreen;
1893   fromCoord = vert ? pEv->y_root : pEv->x_root;
1894 
1895   /* check to make sure the cursor is still on the from screen */
1896   if (!(pEv->same_screen)) {
1897     toCoord = (pDpyInfo->lastFromCoord < fromCoord) ? COORD_DECR : COORD_INCR;
1898   } else {
1899     toCoord = (vert?pDpyInfo->yTables:pDpyInfo->xTables)[toScreenNum][fromCoord];
1900 
1901     /* sanity check motion: necessary for nondeterminism surrounding warps */
1902     delta = pDpyInfo->lastFromCoord - fromCoord;
1903     if (delta < 0) delta = -delta;
1904     if (delta > pDpyInfo->unreasonableDelta) return False;
1905   }
1906 
1907   if (SPECIAL_COORD(toCoord) != 0) { /* special coordinate */
1908     bAbortedDisconnect = False;
1909     if (toCoord == COORD_INCR) {
1910       if (toScreenNum != (pDpyInfo->nScreens - 1)) { /* next screen */
1911         toScreenNum = ++(pDpyInfo->toScreen);
1912         fromCoord = pDpyInfo->fromIncrCoord;
1913         toCoord = (vert?pDpyInfo->yTables:pDpyInfo->xTables)[toScreenNum][fromCoord];
1914       } else { /* disconnect! */
1915         if (doBtnBlock &&
1916             (pEv->state & (Button1Mask | Button2Mask | Button3Mask |
1917                            Button4Mask | Button5Mask)))
1918           bAbortedDisconnect = True;
1919         else {
1920           DoDisconnect(pDpyInfo);
1921           fromCoord = pDpyInfo->fromDiscCoord;
1922         }
1923         toCoord = (vert?pDpyInfo->yTables:pDpyInfo->xTables)[toScreenNum][pDpyInfo->fromConnCoord];
1924       }
1925     } else { /* DECR */
1926       if (toScreenNum != 0) { /* previous screen */
1927         toScreenNum = --(pDpyInfo->toScreen);
1928         fromCoord = pDpyInfo->fromDecrCoord;
1929         toCoord = (vert?pDpyInfo->yTables:pDpyInfo->xTables)[toScreenNum][fromCoord];
1930       } else { /* disconnect! */
1931         if (doBtnBlock &&
1932             (pEv->state & (Button1Mask | Button2Mask | Button3Mask |
1933                            Button4Mask | Button5Mask)))
1934           bAbortedDisconnect = True;
1935         else {
1936           DoDisconnect(pDpyInfo);
1937           fromCoord = pDpyInfo->fromDiscCoord;
1938         }
1939         toCoord = (vert?pDpyInfo->yTables:pDpyInfo->xTables)[toScreenNum][pDpyInfo->fromConnCoord];
1940       }
1941     } /* END if toCoord */
1942     if (!bAbortedDisconnect) {
1943       fromDpy = pDpyInfo->fromDpy;
1944       XWarpPointer(fromDpy, None, pDpyInfo->root, 0, 0, 0, 0,
1945                    vert ? pEv->x_root : fromCoord,
1946                    vert ? fromCoord : pEv->y_root);
1947       XFlush(fromDpy);
1948     }
1949   } /* END if SPECIAL_COORD */
1950   pDpyInfo->lastFromCoord = fromCoord;
1951 
1952   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
1953     if (doDpmsMouse)
1954     {
1955       DoDPMSForceLevel(pShadow, DPMSModeOn);
1956     }
1957 
1958 #ifdef DEBUG_COMPLREG
1959     static unsigned lc;
1960 
1961     if (lc++ % 10 == 0)
1962       debug_cmpreg("sj: Call XTestFakeMotionEvent %d/%d to %d/%d\n",
1963                       pEv->x_root,
1964                       pEv->y_root,
1965                       pDpyInfo->xTables[toScreenNum][pEv->x_root],
1966                       pDpyInfo->yTables[toScreenNum][pEv->y_root]);
1967 #endif
1968 
1969     XTestFakeMotionEvent(pShadow->dpy, toScreenNum,
1970                       vert?pDpyInfo->xTables[toScreenNum][pEv->x_root]:toCoord,
1971                       vert?toCoord:pDpyInfo->yTables[toScreenNum][pEv->y_root],
1972                       0);
1973     XFlush(pShadow->dpy);
1974     pShadow->flush = False;
1975   } /* END for */
1976 
1977   return False;
1978 
1979 } /* END ProcessMotionNotify */
1980 
1981 static Bool ProcessExpose(dpy, pDpyInfo, pEv)
1982 Display  *dpy;
1983 PDPYINFO pDpyInfo;
1984 XExposeEvent *pEv;
1985 {
1986   XClearWindow(pDpyInfo->fromDpy, pDpyInfo->trigger);
1987   DrawWindowText(pDpyInfo);
1988   return False;
1989 
1990 } /* END ProcessExpose */
1991 
1992 static void DrawWindowText(pDpyInfo)
1993 PDPYINFO pDpyInfo;
1994 {
1995   if (pDpyInfo->fid == 0)
1996     return;
1997 
1998   XDrawImageString(pDpyInfo->fromDpy, pDpyInfo->trigger, pDpyInfo->textGC,
1999 		   MAX(0,((pDpyInfo->width - pDpyInfo->twidth) / 2)),
2000 		   MAX(0,((pDpyInfo->height - pDpyInfo->theight) / 2)) +
2001 		   pDpyInfo->tascent, label, strlen(label));
2002 }
2003 static Bool ProcessEnterNotify(dpy, pDpyInfo, pEv)
2004 Display  *dpy;
2005 PDPYINFO pDpyInfo;
2006 XCrossingEvent *pEv;
2007 {
2008   Display *fromDpy = pDpyInfo->fromDpy;
2009   XMotionEvent xmev;
2010 
2011   if (pEv->x_root < compRegLeft)
2012 	  pEv->x_root = compRegLeft;
2013   if (pEv->x_root > compRegRight)
2014 	  pEv->x_root = compRegRight;
2015   if (pEv->y_root < compRegUp)
2016 	  pEv->y_root = compRegUp;
2017   if (pEv->y_root > compRegLow)
2018 	  pEv->y_root = compRegLow;
2019 
2020   if ((pEv->mode == NotifyNormal) &&
2021       (pDpyInfo->mode == X2X_DISCONNECTED) && (dpy == pDpyInfo->fromDpy)) {
2022     DoConnect(pDpyInfo);
2023     if (pDpyInfo->vertical) {
2024       XWarpPointer(fromDpy, None, pDpyInfo->root, 0, 0, 0, 0,
2025                    pEv->x_root, pDpyInfo->fromConnCoord);
2026       xmev.x_root = pEv->x_root;
2027       xmev.y_root = pDpyInfo->lastFromCoord = pDpyInfo->fromConnCoord;
2028     } else {
2029       XWarpPointer(fromDpy, None, pDpyInfo->root, 0, 0, 0, 0,
2030                    pDpyInfo->fromConnCoord, pEv->y_root);
2031       xmev.x_root = pDpyInfo->lastFromCoord = pDpyInfo->fromConnCoord;
2032       xmev.y_root = pEv->y_root;
2033     }
2034     xmev.same_screen = True;
2035     ProcessMotionNotify(NULL, pDpyInfo, &xmev);
2036   }  /* END if NotifyNormal... */
2037   return False;
2038 
2039 } /* END ProcessEnterNotify */
2040 
2041 static Bool ProcessButtonPress(dpy, pDpyInfo, pEv)
2042 Display  *dpy;
2043 PDPYINFO pDpyInfo;
2044 XButtonEvent *pEv;
2045 {
2046   int state;
2047   PSHADOW   pShadow;
2048   unsigned int toButton;
2049 
2050   KeySym  keysym;
2051   KeyCode keycode;
2052   int     eventno;
2053 
2054   switch (pDpyInfo->mode) {
2055   case X2X_DISCONNECTED:
2056     pDpyInfo->mode = X2X_AWAIT_RELEASE;
2057     debug("awaiting button release before connecting\n");
2058     break;
2059   case X2X_CONNECTED:
2060     debug("Got button %d, max is %d (%d)\n", pEv->button, N_BUTTONS, nButtons);
2061     if ((pEv->button <= N_BUTTONS) &&
2062         (buttonmap[pEv->button][0] != NoSymbol))
2063     {
2064       debug("Mapped!\n");
2065       for (pShadow = shadows; pShadow; pShadow = pShadow->pNext)
2066       {
2067         debug("Button %d is mapped, sending keys: ", pEv->button);
2068         for (eventno = 0;
2069              (keysym = buttonmap[pEv->button][eventno]) != NoSymbol;
2070              eventno++)
2071         {
2072           if ((keycode = XKeysymToKeycode(pShadow->dpy, keysym))) {
2073             XTestFakeKeyEvent(pShadow->dpy, keycode, True, 0);
2074             XTestFakeKeyEvent(pShadow->dpy, keycode, False, 0);
2075             XFlush(pShadow->dpy);
2076             debug(" (0x%04X)", keycode);
2077           }
2078           else
2079             debug(" (no code)");
2080         }
2081         debug("\n");
2082       }
2083     } else if (pEv->button <= nButtons) {
2084       toButton = pDpyInfo->inverseMap[pEv->button];
2085       for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
2086         XTestFakeButtonEvent(pShadow->dpy, toButton, True, 0);
2087         debug("from button %d down, to button %d down\n", pEv->button,toButton);
2088         XFlush(pShadow->dpy);
2089 	pShadow->flush = False;
2090       } /* END for */
2091       if (doAutoUp)
2092         FakeAction(pDpyInfo, FAKE_BUTTON, toButton, True);
2093     }
2094     if (doEdge) break;
2095 
2096     /* check if more than one button pressed */
2097     state = pEv->state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
2098     switch (pEv->button) {
2099     case Button1: state &= ~Button1Mask; break;
2100     case Button2: state &= ~Button2Mask; break;
2101     case Button3: state &= ~Button3Mask; break;
2102     case Button4: state &= ~Button4Mask; break;
2103     case Button5: state &= ~Button5Mask; break;
2104     default:
2105       debug("unknown button %d\n", pEv->button);
2106       break;
2107     } /* END switch button */
2108     if (state) { /* then more than one button pressed */
2109       debug("awaiting button release before disconnecting\n");
2110       pDpyInfo->mode = X2X_CONN_RELEASE;
2111     }
2112     break;
2113   default:
2114     break;
2115   } /* END switch mode */
2116   return False;
2117 } /* END ProcessButtonPress */
2118 
2119 static Bool ProcessButtonRelease(dpy, pDpyInfo, pEv)
2120 Display  *dpy;
2121 PDPYINFO pDpyInfo;
2122 XButtonEvent *pEv;
2123 {
2124   int state;
2125   PSHADOW   pShadow;
2126   XMotionEvent xmev;
2127   unsigned int toButton;
2128 
2129   if ((pDpyInfo->mode == X2X_CONNECTED) ||
2130       (pDpyInfo->mode == X2X_CONN_RELEASE)) {
2131     if ((pEv->button <= nButtons) &&
2132         (buttonmap[pEv->button][0] == NoSymbol))
2133       // Do not process button release if it was mapped to keys
2134     {
2135       toButton = pDpyInfo->inverseMap[pEv->button];
2136       for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
2137         XTestFakeButtonEvent(pShadow->dpy, toButton, False, 0);
2138         debug("from button %d up, to button %d up\n", pEv->button, toButton);
2139         XFlush(pShadow->dpy);
2140       } /* END for */
2141       if (doAutoUp)
2142         FakeAction(pDpyInfo, FAKE_BUTTON, toButton, False);
2143     }
2144   } /* END if */
2145 
2146   if (doEdge) return False;
2147   if ((pDpyInfo->mode == X2X_AWAIT_RELEASE) ||
2148       (pDpyInfo->mode == X2X_CONN_RELEASE)) {
2149     /* make sure that all buttons are released */
2150     state = pEv->state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
2151     switch (pEv->button) {
2152     case Button1: state &= ~Button1Mask; break;
2153     case Button2: state &= ~Button2Mask; break;
2154     case Button3: state &= ~Button3Mask; break;
2155     case Button4: state &= ~Button4Mask; break;
2156     case Button5: state &= ~Button5Mask; break;
2157     default:
2158       debug("unknown button %d\n", pEv->button);
2159       break;
2160     } /* END switch button */
2161     if (!state) { /* all buttons up: time to (dis)connect */
2162       if (pDpyInfo->mode == X2X_AWAIT_RELEASE) { /* connect */
2163         DoConnect(pDpyInfo);
2164         if (pDpyInfo->vertical) {
2165           xmev.x_root = pEv->x_root;
2166           xmev.y_root = pDpyInfo->lastFromCoord = pEv->y_root;
2167         } else {
2168           xmev.x_root = pDpyInfo->lastFromCoord = pEv->x_root;
2169           xmev.y_root = pEv->y_root;
2170         }
2171         xmev.same_screen = True;
2172         ProcessMotionNotify(NULL, pDpyInfo, &xmev);
2173       } else { /* disconnect */
2174         DoDisconnect(pDpyInfo);
2175       } /* END if mode */
2176     } /* END if !state */
2177   } /* END if mode */
2178   return False;
2179 
2180 } /* END ProcessButtonRelease */
2181 
2182 static Bool ProcessKeyEvent(dpy, pDpyInfo, pEv)
2183 Display  *dpy;
2184 PDPYINFO pDpyInfo;
2185 XKeyEvent *pEv;
2186 {
2187   KeyCode   keycode;
2188   KeySym    keysym;
2189   PSHADOW   pShadow;
2190   Bool      bPress;
2191   PSTICKY   pSticky;
2192   Bool      DoFakeShift = False;
2193   KeyCode   toShiftCode;
2194 
2195   keysym = XkbKeycodeToKeysym(pDpyInfo->fromDpy, pEv->keycode, 0, 0);
2196   bPress = (pEv->type == KeyPress);
2197 
2198 #ifdef DEBUG
2199   printf("key '%s' %s (state=0x%x)\n",
2200 	XKeysymToString(keysym), (bPress ? "pressed" : "released"), pEv->state);
2201 #endif
2202 
2203   /* If CapsLock is on, we need to do some funny business to make sure the */
2204   /* "to" display does the right thing */
2205   if(doCapsLkHack && (pEv->state & 0x2))
2206   {
2207     /* Throw away any explicit shift events (they're faked as neccessary) */
2208     if((keysym == XK_Shift_L) || (keysym == XK_Shift_R)) return False;
2209 
2210       /* If the shift key is pressed, do the shift, unless the keysym */
2211       /* is an alpha key, in which case we invert the shift logic */
2212       DoFakeShift = (pEv->state & 0x1);
2213       if(((keysym >= XK_A) && (keysym <= XK_Z)) ||
2214          ((keysym >= XK_a) && (keysym <= XK_z)))
2215         DoFakeShift = !DoFakeShift;
2216       debug("DoFakeShift %d\n", DoFakeShift);
2217     }
2218 
2219   for (pSticky = stickies; pSticky; pSticky = pSticky->pNext)
2220     if (keysym == pSticky->keysym)
2221       break;
2222 
2223   if (pSticky) {
2224     for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
2225       toShiftCode = XKeysymToKeycode(pShadow->dpy, XK_Shift_L);
2226       if ((keycode = XKeysymToKeycode(pShadow->dpy, keysym))) {
2227         if(DoFakeShift) XTestFakeKeyEvent(pShadow->dpy, toShiftCode, True, 0);
2228         XTestFakeKeyEvent(pShadow->dpy, keycode, True, 0);
2229         XTestFakeKeyEvent(pShadow->dpy, keycode, False, 0);
2230         if(DoFakeShift) XTestFakeKeyEvent(pShadow->dpy, toShiftCode, False, 0);
2231 	XFlush(pShadow->dpy);
2232 	pShadow->flush = False;
2233       } /* END if */
2234     } /* END for */
2235   } else {
2236     Bool invert = (pEv->state & 0x2) && (pEv->state & 0x1);
2237     for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
2238       toShiftCode = XKeysymToKeycode(pShadow->dpy, XK_Shift_L);
2239       if ((keycode = XKeysymToKeycode(pShadow->dpy, keysym))) {
2240 	if (invert && toShiftCode)
2241 	  XTestFakeKeyEvent(pShadow->dpy, toShiftCode, True, 0);
2242 	XTestFakeKeyEvent(pShadow->dpy, keycode, bPress, 0);
2243 	if (invert && toShiftCode)
2244 	  XTestFakeKeyEvent(pShadow->dpy, toShiftCode, False, 0);
2245 	XFlush(pShadow->dpy);
2246 	pShadow->flush = False;
2247       } /* END if */
2248     } /* END for */
2249     if (doAutoUp)
2250       FakeAction(pDpyInfo, FAKE_KEY, keysym, bPress);
2251   }
2252 
2253   return False;
2254 
2255 } /* END ProcessKeyEvent */
2256 
2257 static Bool ProcessConfigureNotify(dpy, pDpyInfo, pEv)
2258 Display  *dpy;
2259 PDPYINFO pDpyInfo;
2260 XConfigureEvent *pEv;
2261 {
2262   if (pDpyInfo->fid) {
2263     /* reposition text */
2264     pDpyInfo->width = pEv->width;
2265     pDpyInfo->height = pEv->height;
2266   } /* END if font */
2267   return False;
2268 
2269 } /* END ProcessConfigureNotify */
2270 
2271 static Bool ProcessClientMessage(dpy, pDpyInfo, pEv)
2272 Display  *dpy;
2273 PDPYINFO pDpyInfo;
2274 XClientMessageEvent *pEv;
2275 {
2276   /* terminate if atoms match! */
2277   return ((pEv->message_type == pDpyInfo->wmpAtom) &&
2278           (pEv->data.l[0]    == pDpyInfo->wmdwAtom));
2279 
2280 } /* END ProcessClientMessage */
2281 
2282 static Bool ProcessSelectionRequest(dpy, pDpyInfo, pEv)
2283 Display *dpy;
2284 PDPYINFO pDpyInfo;
2285 XSelectionRequestEvent *pEv;
2286 {
2287   PDPYXTRA pDpyXtra = GETDPYXTRA(dpy, pDpyInfo);
2288   Display *otherDpy;
2289   Atom utf8string;
2290 
2291   if (dpy == pDpyInfo->fromDpy) {
2292     utf8string = pDpyInfo->fromDpyUtf8String;
2293   } else {
2294     utf8string = pDpyInfo->toDpyUtf8String;
2295   }
2296 
2297     debug("selection request\n");
2298 
2299   /* bribe me to support more general selection requests,
2300      or send me the code to do it. */
2301   if ((pDpyXtra->sState != SELSTATE_ON) ||
2302       (pEv->selection != XA_PRIMARY) ||
2303       (pEv->target > XA_LAST_PREDEFINED && pEv->target != utf8string)) { /* bad request, punt request */
2304     pEv->property = None;
2305     SendSelectionNotify(pEv); /* blam! */
2306   } else {
2307     otherDpy = pDpyXtra->otherDpy;
2308     SendPing(otherDpy, GETDPYXTRA(otherDpy, pDpyInfo)); /* get started */
2309     if (pDpyInfo->sDpy) {
2310       /* nuke the old one */
2311       pDpyInfo->sEv.property = None;
2312       SendSelectionNotify(&(pDpyInfo->sEv)); /* blam! */
2313     } /* END if InProg */
2314     pDpyInfo->sDpy  = otherDpy;
2315     pDpyInfo->sEv = *pEv;
2316   } /* END if relaySel */
2317   return False;
2318 
2319 } /* END ProcessSelectionRequest */
2320 
2321 static void SendPing(dpy, pDpyXtra)
2322 Display *dpy;
2323 PDPYXTRA pDpyXtra;
2324 {
2325   if (!(pDpyXtra->pingInProg)) {
2326     XChangeProperty(dpy, pDpyXtra->propWin, pDpyXtra->pingAtom, XA_PRIMARY,
2327                     8, PropModeAppend, NULL, 0);
2328     pDpyXtra->pingInProg = True;
2329   } /* END if */
2330 } /* END SendPing */
2331 
2332 static Bool ProcessPropertyNotify(dpy, pDpyInfo, pEv)
2333 Display *dpy;
2334 PDPYINFO pDpyInfo;
2335 XPropertyEvent *pEv;
2336 {
2337   Atom target;
2338   PDPYXTRA pDpyXtra = GETDPYXTRA(dpy, pDpyInfo);
2339 
2340   debug("property notify\n");
2341 
2342   if (pEv->atom == pDpyXtra->pingAtom) { /* acking a ping */
2343     pDpyXtra->pingInProg = False;
2344     if (pDpyXtra->sState == SELSTATE_WAIT) {
2345       pDpyXtra->sState = SELSTATE_ON;
2346       XSetSelectionOwner(dpy, XA_PRIMARY, pDpyXtra->propWin, pEv->time);
2347       XSync(dpy, False);
2348     } else if (dpy == pDpyInfo->sDpy) {
2349       if (pDpyInfo->sTime == pEv->time) {
2350         /* oops, need to ensure uniqueness */
2351         SendPing(dpy, pDpyXtra); /* try for another time stamp */
2352       } else {
2353         target = pDpyInfo->sEv.target;
2354         if (dpy == pDpyInfo->fromDpy && target == pDpyInfo->toDpyUtf8String) {
2355           target = pDpyInfo->fromDpyUtf8String;
2356         } else if (dpy == pDpyInfo->toDpy && target == pDpyInfo->fromDpyUtf8String) {
2357           target = pDpyInfo->toDpyUtf8String;
2358         }
2359 
2360         pDpyInfo->sTime = pEv->time;
2361         XConvertSelection(dpy, pDpyInfo->sEv.selection, target,
2362                           XA_PRIMARY, pDpyXtra->propWin, pEv->time);
2363       } /* END if ... ensure uniqueness */
2364     } /* END if sState... */
2365   } /* END if ping */
2366   return False;
2367 
2368 } /* END ProcessPropertyNotify */
2369 
2370 static Bool ProcessSelectionNotify(dpy, pDpyInfo, pEv)
2371 Display *dpy;
2372 PDPYINFO pDpyInfo;
2373 XSelectionEvent *pEv;
2374 {
2375   Atom type;
2376   int  format;
2377   unsigned long nitems, after;
2378   unsigned char *prop;
2379   Bool success;
2380   XSelectionRequestEvent *pSelReq;
2381   Atom utf8string;
2382 
2383 #define DEFAULT_PROP_SIZE 1024L
2384 
2385   debug("selection notify\n");
2386 
2387   if (dpy == pDpyInfo->fromDpy) {
2388     utf8string = pDpyInfo->fromDpyUtf8String;
2389   } else {
2390     utf8string = pDpyInfo->toDpyUtf8String;
2391   }
2392 
2393   if ((dpy == pDpyInfo->sDpy) && (pDpyInfo->sTime == pEv->time)) {
2394     success = False;
2395     /* corresponding select */
2396     if (XGetWindowProperty(dpy, pEv->requestor, XA_PRIMARY, 0L,
2397                            DEFAULT_PROP_SIZE, True, AnyPropertyType,
2398                            &type, &format, &nitems, &after, &prop)
2399         == Success) { /* got property */
2400       if ((type != None)
2401           && (format != None)
2402           && (nitems != 0)
2403           && (prop != None)
2404           /* known type */
2405           && (type <= XA_LAST_PREDEFINED || type == utf8string)) {
2406         if (after == 0L) { /* got everything */
2407           success = True;
2408         } else { /* try to get everything */
2409           XFree(prop);
2410           success =
2411             ((XGetWindowProperty(dpy, pEv->requestor, XA_PRIMARY, 0L,
2412                                  DEFAULT_PROP_SIZE + after + 1,
2413                                  True, AnyPropertyType,
2414                                  &type, &format, &nitems, &after, &prop)
2415               == Success) &&
2416              (type != None) && (format != None) && (nitems != 0) &&
2417              (after == 0L) && (prop != None));
2418         } /* END if got everything ... else ...*/
2419       } /* END if known type */
2420     } /* END if got property */
2421 
2422     pSelReq = &(pDpyInfo->sEv);
2423     if (success) { /* send bits to the requesting dpy/window */
2424       if (type == utf8string) {
2425         if (dpy == pDpyInfo->fromDpy) {
2426           type = pDpyInfo->toDpyUtf8String;
2427         } else {
2428           type = pDpyInfo->fromDpyUtf8String;
2429         }
2430       }
2431       XChangeProperty(pSelReq->display, pSelReq->requestor,
2432                       pSelReq->property, type, format, PropModeReplace,
2433                       prop, nitems);
2434       XFree(prop);
2435       SendSelectionNotify(pSelReq);
2436     } else {
2437       pSelReq->property = None;
2438       SendSelectionNotify(pSelReq);
2439     } /* END if success */
2440     pDpyInfo->sDpy = NULL;
2441   } /* END if corresponding select */
2442   return False;
2443 
2444 } /* END ProcessSelectionNotify */
2445 
2446 static void SendSelectionNotify(pSelReq)
2447 XSelectionRequestEvent *pSelReq;
2448 {
2449   XSelectionEvent sendEv;
2450 
2451   sendEv.type      = SelectionNotify;
2452   sendEv.serial    = 0;
2453   sendEv.send_event= pSelReq->send_event;
2454   sendEv.display   = pSelReq->display;
2455   sendEv.requestor = pSelReq->requestor;
2456   sendEv.selection = pSelReq->selection;
2457   sendEv.target    = pSelReq->target;
2458   sendEv.property  = pSelReq->property;
2459   sendEv.time      = pSelReq->time;
2460   XSendEvent(pSelReq->display, pSelReq->requestor, False, 0,
2461              (XEvent *)&sendEv);
2462 
2463 } /* END SendSelectionNotify */
2464 
2465 static Bool ProcessSelectionClear(dpy, pDpyInfo, pEv)
2466 Display *dpy;
2467 PDPYINFO pDpyInfo;
2468 XSelectionClearEvent *pEv;
2469 {
2470   Display  *otherDpy;
2471   PDPYXTRA pDpyXtra, pOtherXtra;
2472 
2473   debug("selection clear\n");
2474 
2475   if (pEv->selection == XA_PRIMARY) {
2476     /* track primary selection */
2477     pDpyXtra = GETDPYXTRA(dpy, pDpyInfo);
2478     pDpyXtra->sState = SELSTATE_OFF;
2479     otherDpy = pDpyXtra->otherDpy;
2480     pOtherXtra = GETDPYXTRA(otherDpy, pDpyInfo);
2481     pOtherXtra->sState = SELSTATE_WAIT;
2482     SendPing(otherDpy, pOtherXtra);
2483     if (pDpyInfo->sDpy) { /* nuke the selection in progress */
2484       pDpyInfo->sEv.property = None;
2485       SendSelectionNotify(&(pDpyInfo->sEv)); /* blam! */
2486       pDpyInfo->sDpy = NULL;
2487     } /* END if nuke */
2488   } /* END if primary */
2489   return False;
2490 
2491 } /* END ProcessSelectionClear */
2492 
2493 /**********
2494  * process a visibility event
2495  **********/
2496 static Bool ProcessVisibility(dpy, pDpyInfo, pEv)
2497 Display          *dpy;
2498 PDPYINFO         pDpyInfo;
2499 XVisibilityEvent *pEv;
2500 {
2501   /* might want to qualify, based on other messages.  otherwise,
2502      this code might cause a loop if two windows decide to fight
2503      it out for the top of the stack */
2504   if (pEv->state != VisibilityUnobscured)
2505     XRaiseWindow(dpy, pEv->window);
2506 
2507   return False;
2508 
2509 } /* END ProcessVisibility */
2510 
2511 /**********
2512  * process a keyboard mapping event
2513  **********/
2514 static Bool ProcessMapping(dpy, pDpyInfo, pEv)
2515 Display             *dpy;
2516 PDPYINFO            pDpyInfo;
2517 XMappingEvent       *pEv;
2518 {
2519   debug("mapping\n");
2520 
2521   switch (pEv->request) {
2522   case MappingModifier:
2523   case MappingKeyboard:
2524     XRefreshKeyboardMapping(pEv);
2525     break;
2526   case MappingPointer:
2527     RefreshPointerMapping(dpy, pDpyInfo);
2528     break;
2529   } /* END switch */
2530 
2531   return False;
2532 
2533 } /* END ProcessMapping */
2534 
2535 static void FakeAction(pDpyInfo, type, thing, bDown)
2536 PDPYINFO pDpyInfo;
2537 int type;
2538 KeySym thing;
2539 Bool bDown;
2540 {
2541   Display *fromDpy = pDpyInfo->fromDpy;
2542   KeyCode code = 0;
2543   PFAKE *ppFake;
2544   PFAKE pFake;
2545 
2546   if (type == FAKE_KEY)
2547      code = XKeysymToKeycode(fromDpy, thing);
2548   else
2549      code = thing;
2550 
2551   /* find the associated button, or the last record, whichever comes first */
2552   for (ppFake = &(pDpyInfo->pFakeThings);
2553        (*ppFake &&
2554 	(((*ppFake)->type != type) || ((*ppFake)->code != code)));
2555        ppFake = &((*ppFake)->pNext));
2556 
2557   if (bDown) { /* key down */
2558     if (*ppFake == NULL) { /* need a new record */
2559       pFake = (PFAKE)xmalloc(sizeof(FAKE));
2560       pFake->pNext = NULL; /* always at the end of the list */
2561       pFake->type = type;
2562       pFake->thing = thing;
2563       pFake->code = code;
2564       *ppFake = pFake;
2565     } /* END if */
2566   } else { /* key up */
2567     if (*ppFake != NULL) { /* get rid of the record */
2568       /* splice out of the list */
2569       pFake = *ppFake;
2570       *ppFake = pFake->pNext;
2571       free(pFake); /* blam! */
2572     } /* END if */
2573   } /* END if */
2574 
2575 } /* END FakeAction */
2576 
2577 static void FakeThingsUp(pDpyInfo)
2578 PDPYINFO pDpyInfo;
2579 {
2580   PFAKE pFake, pNext;
2581   PSHADOW pShadow;
2582   unsigned int type;
2583   KeyCode keycode;
2584 
2585   if (pDpyInfo->pFakeThings) { /* everything goes up! */
2586     for (pFake = pDpyInfo->pFakeThings; pFake; pFake = pNext) {
2587       type = pFake->type;
2588       /* send up to all shadows */
2589       for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
2590         if (type == FAKE_KEY) { /* key goes up */
2591           if ((keycode = XKeysymToKeycode(pShadow->dpy, pFake->thing))) {
2592             XTestFakeKeyEvent(pShadow->dpy, keycode, False, 0);
2593 	    pShadow->flush = True;
2594             debug("key 0x%lx up\n", (unsigned long)pFake->thing);
2595           } /* END if */
2596         } else { /* button goes up */
2597           XTestFakeButtonEvent(pShadow->dpy, pFake->thing, False, 0);
2598 	  pShadow->flush = True;
2599           debug("button %ld up\n", (long)pFake->thing);
2600         } /* END if/else */
2601       } /* END for */
2602 
2603       /* get next and free current */
2604       pNext = pFake->pNext;
2605       free(pFake);
2606     } /* END for */
2607 
2608     pDpyInfo->pFakeThings = NULL;
2609   } /* END if */
2610 
2611   /* flush everything at once */
2612   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext)
2613     if (pShadow->flush) {
2614       XFlush(pShadow->dpy);
2615       pShadow->flush = False;
2616     }
2617 
2618 } /* END FakeThingsUp */
2619 
2620 static void RefreshPointerMapping(dpy, pDpyInfo)
2621 Display             *dpy;
2622 PDPYINFO            pDpyInfo;
2623 {
2624   unsigned int buttCtr;
2625   unsigned char buttonMap[N_BUTTONS];
2626 
2627   if (dpy == pDpyInfo->toDpy) { /* only care about toDpy */
2628     /* straightforward mapping */
2629     for (buttCtr = 1; buttCtr <= N_BUTTONS; ++buttCtr) {
2630       pDpyInfo->inverseMap[buttCtr] = buttCtr;
2631     } /* END for */
2632 
2633     nButtons = MIN(N_BUTTONS, XGetPointerMapping(dpy, buttonMap, N_BUTTONS));
2634         debug("got button mapping: %d items\n", nButtons);
2635 #ifdef WIN_2_X
2636     pDpyInfo->nXbuttons = nButtons;
2637 #endif
2638     if (doPointerMap) {
2639       for (buttCtr = 0; buttCtr < nButtons; ++buttCtr) {
2640         debug("button %d -> %d\n", buttCtr + 1, buttonMap[buttCtr]);
2641         if (buttonMap[buttCtr] <= N_BUTTONS)
2642           pDpyInfo->inverseMap[buttonMap[buttCtr]] = buttCtr + 1;
2643       } /* END for */
2644     } /* END if */
2645   } /* END if toDpy */
2646 
2647 } /* END RefreshPointerMapping */
2648 
2649 #ifdef WIN_2_X
2650 
2651 /* These are the Windows specific routines */
2652 
2653 /* Want the Edge window and not the Big one */
2654 void MoveWindowToEdge(PDPYINFO pDpyInfo) {
2655   if (pDpyInfo->onedge) return;
2656   debug("MoveWindowToEdge\n");
2657 
2658   SetWindowPos(pDpyInfo->bigwindow, HWND_BOTTOM,
2659                pDpyInfo->screenRect.left, pDpyInfo->screenRect.top,
2660                pDpyInfo->screenWidth, pDpyInfo->screenHeight,
2661                SWP_HIDEWINDOW /* | SWP_NOREDRAW */);
2662 
2663   SetWindowPos(pDpyInfo->edgewindow, HWND_TOPMOST,
2664                (doEdge == EDGE_EAST) ? pDpyInfo->monitorRect.right -1: pDpyInfo->monitorRect.left,
2665          (doEdge == EDGE_SOUTH) ? pDpyInfo->monitorRect.bottom - 1 : pDpyInfo->monitorRect.top,
2666          (pDpyInfo->vertical) ? pDpyInfo->monitorRect.right - pDpyInfo->monitorRect.left : 1,
2667          (pDpyInfo->vertical) ? 1 : pDpyInfo->monitorRect.bottom - pDpyInfo->monitorRect.top,
2668                SWP_SHOWWINDOW | SWP_NOREDRAW);
2669   pDpyInfo->onedge=1;
2670 
2671   SetForegroundWindow(hWndSave);
2672 }
2673 
2674 int MoveWindowToScreen(PDPYINFO pDpyInfo)
2675 {
2676   int notfg;
2677   LPINPUT pInputs;
2678 
2679   if(!pDpyInfo->onedge) return 1;
2680   debug("MoveWindowToScreen\n");
2681 
2682   hWndSave = GetForegroundWindow();
2683 
2684   if ((notfg = (SetForegroundWindow(pDpyInfo->bigwindow) == 0))) {
2685     debug("Did not become foreground\n");
2686 
2687     /* This code thanks to Thomas Chadwick */
2688     /* Fakes that the user clicked the mouse to move the focus */
2689     /* This is to deal with the XP and 2000 behaviour that attempts to */
2690     /* prevent an application from stealing the focus */
2691     debug("Using SendInput to synthesize a mouse click\n");
2692 
2693     pInputs = (LPINPUT) xmalloc (2*sizeof(INPUT));
2694 
2695     pInputs[0].type           = INPUT_MOUSE;
2696     pInputs[0].mi.dx          = 0;
2697     pInputs[0].mi.dy          = 0;
2698     pInputs[0].mi.mouseData   = 0;
2699     pInputs[0].mi.dwFlags     = MOUSEEVENTF_LEFTDOWN;
2700     pInputs[0].mi.time        = 0;
2701     pInputs[0].mi.dwExtraInfo = 0;
2702 
2703     pInputs[1].type           = INPUT_MOUSE;
2704     pInputs[1].mi.dx          = 0;
2705     pInputs[1].mi.dy          = 0;
2706     pInputs[1].mi.mouseData   = 0;
2707     pInputs[1].mi.dwFlags     = MOUSEEVENTF_LEFTUP;
2708     pInputs[1].mi.time        = 0;
2709     pInputs[1].mi.dwExtraInfo = 0;
2710 
2711     if (SendInput(2, pInputs, sizeof(INPUT)) == 0) {
2712       debug("SendInput failed\n");
2713       free(pInputs);
2714       return 0;
2715     }
2716     free(pInputs);
2717     return 1;
2718   }
2719 
2720   SetWindowPos(pDpyInfo->bigwindow, HWND_TOPMOST,
2721                pDpyInfo->screenRect.left, pDpyInfo->screenRect.top,
2722                pDpyInfo->screenWidth, pDpyInfo->screenHeight,
2723                SWP_SHOWWINDOW | SWP_NOREDRAW);
2724 
2725   pDpyInfo->onedge=0;
2726   return 1;
2727 }
2728 
2729 static void DoWinConnect(pDpyInfo, x, y)
2730 PDPYINFO pDpyInfo;
2731 int x,y;
2732 {
2733   PSHADOW   pShadow;
2734 
2735   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
2736     DoDPMSForceLevel(pShadow, DPMSModeOn);
2737     XFlush(pShadow->dpy);
2738   }
2739 
2740   debug("connecting (Win2x)\n");
2741   pDpyInfo->mode = X2X_CONNECTED;
2742 
2743   if (!pDpyInfo->onedge) return;
2744 
2745   if (MoveWindowToScreen(pDpyInfo)) {
2746     debug("Warp Cursor: %d,%d -> %d, %d\n",
2747            x,y,
2748            (doEdge == EDGE_EAST) ? 1 : pDpyInfo->fromWidth - 3,
2749            y);
2750 
2751     if (pDpyInfo->vertical)
2752     {
2753       pDpyInfo->lastFromCoord = (doEdge == EDGE_SOUTH) ? 0 : pDpyInfo->fromHeight - 2;
2754       pDpyInfo->lastFromX = x - pDpyInfo->monitorRect.left - logicalOffset;
2755       pDpyInfo->lastFromY = pDpyInfo->lastFromCoord;
2756     }
2757     else
2758     {
2759 
2760       pDpyInfo->lastFromCoord = (doEdge == EDGE_EAST) ? 0 : pDpyInfo->fromWidth - 2;
2761       pDpyInfo->lastFromX = pDpyInfo->lastFromCoord;
2762       pDpyInfo->lastFromY = y - pDpyInfo->monitorRect.top - logicalOffset;
2763     }
2764 
2765     SetCursorPos(pDpyInfo->lastFromX, pDpyInfo->lastFromY);
2766   }
2767 }
2768 
2769 static void DoWinDisconnect(pDpyInfo, x, y)
2770 PDPYINFO pDpyInfo;
2771 int x,y;
2772 {
2773 
2774   debug("disconnecting (Win2x)\n");
2775   pDpyInfo->mode = X2X_DISCONNECTED;
2776 
2777   /* If we own the X selection, then windows has it! */
2778   /* otherwise transfer the info by asking the owning X window to */
2779   /* tell us (via ProcessSelectNotifyW) */
2780   if (!pDpyInfo->owntoXsel) {
2781     debug("Ask X for -to selection\n");
2782     XConvertSelection(pDpyInfo->toDpy, XA_PRIMARY, XA_STRING,
2783                       XA_PRIMARY, pDpyInfo->toDpyXtra.propWin, CurrentTime);
2784     XFlush(pDpyInfo->toDpy);
2785     pDpyInfo->expectSelNotify = 1;
2786   }
2787 
2788   if (x >= 0) {
2789     debug("Warp Cursor: %d,%d -> %d, %d\n",
2790            x,y,
2791            (doEdge == EDGE_EAST) ? pDpyInfo->fromWidth - 2 : 2,
2792            y);
2793     if (pDpyInfo->vertical)
2794     {
2795       SetCursorPos(x + pDpyInfo->monitorRect.left + logicalOffset, (doEdge == EDGE_SOUTH) ? pDpyInfo->monitorRect.bottom - 2 : pDpyInfo->monitorRect.top);
2796     }
2797     else
2798     {
2799       SetCursorPos((doEdge == EDGE_EAST) ? pDpyInfo->monitorRect.right - 2 : pDpyInfo->monitorRect.left, y + pDpyInfo->monitorRect.top + logicalOffset);
2800     }
2801   }
2802 
2803   MoveWindowToEdge(pDpyInfo);
2804   /* force normal state on to display: */
2805   if (doAutoUp)
2806     FakeThingsUp(pDpyInfo);
2807 }
2808 
2809 /* Windows Event Management */
2810 LRESULT CALLBACK
2811 WinProcessMessage (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
2812 {
2813   PDPYINFO pDpyInfo = (PDPYINFO) GetWindowLong(hwnd, GWL_USERDATA);
2814 
2815 #ifdef DEBUGCHATTY
2816   if (iMsg != WM_PAINT) printf("Got msg %d (0x%04x) %s %s\n", iMsg, iMsg,
2817                                msgtotext(iMsg),
2818                                (hwnd == NULL) ? "null" :
2819                                (pDpyInfo == NULL) ? "null dpy" :
2820                                (hwnd == pDpyInfo->bigwindow) ? "big" :
2821                                ((hwnd == pDpyInfo->edgewindow) ? "edge" : "XX"));
2822 #endif
2823   switch (iMsg)
2824   {
2825     case WM_CREATE:
2826       return 0;
2827 
2828     case WM_TIMER:
2829       debug("#");
2830 #if 0
2831       if (wParam == m_emulate3ButtonsTimer)
2832       {
2833         SubProcessPointerEvent(m_emulateButtonPressedX,
2834                                m_emulateButtonPressedY,
2835                                m_emulateKeyFlags);
2836         KillTimer(m_hwnd, m_emulate3ButtonsTimer);
2837         m_waitingOnEmulateTimer = false;
2838       }
2839 #endif
2840       return 0;
2841 
2842     case WM_LBUTTONDOWN:
2843     case WM_LBUTTONUP:
2844     case WM_MBUTTONDOWN:
2845     case WM_MBUTTONUP:
2846     case WM_RBUTTONDOWN:
2847     case WM_RBUTTONUP:
2848     case WM_MOUSEMOVE:
2849     {
2850 
2851       POINT pt;
2852       int x,y;
2853 
2854       if (GetFocus() != hwnd) {
2855         if (pDpyInfo == NULL) {
2856           debug("No focus and pDpyInfo NULL\n");
2857           return 0;
2858         }
2859         if (pDpyInfo->onedge) {
2860           debug("No focus and currently on edge\n");
2861           return 0;
2862         }
2863         if (hwnd == pDpyInfo->bigwindow) {
2864           /* Ok, event for the bigwindow, but no focus -> take it */
2865           debug("No focus on bigwindow mouse event, grab it\n");
2866           SetForegroundWindow(pDpyInfo->bigwindow);
2867           SetFocus(pDpyInfo->bigwindow);
2868         }
2869         else
2870           debug("No focus, not on edge, not bigwindow\n");
2871       }
2872 
2873       pt.x = GET_X_LPARAM(lParam);
2874       pt.y = GET_Y_LPARAM(lParam);
2875 
2876       ClientToScreen(hwnd, &pt);
2877       x = pt.x;
2878       y = pt.y;
2879 
2880       if(x<-32768 || x>32768) x=0;
2881       if(y<-32768 || y>32768) y=0;
2882 
2883       if(x>=pDpyInfo->fromWidth)
2884         x = pDpyInfo->fromWidth - 1;
2885       if(y>=pDpyInfo->fromHeight)
2886         y = pDpyInfo->fromHeight - 1;
2887       if(y<0)
2888         y = 0;
2889       if(x<0)
2890         x = 0;
2891 
2892       if (pt.x != x || pt.y != y)
2893       {
2894         SetCursorPos(pDpyInfo->lastFromX, pDpyInfo->lastFromY);
2895         return;
2896       }
2897 
2898 
2899       if(pDpyInfo->onedge)
2900       {
2901         if(hwnd == pDpyInfo->edgewindow)
2902         {
2903           debug("onedge mouse connect\n");
2904           DoWinConnect(pDpyInfo, x,y);
2905         }
2906         else {
2907           debug("onedge mouse move to non edge window ");
2908           DoWinConnect(pDpyInfo, x, y);
2909         }
2910         return 0;
2911       }
2912 
2913       if(hwnd == pDpyInfo->bigwindow)
2914       {
2915         WinPointerEvent(pDpyInfo, x,y, wParam, iMsg);
2916 
2917       } /* END == bigwindow */
2918       if(hwnd == pDpyInfo->edgewindow)
2919       {
2920         int delta;
2921 #ifdef DEBUGMOUSE
2922         printf("e(%d,%d) ", x, y);
2923 #endif
2924         delta = pDpyInfo->lastFromCoord - ((pDpyInfo->vertical) ? y : x);
2925         if (delta < 0) delta = -delta;
2926         if (delta > pDpyInfo->unreasonableDelta) {
2927           /* Guess that the warp failed and try it again... */
2928           debug("Retry warp to (%d, %d)\n",
2929                 (doEdge == EDGE_EAST) ? 1 : pDpyInfo->fromWidth - 3,
2930                  y);
2931           if (pDpyInfo->vertical)
2932           {
2933             SetCursorPos(x, (doEdge == EDGE_SOUTH) ? 1 : pDpyInfo->fromHeight - 3);
2934           }
2935           else
2936           {
2937             SetCursorPos((doEdge == EDGE_EAST) ? 1 : pDpyInfo->fromWidth - 3, y);
2938           }
2939         }
2940       } /* END == edgewindow */
2941 
2942       return 0;
2943     }
2944 
2945     case WM_MOUSEWHEEL:
2946     {
2947       /* Partial deltas can be sent, but should only take notice */
2948       /* in units of WHEEL_DELTA */
2949       int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
2950       int wdelta;
2951 
2952       wdelta = pDpyInfo->wdelta + zDelta;
2953 
2954       debug("Wheel moved to new delta %d (%s window)\n", wdelta,
2955              (hwnd == pDpyInfo->edgewindow) ? "edge" :
2956              (hwnd == pDpyInfo->bigwindow) ? "big" : "other");
2957 
2958       while (wdelta >= WHEEL_DELTA) {
2959         /* Scroll up by sending Button 4 */
2960         SendButtonClick(pDpyInfo, 4);
2961         wdelta -= WHEEL_DELTA;
2962       }
2963 
2964       while (wdelta <= -WHEEL_DELTA) {
2965         /* Scroll down by sending Button 5 */
2966         SendButtonClick(pDpyInfo, 5);
2967         wdelta += WHEEL_DELTA;
2968       }
2969       pDpyInfo->wdelta = wdelta;
2970 
2971       return 0;
2972     }
2973 
2974     case WM_KEYDOWN:
2975     case WM_KEYUP:
2976     case WM_SYSKEYDOWN:
2977     case WM_SYSKEYUP:
2978     {
2979 #if 0
2980       if (pDpyInfo->winSSave) {
2981         debug("Attempt to restore from screen saver (key)!\n");
2982         if (!pDpyInfo->onedge) {
2983           SetWindowPos(pDpyInfo->bigwindow, HWND_TOPMOST,
2984                        pDpyInfo->screenRect.left, pDpyInfo->screenRect.top,
2985                        pDpyInfo->screenWidth, pDpyInfo->screenHeight,
2986                        SWP_HIDEWINDOW);
2987           SetWindowPos(pDpyInfo->bigwindow, HWND_TOPMOST,
2988                        pDpyInfo->screenRect.left, pDpyInfo->screenRect.top,
2989                        pDpyInfo->screenWidth, pDpyInfo->screenHeight,
2990                        SWP_SHOWWINDOW  | SWP_NOREDRAW);
2991         }
2992         pDpyInfo->winSSave = 0;
2993       }
2994 #endif /* 0 */
2995       WinKeyEvent(pDpyInfo, (int) wParam, (DWORD) lParam);
2996       return 0;
2997     }
2998 
2999     case WM_CHAR:
3000     case WM_SYSCHAR:
3001     case WM_DEADCHAR:
3002     case WM_SYSDEADCHAR:
3003       return 0;
3004 
3005       // Cacnel modifiers when we lose focus
3006     case WM_KILLFOCUS:
3007     {
3008 #if 0
3009       /* XXX mdh - need to think about this */
3010       if (!m_running) return 0;
3011       log.Print(6, _T("Losing focus - cancelling modifiers\n"));
3012       SendKeyEvent(XK_Alt_L,     false);
3013       SendKeyEvent(XK_Control_L, false);
3014       SendKeyEvent(XK_Shift_L,   false);
3015       SendKeyEvent(XK_Alt_R,     false);
3016       SendKeyEvent(XK_Control_R, false);
3017       SendKeyEvent(XK_Shift_R,   false);
3018 #endif /* 0 */
3019       return 0;
3020     }
3021 
3022     case WM_CLOSE:
3023     {
3024       DestroyWindow(hwnd);
3025       return 0;
3026     }
3027 
3028     case WM_DESTROY:
3029     {
3030       // Remove us from the clipboard viewer chain
3031       if (doSel) {
3032         /*int res =*/ ChangeClipboardChain(pDpyInfo->edgewindow,
3033                                        pDpyInfo->hwndNextViewer);
3034       }
3035 
3036       SetWindowLong(pDpyInfo->bigwindow, GWL_USERDATA, (LONG) 0);
3037       SetWindowLong(pDpyInfo->edgewindow, GWL_USERDATA, (LONG) 0);
3038 
3039       if(hwnd == pDpyInfo->bigwindow) pDpyInfo->bigwindow = 0;
3040       if(hwnd == pDpyInfo->edgewindow) pDpyInfo->edgewindow = 0;
3041       /* XXX mdh - is a PostQuitMessage(0) needed here? */
3042       return 0;
3043     }
3044 
3045     case WM_SETCURSOR:
3046     {
3047       POINT pt;
3048       if(doEdge && (hwnd == pDpyInfo->edgewindow))
3049       {
3050         GetCursorPos(&pt);
3051         debug("Activated by action: WM_SETCURSOR\n");
3052         DoWinConnect(pDpyInfo, pt.x, pt.y);
3053       }
3054       return DefWindowProc(hwnd, iMsg, wParam, lParam);
3055     }
3056 
3057     case WM_DRAWCLIPBOARD:
3058       if (doSel) {
3059         // The clipboard contents changed
3060         /* For now can only process text */
3061         if (IsClipboardFormatAvailable(CF_TEXT) &&
3062             OpenClipboard(pDpyInfo->edgewindow)) {
3063           HGLOBAL hglb = GetClipboardData(CF_TEXT);
3064           LPTSTR lptstr = GlobalLock(hglb);
3065           int len;
3066           len = strlen(lptstr);
3067           /* If the length is bad just ignore */
3068           if (len > 0) {
3069             if (pDpyInfo->winSelText != NULL) free(pDpyInfo->winSelText);
3070             pDpyInfo->winSelText = xmalloc(len + 10);
3071             if (pDpyInfo->winSelText != NULL)
3072               strcpy(pDpyInfo->winSelText, lptstr);
3073             /* XXX mdh - hope this does (null) as expected! */
3074             debug("Windows clipboard changed to %s\n", pDpyInfo->winSelText);
3075           }
3076           GlobalUnlock(hglb);
3077           CloseClipboard();
3078           /* Prevent grabbing X selection in response to us copying  */
3079           /* it to windows (mostly cosmetic in that the real source  */
3080           /* X app will unhighlight or whatever on loosing selection)*/
3081           /* XXX mdh - the strlen+1 isn't guarenteed to be unique */
3082           /* but I think it is good enough, since if it goes wrong */
3083           /* the X app looses its selection but the actual text is preserved */
3084           if (pDpyInfo->expectOwnClip != 0) {
3085             if (pDpyInfo->expectOwnClip == (len+1)) {
3086               /* Its ours stop looking */
3087               pDpyInfo->expectOwnClip = 0;
3088               debug("Saw own addition to clipboard\n");
3089             } else {
3090                 debug("Oops. expectOwrClip %d with len %d\n",
3091                        pDpyInfo->expectOwnClip, len);
3092             }
3093           } else {
3094             /* This can race during creation */
3095             /* but we will claim selection in the init routine */
3096             if (pDpyInfo->toDpyXtra.propWin != 0) {
3097               debug("Selection Owner to %x\n",
3098                      (unsigned int)pDpyInfo->toDpyXtra.propWin);
3099               XSetSelectionOwner(pDpyInfo->toDpy, XA_PRIMARY,
3100                                  pDpyInfo->toDpyXtra.propWin, CurrentTime);
3101               pDpyInfo->owntoXsel = 1;
3102             }
3103           }
3104         }
3105         // We have to tell the next clipbard viewer
3106         SendMessage(pDpyInfo->hwndNextViewer, iMsg, wParam, lParam);
3107       } else {
3108         printf("Unxepected WM_DRAWCLIPBOARD when not doing selection\n");
3109       }
3110       // ProcessLocalClipboardChange();
3111 
3112       return 0;
3113 
3114     case WM_CHANGECBCHAIN:
3115     {
3116       if (doSel) {
3117         // The clipboard chain is changing
3118         HWND hWndRemove = (HWND) wParam;     // handle of window being removed
3119         HWND hWndNext = (HWND) lParam;       // handle of next window in chain
3120         // If next window is closing, update our pointer.
3121         if (hWndRemove == pDpyInfo->hwndNextViewer)
3122           pDpyInfo->hwndNextViewer = hWndNext;
3123         // Otherwise, pass the message to the next link.
3124         else if (pDpyInfo->hwndNextViewer != NULL)
3125           SendMessage(pDpyInfo->hwndNextViewer, WM_CHANGECBCHAIN,
3126                       (WPARAM) hWndRemove,  (LPARAM) hWndNext );
3127       } else {
3128         printf("Unxepected WM_CHANGEKBCHAIN when not doing selection\n");
3129       }
3130       return 0;
3131     }
3132 
3133     case WM_SYSCOMMAND:
3134       debug("WM_SYSCOMMAND with wParam %d (0x%x)\n", wParam, wParam);
3135       if (wParam == SC_SCREENSAVE) {
3136 	PSHADOW   pShadow;
3137 	pDpyInfo->winSSave = 1;
3138 	for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
3139 	  XActivateScreenSaver(pShadow->dpy);
3140 	  XFlush(pShadow->dpy);
3141 	  pShadow->flush = False;
3142 	} /* END for shadow */
3143       }
3144       /* Fall through */
3145     case WM_SYNCPAINT:
3146     case WM_PAINT:
3147       return DefWindowProc(hwnd, iMsg, wParam, lParam);
3148 
3149     case WM_NCPAINT:
3150       return 0;
3151 
3152     case WM_SETFOCUS:
3153     case WM_ACTIVATE:
3154     case WM_NCHITTEST:
3155     case WM_NCACTIVATE:
3156     case WM_WINDOWPOSCHANGED:
3157     case WM_WINDOWPOSCHANGING:
3158     case WM_SIZE:
3159     case WM_HSCROLL:
3160     case WM_VSCROLL:
3161     case WM_GETMINMAXINFO:
3162     case WM_QUERYNEWPALETTE:
3163     case WM_PALETTECHANGED:
3164       return 1;
3165 
3166       /* keep transparent (I hope) */
3167     case WM_ERASEBKGND:
3168       return 1;
3169 
3170     case X2X_MSG_HOTKEY:
3171       if (pDpyInfo->mode == X2X_CONNECTED)
3172       {
3173         DoWinDisconnect(pDpyInfo, 30, 30);
3174       }
3175       else
3176       {
3177         DoWinConnect(pDpyInfo, 30, 30);
3178       }
3179 
3180       return 1;
3181   }
3182 
3183   debug("Unused message: %d (0x%04x)\n", iMsg, iMsg);
3184   return DefWindowProc(hwnd, iMsg, wParam, lParam);
3185 }
3186 
3187 void WinPointerEvent(PDPYINFO pDpyInfo,
3188                      int x, int y, DWORD keyflags, UINT msg)
3189 {
3190   int down = 0;
3191   unsigned int button, toButton;
3192   int       toScreenNum;
3193   PSHADOW   pShadow;
3194   int       toCoord, fromCoord, fromX, fromY, delta;
3195   short **coordTables;
3196 
3197   button = 0;
3198   switch (msg) {
3199   case WM_MOUSEMOVE:
3200 
3201     /* seems that we get repeats, ignore them */
3202     if ((x == pDpyInfo->lastFromX) && (y == pDpyInfo->lastFromY)) {
3203       return;
3204     }
3205 
3206     /* find the screen */
3207     toScreenNum = pDpyInfo->toScreen;
3208     fromX = x;
3209     fromY = y;
3210 
3211     fromCoord = (pDpyInfo->vertical) ? fromY : fromX;
3212 
3213     coordTables = (pDpyInfo->vertical) ? pDpyInfo->yTables : pDpyInfo->xTables;
3214 
3215     toCoord = coordTables[toScreenNum][fromCoord];
3216 
3217     /* sanity check motion: necessary for nondeterminism surrounding warps */
3218     delta = pDpyInfo->lastFromCoord - fromCoord;
3219     if (delta < 0) delta = -delta;
3220     if (delta > pDpyInfo->unreasonableDelta) {
3221       if (pDpyInfo->unreasonableCount++ < MAX_UNREASONABLES) {
3222 	debug("Unreasonable x delta last = %d this = %d\n",
3223 	      pDpyInfo->lastFromX, fromX);
3224 	return;
3225       }
3226       pDpyInfo->unreasonableCount = 0;
3227     }
3228 
3229     if (SPECIAL_COORD(toCoord) != 0) { /* special coordinate */
3230       if (toCoord == COORD_INCR) {
3231         if (toScreenNum != (pDpyInfo->nScreens - 1)) { /* next screen */
3232           toScreenNum = ++(pDpyInfo->toScreen);
3233           fromCoord = pDpyInfo->fromIncrCoord;
3234           toCoord = coordTables[toScreenNum][fromCoord];
3235         } else { /* disconnect! */
3236           if (doBtnBlock &&
3237             (keyflags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) {
3238           } else {
3239             DoWinDisconnect(pDpyInfo, x, y);
3240             fromCoord = pDpyInfo->fromDiscCoord;
3241           }
3242           toCoord = coordTables[toScreenNum][pDpyInfo->fromConnCoord];
3243         }
3244       } else { /* DECR */
3245         if (toScreenNum != 0) { /* previous screen */
3246           toScreenNum = --(pDpyInfo->toScreen);
3247           fromCoord = pDpyInfo->fromDecrCoord;
3248           toCoord = coordTables[toScreenNum][fromCoord];
3249         } else { /* disconnect! */
3250           if (doBtnBlock &&
3251             (keyflags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) {
3252           } else {
3253             DoWinDisconnect(pDpyInfo, x, y);
3254             fromCoord = pDpyInfo->fromDiscCoord;
3255           }
3256           toCoord = coordTables[toScreenNum][pDpyInfo->fromConnCoord];
3257         }
3258       } /* END if toX */
3259     } /* END if SPECIAL_COORD */
3260     pDpyInfo->lastFromCoord = fromCoord;
3261     pDpyInfo->lastFromX = fromX;
3262     pDpyInfo->lastFromY = fromY;
3263 
3264     for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
3265       if (doDpmsMouse)
3266       {
3267         DoDPMSForceLevel(pShadow, DPMSModeOn);
3268       }
3269       XTestFakeMotionEvent(pShadow->dpy, toScreenNum, pDpyInfo->xTables[toScreenNum][x],
3270         pDpyInfo->yTables[toScreenNum][y], 0);
3271       XFlush(pShadow->dpy);
3272       pShadow->flush = False;
3273     } /* END for */
3274     return;
3275 
3276   case WM_LBUTTONDOWN: down++;
3277   case WM_LBUTTONUP: button = Button1;
3278     break;
3279   case WM_MBUTTONDOWN: down++;
3280   case WM_MBUTTONUP: button = Button2;
3281     break;
3282   case WM_RBUTTONDOWN: down++;
3283   case WM_RBUTTONUP: button = Button3;
3284     break;
3285   }
3286   /* down and button tell us what to do */
3287   if (button <= N_BUTTONS) {
3288     toButton = pDpyInfo->inverseMap[button];
3289     for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
3290       XTestFakeButtonEvent(pShadow->dpy, toButton, down, 0);
3291       debug("from button %d %s, to button %d %s\n",
3292             button, down ? "down":"up", toButton, down ? "down":"up");
3293       XFlush(pShadow->dpy);
3294       pShadow->flush = False;
3295     } /* END for */
3296     if (doAutoUp)
3297       FakeAction(pDpyInfo, FAKE_BUTTON, toButton, down);
3298   }
3299 }
3300 
3301 void SendButtonClick(pDpyInfo, button)
3302      PDPYINFO pDpyInfo;
3303      int button;
3304 {
3305   int toButton;
3306   int       toScreenNum;
3307   PSHADOW   pShadow;
3308 
3309   toScreenNum = pDpyInfo->toScreen;
3310 
3311   if (button <= N_BUTTONS) {
3312     toButton = pDpyInfo->inverseMap[button];
3313     if (toButton <= pDpyInfo->nXbuttons)
3314       for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
3315         XTestFakeButtonEvent(pShadow->dpy, toButton, True, 0);
3316         XTestFakeButtonEvent(pShadow->dpy, toButton, False, 0);
3317         debug("Click from button %d, to button %d\n",
3318                button, toButton);
3319         XFlush(pShadow->dpy);
3320 	pShadow->flush = False;
3321       } /* END for */
3322     else
3323       debug("to only has %d buttons, cant clikc from %d -> to %d\n",
3324              pDpyInfo->nXbuttons, button, toButton);
3325   }
3326 }
3327 
3328 //
3329 // ProcessKeyEvent
3330 //
3331 // Normally a single Windows key event will map onto a single RFB
3332 // key message, but this is not always the case.  Much of the stuff
3333 // here is to handle AltGr (=Ctrl-Alt) on international keyboards.
3334 // Example cases:
3335 //
3336 //    We want Ctrl-F to be sent as:
3337 //      Ctrl-Down, F-Down, F-Up, Ctrl-Up.
3338 //    because there is no keysym for ctrl-f, and because the ctrl
3339 //    will already have been sent by the time we get the F.
3340 //
3341 //    On German keyboards, @ is produced using AltGr-Q, which is
3342 //    Ctrl-Alt-Q.  But @ is a valid keysym in its own right, and when
3343 //    a German user types this combination, he doesn't mean Ctrl-@.
3344 //    So for this we will send, in total:
3345 //
3346 //      Ctrl-Down, Alt-Down,
3347 //                 (when we get the AltGr pressed)
3348 //
3349 //      Alt-Up, Ctrl-Up, @-Down, Ctrl-Down, Alt-Down
3350 //                 (when we discover that this is @ being pressed)
3351 //
3352 //      Alt-Up, Ctrl-Up, @-Up, Ctrl-Down, Alt-Down
3353 //                 (when we discover that this is @ being released)
3354 //
3355 //      Alt-Up, Ctrl-Up
3356 //                 (when the AltGr is released)
3357 
3358 void WinKeyEvent(pDpyInfo, virtkey, keyData)
3359 PDPYINFO pDpyInfo;
3360 int virtkey;
3361 DWORD keyData;
3362 {
3363 #ifdef DEBUG
3364   char keyname[32];
3365 #endif
3366   KeyActionSpec kas;
3367   int down = ((keyData & 0x80000000l) == 0);
3368   int i;
3369   int winShift = 0;
3370 
3371   /* Attempt to discard spurious keys and 'fake' loss of focus */
3372   if (pDpyInfo->onedge && down) {
3373     debug("Ignore key while onedge\n");
3374     return;
3375   }
3376   // if virtkey found in mapping table, send X equivalent
3377   // else
3378   //   try to convert directly to ascii
3379   //   if result is in range supported by X keysyms,
3380   //      raise any modifiers, send it, then restore mods
3381   //   else
3382   //      calculate what the ascii would be without mods
3383   //      send that
3384 
3385 #ifdef DEBUG
3386   if (GetKeyNameText(  keyData,keyname, 31)) {
3387     debug("Process key: %s (vk %d keyData %04x): ", keyname, virtkey, (unsigned int)keyData);
3388   };
3389 #endif
3390 
3391   if (doCapsLkHack && (virtkey == VK_CAPITAL)) {
3392     /* We rely on Windows to process Caps Lock so don't send to X */
3393     debug(" Ignore Caps Lock\n");
3394     return;
3395   }
3396 
3397   /* Special cases */
3398   if ((virtkey == VK_HOME) || (virtkey == VK_END)) {
3399     if (GetKeyState(VK_RMENU) & 0x8000) { // right ALT down
3400       debug("Magic key \n");
3401       SendKeyEvent(pDpyInfo, XK_Alt_R, False, False, 0);
3402       if (virtkey == VK_END)
3403         exit(0); // Is there a proper way?
3404 
3405       DoWinDisconnect(pDpyInfo, -1, -1);
3406       return;
3407     }
3408   }
3409 
3410   kas = PCtoX(virtkey, keyData);
3411 
3412   if (kas.releaseModifiers & KEYMAP_LCONTROL) {
3413     SendKeyEvent(pDpyInfo, XK_Control_L, False, False, 0);
3414     debug("fake L Ctrl raised\n");
3415   }
3416 
3417   if (kas.releaseModifiers & KEYMAP_LALT) {
3418     SendKeyEvent(pDpyInfo, XK_Alt_L, False, False, 0);
3419     debug("fake L Alt raised\n");
3420   }
3421 
3422   if (kas.releaseModifiers & KEYMAP_RCONTROL) {
3423     SendKeyEvent(pDpyInfo, XK_Control_R, False, False, 0);
3424     debug("fake R Ctrl raised\n");
3425   }
3426 
3427   if (kas.releaseModifiers & KEYMAP_RALT) {
3428     SendKeyEvent(pDpyInfo, XK_Alt_R, False, False, 0);
3429     debug("fake R Alt raised\n");
3430   }
3431 
3432   /* NOTE: confusingly the 'keycodes' array actually contains keysyms */
3433 
3434   if (doCapsLkHack) {   /* Arguably this should be the dafault */
3435     winShift = (((GetKeyState(VK_LSHIFT) & 0x8000) ? 1 : 0) |
3436                 ((GetKeyState(VK_RSHIFT) & 0x8000) ? 2 : 0));
3437     debug(" winShift %d ", winShift);
3438   }
3439   for (i = 0; kas.keycodes[i] != XK_VoidSymbol && i < MaxKeysPerKey; i++) {
3440     SendKeyEvent(pDpyInfo, kas.keycodes[i], down, doCapsLkHack, winShift);
3441       debug("Sent keysym %04x (%s)\n",
3442              (int)kas.keycodes[i], down ? "press" : "release");
3443   }
3444 
3445   if (kas.releaseModifiers & KEYMAP_RALT) {
3446     SendKeyEvent(pDpyInfo, XK_Alt_R, True, False, 0);
3447     debug("fake R Alt pressed\n");
3448   }
3449 
3450   if (kas.releaseModifiers & KEYMAP_RCONTROL) {
3451     SendKeyEvent(pDpyInfo, XK_Control_R, True, False, 0);
3452     debug("fake R Ctrl pressed\n");
3453   }
3454 
3455   if (kas.releaseModifiers & KEYMAP_LALT) {
3456     SendKeyEvent(pDpyInfo, XK_Alt_L, False, False, 0);
3457     debug("fake L Alt pressed\n");
3458   }
3459 
3460   if (kas.releaseModifiers & KEYMAP_LCONTROL) {
3461     SendKeyEvent(pDpyInfo, XK_Control_L, False, False, 0);
3462     debug("fake L Ctrl pressed\n");
3463   }
3464 }
3465 
3466 //
3467 // SendKeyEvent
3468 //
3469 
3470 /* Note implementation of capsLockHack is different from X version */
3471 /* This version should also catch the case of keys that are shifted on from */
3472 /* but unshifted on to. Eg '<' above comma on from, going to '>' above '<' */
3473 void SendKeyEvent(PDPYINFO pDpyInfo, KeySym keysym, int down,
3474                   int chkShift, int winShift)
3475 {
3476   KeyCode keycode;
3477   PSHADOW   pShadow;
3478   int invShift;
3479 
3480   for (pShadow = shadows; pShadow; pShadow = pShadow->pNext) {
3481     if ((keycode = XKeysymToKeycode(pShadow->dpy, keysym))) {
3482       invShift = 0;
3483       if (chkShift && (keysym != XK_Shift_R) && (keysym != XK_Shift_L)) {
3484         /* Check that the shift key matches where the keysym is */
3485         if (XKeycodeToKeysym(pShadow->dpy, keycode, winShift ? 1:0) != keysym){
3486           /* Ok, key does not match with current shift */
3487           if (XKeycodeToKeysym(pShadow->dpy,keycode,winShift ? 0:1) == keysym){
3488             /* But does with shift inverted */
3489             invShift = 1;
3490             debug("Invert shift ");
3491           }
3492           else
3493             debug(" keysym not keycode 0 or 1 hope for the best ");
3494         }
3495       }
3496 
3497       /* XXX mdh -- As far as I can tell we will never generate RSHIFT */
3498       /* Windows reports virtkey=SHIFT for both types which sends      */
3499       /* left shift to X. The code here is simpler taking advantage    */
3500       /* of this. But I had already written the general case, and if   */
3501       /* PCtoX is fixed to use both shifts, then you need to define    */
3502       /* USING_RSHIFT  */
3503 
3504       if (invShift) {
3505         KeyCode toShiftLCode = XKeysymToKeycode(pShadow->dpy, XK_Shift_L);
3506 #ifdef USING_RSHIFT
3507         KeyCode toShiftRCode = XKeysymToKeycode(pShadow->dpy, XK_Shift_R);
3508 #endif
3509         /* XXX mdh - Would it be better to only mess with shifts on down */
3510         /* XXX mdh - and only restore on up? */
3511 
3512         if (winShift == 0) { // Need to press, choose left
3513           XTestFakeKeyEvent(pShadow->dpy, toShiftLCode, True, 0);
3514           debug("LSdown ");
3515         } else {
3516           // Release whichever is pressed or both
3517 #ifdef USING_RSHIFT
3518           if (winShift & 1) {
3519                       XTestFakeKeyEvent(pShadow->dpy, toShiftLCode, False, 0);
3520                   debug("LSup ");
3521           }
3522           if (winShift & 2) {
3523                       XTestFakeKeyEvent(pShadow->dpy, toShiftRCode, False, 0);
3524                   debug("RSup ");
3525           }
3526 #else /* not USING_RSHIFT */
3527           /* Since we only ever send Left shifts, thats all we need release */
3528           XTestFakeKeyEvent(pShadow->dpy, toShiftLCode, False, 0);
3529           debug("LSup ");
3530 #endif /* USING_RSHIFT */
3531         }
3532         XTestFakeKeyEvent(pShadow->dpy, keycode, down, 0);
3533         if (winShift == 0) // Needed to press, so release
3534           XTestFakeKeyEvent(pShadow->dpy, toShiftLCode, False, 0);
3535         else {
3536 #ifdef USING_RSHIFT
3537           // Restore whichever is pressed
3538           if (winShift & 1)
3539                       XTestFakeKeyEvent(pShadow->dpy, toShiftLCode, True, 0);
3540           if (winShift & 2)
3541                       XTestFakeKeyEvent(pShadow->dpy, toShiftRCode, True, 0);
3542 #else /* not USING_RSHIFT */
3543           XTestFakeKeyEvent(pShadow->dpy, toShiftLCode, True, 0);
3544 #endif /* USING_RSHIFT */
3545         }
3546       }
3547       else
3548         XTestFakeKeyEvent(pShadow->dpy, keycode, down, 0);
3549 
3550       XFlush(pShadow->dpy);
3551       pShadow->flush = False;
3552     } /* END if */
3553   } /* END for */
3554   if (doAutoUp)
3555     FakeAction(pDpyInfo, FAKE_KEY, keysym, down);
3556 }
3557 
3558 /* SelectionClear event indicates we lost the selection */
3559 static Bool ProcessSelectionClearW(dpy, pDpyInfo, pEv)
3560 Display *dpy;
3561 PDPYINFO pDpyInfo;
3562 XSelectionClearEvent *pEv;
3563 {
3564   debug("selection clear W\n");
3565 
3566   if (pEv->selection == XA_PRIMARY) {
3567     pDpyInfo->owntoXsel = 0;
3568     if (pDpyInfo->winSelText) {
3569       free(pDpyInfo->winSelText);
3570       pDpyInfo->winSelText = NULL;
3571     }
3572   } /* END if primary */
3573   return False;
3574 
3575 } /* END ProcessSelectionClearW */
3576 
3577 static Bool ProcessSelectionRequestW(dpy, pDpyInfo, pEv)
3578 Display *dpy;
3579 PDPYINFO pDpyInfo;
3580 XSelectionRequestEvent *pEv;
3581 {
3582   debug("selection request W\n");
3583 
3584   /* only do strings to PRIMARY */
3585   if ((pDpyInfo->winSelText == NULL) ||
3586       (pEv->selection != XA_PRIMARY) ||
3587       (pEv->target != XA_STRING)) { /* bad request, punt request */
3588     pEv->property = None;
3589     SendSelectionNotify(pEv); /* blam! */
3590   } else {
3591     XChangeProperty(pEv->display, pEv->requestor,
3592                     pEv->property, XA_STRING, 8, PropModeReplace,
3593                     pDpyInfo->winSelText, strlen(pDpyInfo->winSelText));
3594     SendSelectionNotify(pEv);
3595   }
3596   return False;
3597 } /* END ProcessSelectionRequest */
3598 
3599 /* This gets called back once the other X app has posted the property */
3600 static Bool ProcessSelectionNotifyW(dpy, pDpyInfo, pEv)
3601 Display *dpy;
3602 PDPYINFO pDpyInfo;
3603 XSelectionEvent *pEv;
3604 {
3605   Atom type;
3606   int  format;
3607   unsigned long nitems, after;
3608   unsigned char *prop;
3609   Bool success;
3610 
3611 #define DEFAULT_PROP_SIZE 1024L
3612 
3613   debug("selection notify\n");
3614   pDpyInfo->expectSelNotify = 0;
3615   /* property None indicates the source couldn't send anything */
3616   if (pEv->property != None) {
3617     /* Grab the property */
3618     success = False;
3619     if (XGetWindowProperty(dpy, pEv->requestor, XA_PRIMARY, 0L,
3620                            DEFAULT_PROP_SIZE, True, AnyPropertyType,
3621                            &type, &format, &nitems, &after, &prop)
3622         == Success) { /* got property */
3623       /* Check there is a string */
3624       /* XXX mdh -- emacs seems to give me type 233 but its useable */
3625       if ((type != None) &&
3626           ((type == XA_STRING) || !doClipCheck) &&
3627           (format != None) && (nitems != 0) &&
3628           (prop != None)) { /* known type */
3629         if (after == 0L) { /* got everything */
3630           success = True;
3631         } else { /* try to get everything */
3632           XFree(prop);
3633           /* The trick here is to find the whole size from what it told us */
3634           success =
3635             ((XGetWindowProperty(dpy, pEv->requestor, XA_PRIMARY, 0L,
3636                                  DEFAULT_PROP_SIZE + after + 1,
3637                                  True, AnyPropertyType,
3638                                  &type, &format, &nitems, &after, &prop)
3639               == Success) &&
3640              (type != None) && (format != None) && (nitems != 0) &&
3641              (after == 0L) && (prop != None));
3642         } /* END if got everything ... else ...*/
3643       } /* END if known type */
3644       else debug("Bad prop: type %d, format %d, nitems %d, prop %d(%s)\n",
3645                   (int)type, format, (int)nitems, (int)prop,
3646                   (prop == None) ? "none": (char *)prop);
3647     } /* END if got property */
3648       else debug("Did not get property\n");
3649 
3650     if (success) { /* send bits to the Windows Clipboard */
3651       debug("Send X selection to Windows Clipboard: %s\n", prop);
3652       if (OpenClipboard(pDpyInfo->edgewindow)) {
3653         HGLOBAL hglbCopy;
3654         EmptyClipboard();
3655         hglbCopy = GlobalAlloc(GMEM_MOVEABLE,
3656                                (strlen(prop) + 1) * sizeof(char));
3657         if (hglbCopy != NULL)
3658         {
3659           // Lock the handle and copy the text to the buffer.
3660           LPTSTR lptstrCopy = GlobalLock(hglbCopy);
3661           strcpy(lptstrCopy, prop);
3662           GlobalUnlock(hglbCopy);
3663           // Place the handle on the clipboard.
3664           SetClipboardData(CF_TEXT, hglbCopy);
3665           pDpyInfo->expectOwnClip = strlen(prop) + 1;
3666         }
3667         CloseClipboard();
3668       } /* END if open ok */
3669       XFree(prop);
3670     }
3671   }/* end ev->property != None */
3672   return False;
3673 } /* END ProcessSelectionNotify */
3674 
3675 #endif /* WIN_2_X only routines */
3676 
3677 static void *xmalloc(size)
3678 size_t size;
3679 {
3680   void * ptr = malloc(size);
3681   if (!ptr) {
3682     fprintf(stderr, "%s - error: %s\n", programStr, strerror(errno));
3683     exit(1);
3684   }
3685   return memset(ptr, 0, size);
3686 }
3687