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