source: trunk/synergy/lib/platform/CPMScreen.cpp@ 2762

Last change on this file since 2762 was 2762, checked in by bird, 19 years ago

quick and dirty mouse motion.

File size: 30.9 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2002 Chris Schoeneman
4 * Copyright (C) 2006 Knut St. Osmundsen
5 *
6 * This package is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * found in the file COPYING that should have accompanied this file.
9 *
10 * This package is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include "CPMScreen.h"
17#include "CPMClipboard.h"
18#include "CPMEventQueueBuffer.h"
19#include "CPMKeyState.h"
20#include "CPMScreenSaver.h"
21#include "CClipboard.h"
22#include "CKeyMap.h"
23#include "XScreen.h"
24#include "CLock.h"
25#include "CThread.h"
26#include "CFunctionJob.h"
27#include "CLog.h"
28#include "CString.h"
29#include "CStringUtil.h"
30#include "IEventQueue.h"
31#include "TMethodEventJob.h"
32#include "TMethodJob.h"
33#include "CArch.h"
34#include "CPMUtil.h"
35#include <string.h>
36#include <unistd.h>
37
38//
39// CPMScreen
40//
41
42CPMScreen* CPMScreen::s_screen = NULL;
43
44CPMScreen::CPMScreen(bool isPrimary) :
45 m_isPrimary(isPrimary),
46 m_isOnScreen(m_isPrimary),
47 m_x(0), m_y(0),
48 m_cx(0), m_cy(0),
49 m_xCenter(0), m_yCenter(0),
50 m_multimon(false),
51 m_xCursor(0), m_yCursor(0),
52 m_sequenceNumber(0),
53 m_mark(0),
54 m_markReceived(0),
55 m_screensaver(NULL),
56 m_screensaverNotify(false),
57 m_screensaverActive(false),
58 m_window(NULLHANDLE),
59 m_nextClipboardWindow(NULLHANDLE),
60 m_ownClipboard(false),
61 m_hmodHook(NULLHANDLE),
62 m_init(NULL),
63 m_cleanup(NULL),
64 m_setSides(NULL),
65 m_setZone(NULL),
66 m_setMode(NULL),
67 m_keyState(NULL),
68 m_hasMouse(WinQuerySysValue(HWND_DESKTOP, SV_MOUSEPRESENT) != FALSE),
69 m_showingMouse(false)
70{
71 assert(s_screen == NULL);
72 s_screen = this;
73
74 // create the event queue buffer first so we know there is a message queue.
75 CPMEventQueueBuffer *eventQueue = new CPMEventQueueBuffer();
76
77 // query curren thread bits.
78 m_threadID = _gettid();
79 m_hab = WinQueryAnchorBlock(HWND_DESKTOP);
80 //if (m_hab == NULLHANDLE) {
81 // m_hab = WinInitialize(0);
82 //}
83 m_hmq = WinQueueFromID(m_hab, getpid(), _gettid());
84 //if (m_hab != NULLHANDLE && m_hmq == NULLHANDLE) {
85 // m_hmq = WinCreateMsgQueue(m_hab, 0);
86 //}
87 if (m_hab == NULLHANDLE || m_hmq == NULLHANDLE) {
88 LOG((CLOG_CRIT "couldn't get the hab(%ld)/hmq(%ld) of the current thread! %d", m_hab, m_hmq));
89 delete eventQueue;
90 s_screen = NULL;
91 throw XScreenOpenFailure();
92 }
93
94 try {
95 if (m_isPrimary)
96 m_hmodHook = openHookLibrary("synrgyhk");
97 m_screensaver = new CPMScreenSaver();
98 m_keyState = new CPMKeyState(getEventTarget());
99 updateScreenShape();
100 m_window = createWindow("Synergy");
101 forceShowCursor();
102 LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_cx, m_cy, m_multimon ? "(multi-monitor)" : ""));
103 LOG((CLOG_DEBUG "window is 0x%08x", m_window));
104 } catch (...) {
105 delete m_keyState;
106 delete m_screensaver;
107 destroyWindow(m_window);
108 closeHookLibrary(m_hmodHook);
109 delete eventQueue;
110 s_screen = NULL;
111 throw;
112 }
113
114 // install event handlers
115 EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(),
116 new TMethodEventJob<CPMScreen>(this, &CPMScreen::handleSystemEvent));
117
118 // install the platform event queue
119 EVENTQUEUE->adoptBuffer(new CPMEventQueueBuffer);
120}
121
122CPMScreen::~CPMScreen()
123{
124 assert(s_screen != NULL);
125
126 disable();
127 EVENTQUEUE->adoptBuffer(NULL);
128 EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget());
129 delete m_keyState;
130 delete m_screensaver;
131 destroyWindow(m_window);
132 closeHookLibrary(m_hmodHook);
133 s_screen = NULL;
134}
135
136void
137CPMScreen::enable()
138{
139 assert(m_isOnScreen == m_isPrimary);
140
141 // install our clipboard snooper
142 HAB hab = CPMUtil::getHAB();
143 m_nextClipboardWindow = WinQueryClipbrdViewer(hab);
144 WinSetClipbrdViewer(hab, m_window);
145
146 if (m_isPrimary) {
147 // set jump zones
148 m_setZone(m_x, m_y, m_cx, m_cy, getJumpZoneSize());
149
150 // watch jump zones
151 m_setMode(kHOOK_WATCH_JUMP_ZONE);
152 }
153}
154
155void
156CPMScreen::disable()
157{
158 if (m_isPrimary) {
159 // disable hooks
160 m_setMode(kHOOK_DISABLE);
161 }
162
163 // tell key state
164 m_keyState->disable();
165
166 // stop snooping the clipboard
167 WinSetClipbrdViewer(m_window, m_nextClipboardWindow);
168 m_nextClipboardWindow = NULL;
169
170 m_isOnScreen = m_isPrimary;
171 forceShowCursor();
172}
173
174void
175CPMScreen::enter()
176{
177 // show the pointer (?), hide the synergy window and restore focus.
178 WinShowPointer(HWND_DESKTOP, TRUE);
179 WinSetWindowPos(m_window, HWND_BOTTOM, 0, 0, 0, 0, SWP_HIDE);
180 //?? WinSetFocus(
181 //?? WinEnableWindow
182
183 if (m_isPrimary) {
184 // watch jump zones
185 m_setMode(kHOOK_WATCH_JUMP_ZONE);
186
187 // all messages prior to now are invalid
188 nextMark();
189 }
190
191 // now on screen
192 m_isOnScreen = true;
193 forceShowCursor();
194}
195
196bool
197CPMScreen::leave()
198{
199 /// @todo save focus window.
200
201 if (m_isPrimary) {
202 // warp to center
203 warpCursor(m_xCenter, m_yCenter);
204
205 // all messages prior to now are invalid
206 nextMark();
207
208 // remember the modifier state. this is the modifier state
209 // reflected in the internal keyboard state.
210 m_keyState->saveModifiers();
211
212 // capture events
213 m_setMode(kHOOK_RELAY_EVENTS);
214 }
215
216 // now off screen
217 m_isOnScreen = false;
218 forceShowCursor();
219
220 return true;
221}
222
223bool
224CPMScreen::setClipboard(ClipboardID, const IClipboard* src)
225{
226 CPMClipboard dst(m_window);
227 if (src != NULL) {
228 // save clipboard data
229 return CClipboard::copy(&dst, src);
230 }
231 // assert clipboard ownership
232 if (!dst.open(0)) {
233 return false;
234 }
235 dst.empty();
236 dst.close();
237 return true;
238}
239
240void
241CPMScreen::checkClipboards()
242{
243 // be careful, even if we don't have the NT bugs.
244 if (m_ownClipboard && !CPMClipboard::isOwnedBySynergy()) {
245 LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
246 m_ownClipboard = false;
247 sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
248 sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
249 }
250}
251
252void
253CPMScreen::openScreensaver(bool notify)
254{
255 m_screensaverNotify = notify;
256 if (!m_screensaverNotify)
257 m_screensaver->disable();
258}
259
260void
261CPMScreen::closeScreensaver()
262{
263 if (!m_screensaverNotify)
264 m_screensaver->enable();
265}
266
267void
268CPMScreen::screensaver(bool activate)
269{
270 if (activate)
271 m_screensaver->activate();
272 else
273 m_screensaver->deactivate();
274}
275
276void
277CPMScreen::resetOptions()
278{
279//example m_xtestIsXineramaUnaware = true;
280}
281
282void
283CPMScreen::setOptions(const COptionsList& options)
284{
285 for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
286//example if (options[i] == kOptionXTestXineramaUnaware) {
287//example m_xtestIsXineramaUnaware = (options[i + 1] != 0);
288//example LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false"));
289//example }
290 }
291}
292
293void
294CPMScreen::setSequenceNumber(UInt32 seqNum)
295{
296 m_sequenceNumber = seqNum;
297}
298
299bool
300CPMScreen::isPrimary() const
301{
302 return m_isPrimary;
303}
304
305void*
306CPMScreen::getEventTarget() const
307{
308 return const_cast<CPMScreen*>(this);
309}
310
311bool
312CPMScreen::getClipboard(ClipboardID, IClipboard* dst) const
313{
314 CPMClipboard src(m_window);
315 return CClipboard::copy(dst, &src);
316}
317
318void
319CPMScreen::getShape(SInt32& x, SInt32& y, SInt32& cx, SInt32& cy) const
320{
321 x = m_x;
322 y = m_y;
323 cx= m_cx;
324 cy = m_cy;
325}
326
327void
328CPMScreen::getCursorPos(SInt32& x, SInt32& y) const
329{
330 CURSORINFO Info;
331 if (WinQueryCursorInfo(HWND_DESKTOP, &Info)) {
332 x = Info.x;
333 y = m_cy - Info.y;
334 } else {
335 x = 0;
336 y = 0;
337 }
338}
339
340void
341CPMScreen::reconfigure(UInt32 activeSides)
342{
343 // do nothing.
344}
345
346void
347CPMScreen::warpCursor(SInt32 x, SInt32 y)
348{
349 // warp mouse
350 warpCursorNoFlush(x, y);
351
352 // remove all input events before and including warp
353 QMSG qmsg;
354 while (WinPeekMsg(m_hab, &qmsg, NULLHANDLE, SYNERGY_PM_MSG_INPUT_FIRST, SYNERGY_PM_MSG_INPUT_LAST, PM_REMOVE)) {
355 // do nothing
356 }
357
358 // save position as last position
359 m_xCursor = x;
360 m_yCursor = y;
361}
362
363UInt32
364CPMScreen::registerHotKey(KeyID key, KeyModifierMask mask)
365{
366 // only allow certain modifiers
367 if ((mask & ~(KeyModifierShift | KeyModifierControl |
368 KeyModifierAlt | KeyModifierSuper)) != 0) {
369 LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
370 return 0;
371 }
372
373 // fail if no keys
374 if (key == kKeyNone && mask == 0) {
375 return 0;
376 }
377
378#if 1
379 LOG((CLOG_WARN "not implemented id=%04x mask=%04x", key, mask));
380 return 0;
381#else
382 // convert to win32
383 UINT modifiers = 0;
384 if ((mask & KeyModifierShift) != 0) {
385 modifiers |= MOD_SHIFT;
386 }
387 if ((mask & KeyModifierControl) != 0) {
388 modifiers |= MOD_CONTROL;
389 }
390 if ((mask & KeyModifierAlt) != 0) {
391 modifiers |= MOD_ALT;
392 }
393 if ((mask & KeyModifierSuper) != 0) {
394 modifiers |= MOD_WIN;
395 }
396 UINT vk = m_keyState->mapKeyToVirtualKey(key);
397 if (key != kKeyNone && vk == 0) {
398 // can't map key
399 LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
400 return 0;
401 }
402
403 // choose hotkey id
404 UInt32 id;
405 if (!m_oldHotKeyIDs.empty()) {
406 id = m_oldHotKeyIDs.back();
407 m_oldHotKeyIDs.pop_back();
408 }
409 else {
410 id = m_hotKeys.size() + 1;
411 }
412
413 // if this hot key has modifiers only then we'll handle it specially
414 bool err;
415 if (key == kKeyNone) {
416 // check if already registered
417 err = (m_hotKeyToIDMap.count(CHotKeyItem(vk, modifiers)) > 0);
418 }
419 else {
420 // register with OS
421 err = (RegisterHotKey(NULL, id, modifiers, vk) == 0);
422 }
423
424 if (!err) {
425 m_hotKeys.insert(std::make_pair(id, CHotKeyItem(vk, modifiers)));
426 m_hotKeyToIDMap[CHotKeyItem(vk, modifiers)] = id;
427 }
428 else {
429 m_oldHotKeyIDs.push_back(id);
430 m_hotKeys.erase(id);
431 LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask));
432 return 0;
433 }
434
435 LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id));
436 return id;
437#endif
438}
439
440void
441CPMScreen::unregisterHotKey(UInt32 id)
442{
443 // look up hotkey
444 HotKeyMap::iterator i = m_hotKeys.find(id);
445 if (i == m_hotKeys.end()) {
446 return;
447 }
448
449 // unregister with OS
450 bool err;
451#if 1
452 err = true;
453#else
454 if (i->second.getVirtualKey() != 0) {
455 err = !UnregisterHotKey(NULL, id);
456 } else {
457 err = false;
458 }
459#endif
460 if (err) {
461 LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
462 }
463 else {
464 LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
465 }
466
467 // discard hot key from map and record old id for reuse
468 m_hotKeyToIDMap.erase(i->second);
469 m_hotKeys.erase(i);
470 m_oldHotKeyIDs.push_back(id);
471}
472
473void
474CPMScreen::fakeInputBegin()
475{
476 assert(m_isPrimary);
477
478 if (!m_isOnScreen) {
479 m_keyState->useSavedModifiers(true);
480 }
481 //m_desks->fakeInputBegin();
482}
483
484void
485CPMScreen::fakeInputEnd()
486{
487 assert(m_isPrimary);
488
489 //m_desks->fakeInputEnd();
490 if (!m_isOnScreen) {
491 m_keyState->useSavedModifiers(false);
492 }
493}
494
495SInt32
496CPMScreen::getJumpZoneSize() const
497{
498 return 1;
499}
500
501bool
502CPMScreen::isAnyMouseButtonDown() const
503{
504 static const char* buttonToName[] = {
505 "<invalid>",
506 "Left Button",
507 "Middle Button",
508 "Right Button",
509 "X Button 1",
510 "X Button 2"
511 };
512
513 for (UInt32 i = 1; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
514 if (m_buttons[i]) {
515 LOG((CLOG_DEBUG "locked by \"%s\"", buttonToName[i]));
516 return true;
517 }
518 }
519
520 return false;
521}
522
523void
524CPMScreen::getCursorCenter(SInt32& x, SInt32& y) const
525{
526 x = m_xCenter;
527 y = m_yCenter;
528}
529
530void
531CPMScreen::fakeMouseButton(ButtonID id, bool press) const
532{
533 //m_desks->fakeMouseButton(id, press);
534}
535
536void
537CPMScreen::fakeMouseMove(SInt32 x, SInt32 y) const
538{
539 LOG((CLOG_DEBUG "fakeMouseMove: %d,%d (y=%d)", x, m_cy - y, y));
540 if (WinSetPointerPos(HWND_DESKTOP, x, m_cy - y)) {
541 return;
542 }
543 LOG((CLOG_CRIT "fakeMouseMove: failed %#lx", WinGetLastError(CPMUtil::getHAB())));
544}
545
546void
547CPMScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
548{
549 POINTL ptl;
550 if (WinQueryPointerPos(HWND_DESKTOP, &ptl)) {
551 LOG((CLOG_DEBUG "fakeMouseRelativeMove: %d,%d (%d,%d +/- %d,%d)", ptl.x + dx, ptl.y - dy, ptl.y, ptl.x, dx, dy));
552 if (WinSetPointerPos(HWND_DESKTOP, ptl.x + dx, ptl.y - dy)) {
553 return;
554 }
555 }
556 LOG((CLOG_CRIT "fakeMouseRelativeMove: failed %#lx", WinGetLastError(CPMUtil::getHAB())));
557}
558
559void
560CPMScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
561{
562 //m_desks->fakeMouseWheel(xDelta, yDelta);
563}
564
565void
566CPMScreen::updateKeys()
567{
568 //m_desks->updateKeys();
569}
570
571void
572CPMScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
573 KeyButton button)
574{
575 CPlatformScreen::fakeKeyDown(id, mask, button);
576 updateForceShowCursor();
577}
578
579void
580CPMScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
581 SInt32 count, KeyButton button)
582{
583 CPlatformScreen::fakeKeyRepeat(id, mask, count, button);
584 updateForceShowCursor();
585}
586
587void
588CPMScreen::fakeKeyUp(KeyButton button)
589{
590 CPlatformScreen::fakeKeyUp(button);
591 updateForceShowCursor();
592}
593
594void
595CPMScreen::fakeAllKeysUp()
596{
597 CPlatformScreen::fakeAllKeysUp();
598 updateForceShowCursor();
599}
600
601HMODULE
602CPMScreen::openHookLibrary(const char* name)
603{
604 // load the hook library
605 HMODULE hmod;
606 APIRET rc = DosLoadModule(NULL, 0, (PCSZ)name, &hmod);
607 if (rc != NO_ERROR) {
608 LOG((CLOG_ERR "Failed to load hook library (%s), rc=%d ", name, rc));
609 throw XScreenOpenFailure();
610 }
611
612 // look up functions
613 if ( (rc = DosQueryProcAddr(hmod, 0, (PCSZ)"_setSides", (PPFN)&m_setSides)) != NO_ERROR
614 || (rc = DosQueryProcAddr(hmod, 0, (PCSZ)"_setZone", (PPFN)&m_setZone )) != NO_ERROR
615 || (rc = DosQueryProcAddr(hmod, 0, (PCSZ)"_setMode", (PPFN)&m_setMode )) != NO_ERROR
616 || (rc = DosQueryProcAddr(hmod, 0, (PCSZ)"_init", (PPFN)&m_init )) != NO_ERROR
617 || (rc = DosQueryProcAddr(hmod, 0, (PCSZ)"_cleanup", (PPFN)&m_cleanup )) != NO_ERROR) {
618 LOG((CLOG_ERR "Invalid hook library; use a newer %s.dll (rc=%d)", name, rc));
619 DosFreeModule(hmod);
620 throw XScreenOpenFailure();
621 }
622
623 // initialize hook library
624 if (m_init(m_hmq, _gettid(), getpid()) == 0) {
625 LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?"));
626 DosFreeModule(hmod);
627 throw XScreenOpenFailure();
628 }
629 return hmod;
630}
631
632void
633CPMScreen::closeHookLibrary(HMODULE hmod) const
634{
635 if (hmod != NULL) {
636 m_cleanup(m_hab);
637 DosFreeModule(hmod);
638 }
639}
640
641HWND
642CPMScreen::createWindow(const char* name)
643{
644 WinRegisterClass(m_hab, (PCSZ)"Synergy", CPMScreen::wndProc, CS_MOVENOTIFY, sizeof(uintptr_t));
645 HWND hwnd = WinCreateWindow(HWND_DESKTOP,
646 (PCSZ)"Synergy",
647 NULL,
648 0, //WS_?,
649 0, 0, 1, 1,
650 NULLHANDLE,
651 HWND_TOP,
652 0,
653 this,
654 NULL);
655 if (hwnd == NULLHANDLE) {
656 LOG((CLOG_ERR "failed to create window: %lx", WinGetLastError(m_hab)));
657 throw XScreenOpenFailure();
658 }
659 return hwnd;
660}
661
662void
663CPMScreen::destroyWindow(HWND hwnd) const
664{
665 if (hwnd != NULL) {
666 WinDestroyWindow(hwnd);
667 }
668}
669
670void
671CPMScreen::sendEvent(CEvent::Type type, void* data)
672{
673 EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
674}
675
676void
677CPMScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id)
678{
679 CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo));
680 info->m_id = id;
681 info->m_sequenceNumber = m_sequenceNumber;
682 sendEvent(type, info);
683}
684
685void
686CPMScreen::handleSystemEvent(const CEvent& event, void*)
687{
688 PQMSG pqmsg = (PQMSG)event.getData();
689 assert(pqmsg != NULL);
690 if (onPreDispatch(pqmsg->hwnd, pqmsg->msg, pqmsg->mp1, pqmsg->mp2))
691 return;
692 WinDispatchMsg(CPMUtil::getHAB(), pqmsg);
693}
694
695void
696CPMScreen::updateButtons()
697{
698 m_buttons[kButtonNone] = false;
699 m_buttons[kButtonLeft] = (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) != 0;
700 m_buttons[kButtonRight] = (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000) != 0;
701 m_buttons[kButtonMiddle] = (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000) != 0;
702 m_buttons[kButtonExtra0 + 0] = false;
703 m_buttons[kButtonExtra0 + 1] = false;
704}
705
706IKeyState*
707CPMScreen::getKeyState() const
708{
709 return m_keyState;
710}
711
712bool
713CPMScreen::onPreDispatch(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
714{
715 // handle event
716 switch (msg) {
717 case SYNERGY_PM_MSG_DEBUG:
718 LOG((CLOG_DEBUG1 "hook: 0x%08x 0x%08x", mp1, mp2));
719 return true;
720 }
721
722 if (m_isPrimary) {
723 return onPreDispatchPrimary(hwnd, msg, mp1, mp2);
724 }
725
726 return false;
727}
728
729bool
730CPMScreen::onPreDispatchPrimary(HWND, ULONG msg, MPARAM mp1, MPARAM mp2)
731{
732 // handle event
733 switch (msg) {
734 case SYNERGY_PM_MSG_MARK:
735 return onMark((uintptr_t)mp1);
736
737 case SYNERGY_PM_MSG_KEY:
738 return onKey(SHORT1FROMMP(mp1), CHAR3FROMMP(mp1), CHAR4FROMMP(mp1), SHORT1FROMMP(mp2), SHORT2FROMMP(mp2));
739
740 case SYNERGY_PM_MSG_MOUSE_BUTTON:
741 return onMouseButton(SHORT1FROMMP(mp1), (SHORT)SHORT1FROMMP(mp2), m_cy - (SHORT)SHORT2FROMMP(mp2));
742
743 case SYNERGY_PM_MSG_MOUSE_MOVE:
744 return onMouseMove(SHORT1FROMMP(mp1), (SHORT)SHORT1FROMMP(mp2), m_cy - (SHORT)SHORT2FROMMP(mp2));
745
746 case SYNERGY_PM_MSG_PRE_WARP:
747 {
748 // Save position to compute delta of next motion
749 m_xCursor = (SInt32)mp1;
750 m_yCursor = (SInt32)mp2;
751
752 // We warped the mouse. Discard events until we find the
753 // matching post warp event. See warpCursorNoFlush() for
754 // where the events are sent. We discard the matching
755 // post warp event and can be sure we've skipped the warp
756 // event.
757 QMSG qmsg;
758 while ( WinGetMsg(m_hab, &qmsg, NULLHANDLE, SYNERGY_PM_MSG_MOUSE_MOVE, SYNERGY_PM_MSG_POST_WARP)
759 && qmsg.msg != SYNERGY_PM_MSG_POST_WARP)
760 /* nothing */;
761 }
762 return true;
763
764 case SYNERGY_PM_MSG_POST_WARP:
765 LOG((CLOG_WARN "unmatched post warp"));
766 return true;
767 }
768
769 return false;
770}
771
772bool
773CPMScreen::onEvent(HWND, ULONG msg, MPARAM mp1, MPARAM mp2, MRESULT *result)
774{
775 switch (msg) {
776 case WM_DRAWCLIPBOARD:
777 //// first pass on the message
778 //if (m_nextClipboardWindow != NULL) {
779 // WinSendMessage(m_nextClipboardWindow, msg, mp1, mp2);
780 //}
781
782 // now handle the message
783 return onClipboardChange();
784 }
785
786 return false;
787}
788
789bool
790CPMScreen::onMark(UInt32 mark)
791{
792 m_markReceived = mark;
793 return true;
794}
795
796bool
797CPMScreen::onKey(USHORT fsFlags, UCHAR ucRepeat, UCHAR ucScanCode, USHORT usch, USHORT usvk)
798{
799#if 0
800 static const KeyModifierMask s_ctrlAlt = KeyModifierControl | KeyModifierAlt;
801
802 LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, mp2=0x%08x", (mp1 & 0xff00u) >> 8, mp1 & 0xffu, (mp1 & 0x10000u) ? 1 : 0, mp2));
803
804 // get event info
805 KeyButton button = (KeyButton)((mp2 & 0x01ff0000) >> 16);
806 bool down = ((mp2 & 0x80000000u) == 0x00000000u);
807 bool wasDown = isKeyDown(button);
808 KeyModifierMask oldState = pollActiveModifiers();
809
810 // check for autorepeat
811 if (m_keyState->testAutoRepeat(down, (mp2 & 0x40000000u) == 1, button)) {
812 mp2 |= 0x40000000u;
813 }
814
815 // if the button is zero then guess what the button should be.
816 // these are badly synthesized key events and logitech software
817 // that maps mouse buttons to keys is known to do this.
818 // alternatively, we could just throw these events out.
819 if (button == 0) {
820 button = m_keyState->virtualKeyToButton(mp1 & 0xffu);
821 if (button == 0) {
822 return true;
823 }
824 wasDown = isKeyDown(button);
825 }
826
827 // record keyboard state
828 m_keyState->onKey(button, down, oldState);
829
830 // windows doesn't tell us the modifier key state on mouse or key
831 // events so we have to figure it out. most apps would use
832 // GetKeyState() or even GetAsyncKeyState() for that but we can't
833 // because our hook doesn't pass on key events for several modifiers.
834 // it can't otherwise the system would interpret them normally on
835 // the primary screen even when on a secondary screen. so tapping
836 // alt would activate menus and tapping the windows key would open
837 // the start menu. if you don't pass those events on in the hook
838 // then GetKeyState() understandably doesn't reflect the effect of
839 // the event. curiously, neither does GetAsyncKeyState(), which is
840 // surprising.
841 //
842 // so anyway, we have to track the modifier state ourselves for
843 // at least those modifiers we don't pass on. pollActiveModifiers()
844 // does that but we have to update the keyboard state before calling
845 // pollActiveModifiers() to get the right answer. but the only way
846 // to set the modifier state or to set the up/down state of a key
847 // is via onKey(). so we have to call onKey() twice.
848 KeyModifierMask state = pollActiveModifiers();
849 m_keyState->onKey(button, down, state);
850
851 // check for hot keys
852 if (oldState != state) {
853 // modifier key was pressed/released
854 if (onHotKey(0, mp2)) {
855 return true;
856 }
857 }
858 else {
859 // non-modifier was pressed/released
860 if (onHotKey(mp1, mp2)) {
861 return true;
862 }
863 }
864
865 // ignore message if posted prior to last mark change
866 if (!ignore()) {
867 // check for ctrl+alt+del. we do not want to pass that to the
868 // client. the user can use ctrl+alt+pause to emulate it.
869 UINT virtKey = (mp1 & 0xffu);
870 if (virtKey == VK_DELETE && (state & s_ctrlAlt) == s_ctrlAlt) {
871 LOG((CLOG_DEBUG "discard ctrl+alt+del"));
872 return true;
873 }
874
875 // check for ctrl+alt+del emulation
876 if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) &&
877 (state & s_ctrlAlt) == s_ctrlAlt) {
878 LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
879 // switch mp1 and mp2 to be as if VK_DELETE was
880 // pressed or released. when mapping the key we require that
881 // we not use AltGr (the 0x10000 flag in mp1) and we not
882 // use the keypad delete key (the 0x01000000 flag in mp2).
883 mp1 = VK_DELETE | 0x00010000u;
884 mp2 &= 0xfe000000;
885 mp2 |= m_keyState->virtualKeyToButton(mp1 & 0xffu) << 16;
886 mp2 |= 0x01000001;
887 }
888
889 // process key
890 KeyModifierMask mask;
891 KeyID key = m_keyState->mapKeyFromEvent((ULONG)mp1, (ULONG)mp2, &mask);
892 button = static_cast<KeyButton>((mp2 & 0x01ff0000u) >> 16);
893 if (key != kKeyNone) {
894 // fix key up. if the key isn't down according to
895 // our table then we never got the key press event
896 // for it. if it's not a modifier key then we'll
897 // synthesize the press first. only do this on
898 // the windows 95 family, which eats certain special
899 // keys like alt+tab, ctrl+esc, etc.
900 if (m_is95Family && !wasDown && !down) {
901 switch (virtKey) {
902 case VK_SHIFT:
903 case VK_LSHIFT:
904 case VK_RSHIFT:
905 case VK_CONTROL:
906 case VK_LCONTROL:
907 case VK_RCONTROL:
908 case VK_MENU:
909 case VK_LMENU:
910 case VK_RMENU:
911 case VK_LWIN:
912 case VK_RWIN:
913 case VK_CAPITAL:
914 case VK_NUMLOCK:
915 case VK_SCROLL:
916 break;
917
918 default:
919 m_keyState->sendKeyEvent(getEventTarget(),
920 true, false, key, mask, 1, button);
921 break;
922 }
923 }
924
925 // do it
926 m_keyState->sendKeyEvent(getEventTarget(),
927 ((mp2 & 0x80000000u) == 0),
928 ((mp2 & 0x40000000u) != 0),
929 key, mask, (SInt32)(mp2 & 0xffff), button);
930 }
931 else {
932 LOG((CLOG_DEBUG1 "cannot map key"));
933 }
934 }
935#endif
936 return true;
937}
938
939bool
940CPMScreen::onHotKey(MPARAM mp1, MPARAM mp2)
941{
942#if 0
943 // get the key info
944 KeyModifierMask state = getActiveModifiers();
945 UINT virtKey = (mp1 & 0xffu);
946 UINT modifiers = 0;
947 if ((state & KeyModifierShift) != 0) {
948 modifiers |= MOD_SHIFT;
949 }
950 if ((state & KeyModifierControl) != 0) {
951 modifiers |= MOD_CONTROL;
952 }
953 if ((state & KeyModifierAlt) != 0) {
954 modifiers |= MOD_ALT;
955 }
956 if ((state & KeyModifierSuper) != 0) {
957 modifiers |= MOD_WIN;
958 }
959
960 // find the hot key id
961 HotKeyToIDMap::const_iterator i =
962 m_hotKeyToIDMap.find(CHotKeyItem(virtKey, modifiers));
963 if (i == m_hotKeyToIDMap.end()) {
964 return false;
965 }
966
967 // find what kind of event
968 CEvent::Type type;
969 if ((mp2 & 0x80000000u) == 0u) {
970 if ((mp2 & 0x40000000u) != 0u) {
971 // ignore key repeats but it counts as a hot key
972 return true;
973 }
974 type = getHotKeyDownEvent();
975 }
976 else {
977 type = getHotKeyUpEvent();
978 }
979
980 // generate event
981 EVENTQUEUE->addEvent(CEvent(type, getEventTarget(),
982 CHotKeyInfo::alloc(i->second)));
983
984#endif
985 return true;
986}
987
988bool
989CPMScreen::onMouseButton(ULONG msg, SInt32 ax, SInt32 ay)
990{
991 // get which button
992 bool pressed = mapPressFromEvent(msg);
993 ButtonID button = mapButtonFromEvent(msg);
994
995 // keep our shadow key state up to date
996 if (button >= kButtonLeft && button <= kButtonExtra0 + 1) {
997 if (pressed) {
998 m_buttons[button] = true;
999 }
1000 else {
1001 m_buttons[button] = false;
1002 }
1003 }
1004
1005 // ignore message if posted prior to last mark change
1006 if (!ignore()) {
1007 KeyModifierMask mask = m_keyState->getActiveModifiers();
1008 if (pressed) {
1009 LOG((CLOG_DEBUG1 "event: button press button=%d", button));
1010 if (button != kButtonNone) {
1011 sendEvent(getButtonDownEvent(),
1012 CButtonInfo::alloc(button, mask));
1013 }
1014 }
1015 else {
1016 LOG((CLOG_DEBUG1 "event: button release button=%d", button));
1017 if (button != kButtonNone) {
1018 sendEvent(getButtonUpEvent(),
1019 CButtonInfo::alloc(button, mask));
1020 }
1021 }
1022 }
1023
1024 return true;
1025}
1026
1027bool
1028CPMScreen::onMouseMove(ULONG msg, SInt32 ax, SInt32 ay)
1029{
1030 // compute motion delta (relative to the last known
1031 // mouse position)
1032 SInt32 x = ax - m_xCursor;
1033 SInt32 y = ay - m_yCursor;
1034
1035 // ignore if the mouse didn't move or if message posted prior
1036 // to last mark change.
1037 if (ignore() || (x == 0 && y == 0)) {
1038 return true;
1039 }
1040
1041 // save position to compute delta of next motion
1042 m_xCursor = ax;
1043 m_yCursor = ay;
1044
1045 if (m_isOnScreen) {
1046 // motion on primary screen
1047 sendEvent(getMotionOnPrimaryEvent(), CMotionInfo::alloc(m_xCursor, m_yCursor));
1048 }
1049 else {
1050 // motion on secondary screen. warp mouse back to
1051 // center.
1052 warpCursorNoFlush(m_xCenter, m_yCenter);
1053
1054 // examine the motion. if it's about the distance
1055 // from the center of the screen to an edge then
1056 // it's probably a bogus motion that we want to
1057 // ignore (see warpCursorNoFlush() for a further
1058 // description).
1059 static SInt32 bogusZoneSize = 10;
1060 if (-x + bogusZoneSize > m_xCenter - m_x ||
1061 x + bogusZoneSize > m_x + m_cx - m_xCenter ||
1062 -y + bogusZoneSize > m_yCenter - m_y ||
1063 y + bogusZoneSize > m_y + m_cy - m_yCenter) {
1064 LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y));
1065 }
1066 else {
1067 // send motion
1068 sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y));
1069 }
1070 }
1071
1072 return true;
1073}
1074
1075bool
1076CPMScreen::onClipboardChange()
1077{
1078 // now notify client that somebody changed the clipboard (unless
1079 // we're the owner).
1080 if (!CPMClipboard::isOwnedBySynergy()) {
1081 if (m_ownClipboard) {
1082 LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
1083 m_ownClipboard = false;
1084 sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
1085 sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
1086 }
1087 }
1088 else if (!m_ownClipboard) {
1089 LOG((CLOG_DEBUG "clipboard changed: synergy owned"));
1090 m_ownClipboard = true;
1091 }
1092
1093 return true;
1094}
1095
1096void
1097CPMScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
1098{
1099 // send an event that we can recognize before the mouse warp
1100 WinPostQueueMsg(m_hmq, SYNERGY_PM_MSG_PRE_WARP, (MPARAM)x, (MPARAM)y);
1101
1102 // warp mouse. hopefully this inserts a mouse motion event
1103 // between the previous message and the following message.
1104 WinSetPointerPos(HWND_DESKTOP, x, y);
1105
1106 // yield the CPU. there's a race condition when warping:
1107 // a hardware mouse event occurs
1108 // the mouse hook is not called because that process doesn't have the CPU
1109 // we send PRE_WARP, SetCursorPos(), send POST_WARP
1110 // we process all of those events and update m_x, m_y
1111 // we finish our time slice
1112 // the hook is called
1113 // the hook sends us a mouse event from the pre-warp position
1114 // we get the CPU
1115 // we compute a bogus warp
1116 // we need the hook to process all mouse events that occur
1117 // before we warp before we do the warp but i'm not sure how
1118 // to guarantee that. yielding the CPU here may reduce the
1119 // chance of undesired behavior. we'll also check for very
1120 // large motions that look suspiciously like about half width
1121 // or height of the screen.
1122 ARCH->sleep(0.0);
1123
1124 // send an event that we can recognize after the mouse warp
1125 WinPostQueueMsg(m_hmq, SYNERGY_PM_MSG_POST_WARP, 0, 0);
1126}
1127
1128void
1129CPMScreen::nextMark()
1130{
1131 // next mark
1132 ++m_mark;
1133
1134 // mark point in message queue where the mark was changed
1135 WinPostQueueMsg(m_hmq, SYNERGY_PM_MSG_MARK, (MPARAM)m_mark, 0);
1136}
1137
1138bool
1139CPMScreen::ignore() const
1140{
1141 return (m_mark != m_markReceived);
1142}
1143
1144void
1145CPMScreen::updateScreenShape()
1146{
1147 // get shape
1148 m_x = 0;
1149 m_y = 0;
1150 m_cx = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
1151 m_cy = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
1152
1153 // get center for cursor
1154 m_xCenter = m_cx / 2;
1155 m_yCenter = m_cy / 2;
1156
1157 // check for multiple monitors
1158 m_multimon = false;
1159}
1160
1161ButtonID
1162CPMScreen::mapButtonFromEvent(ULONG msg) const
1163{
1164 /** @todo query which is left and which is right. */
1165
1166 switch (msg) {
1167 case WM_BUTTON1DOWN:
1168 case WM_BUTTON1UP:
1169 case WM_BUTTON1DBLCLK:
1170 case WM_BUTTON1CLICK: //??
1171 return kButtonLeft;
1172
1173 case WM_BUTTON2DOWN:
1174 case WM_BUTTON2UP:
1175 case WM_BUTTON2DBLCLK:
1176 case WM_BUTTON2CLICK: //??
1177 return kButtonLeft;
1178
1179 case WM_BUTTON3DOWN:
1180 case WM_BUTTON3UP:
1181 case WM_BUTTON3DBLCLK:
1182 case WM_BUTTON3CLICK: //??
1183 return kButtonMiddle;
1184 default:
1185 return kButtonNone;
1186 }
1187}
1188
1189bool
1190CPMScreen::mapPressFromEvent(ULONG msg) const
1191{
1192 switch (msg) {
1193 case WM_BUTTON1DOWN:
1194 case WM_BUTTON1CLICK: //??
1195 case WM_BUTTON1DBLCLK:
1196 case WM_BUTTON2DOWN:
1197 case WM_BUTTON2DBLCLK:
1198 case WM_BUTTON2CLICK: //??
1199 case WM_BUTTON3DOWN:
1200 case WM_BUTTON3DBLCLK:
1201 case WM_BUTTON3CLICK: //??
1202 return true;
1203 case WM_BUTTON1UP:
1204 case WM_BUTTON2UP:
1205 case WM_BUTTON3UP:
1206 return false;
1207 default:
1208 return false;
1209 }
1210}
1211
1212void
1213CPMScreen::updateKeysCB(void*)
1214{
1215 // record which keys we think are down
1216 bool down[IKeyState::kNumButtons];
1217 bool sendFixes = (isPrimary() && !m_isOnScreen);
1218 if (sendFixes) {
1219 for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
1220 down[i] = m_keyState->isKeyDown(i);
1221 }
1222 }
1223
1224 // now update the keyboard state
1225 CPlatformScreen::updateKeyState();
1226
1227 // now see which keys we thought were down but now think are up.
1228 // send key releases for these keys to the active client.
1229 if (sendFixes) {
1230 KeyModifierMask mask = pollActiveModifiers();
1231 for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
1232 if (down[i] && !m_keyState->isKeyDown(i)) {
1233 m_keyState->sendKeyEvent(getEventTarget(),
1234 false, false, kKeyNone, mask, 1, i);
1235 }
1236 }
1237 }
1238}
1239
1240void
1241CPMScreen::forceShowCursor()
1242{
1243 // check for mouse - probably not required.
1244 m_hasMouse = WinQuerySysValue(HWND_DESKTOP, SV_MOUSEPRESENT) != FALSE;
1245
1246 // decide if we should show the mouse
1247 bool showMouse = (!m_hasMouse && !m_isPrimary && m_isOnScreen);
1248
1249 // show/hide the mouse
1250 if (showMouse != m_showingMouse) {
1251 WinShowPointer(HWND_DESKTOP, showMouse);
1252 m_showingMouse = WinQuerySysValue(HWND_DESKTOP, SV_POINTERLEVEL) != 0;
1253 }
1254}
1255
1256void
1257CPMScreen::updateForceShowCursor()
1258{
1259 //???
1260}
1261
1262MRESULT EXPENTRY
1263CPMScreen::wndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1264{
1265 assert(s_screen != NULL);
1266
1267 MRESULT mr = 0;
1268 if (!s_screen->onEvent(hwnd, msg, mp1, mp2, &mr)) {
1269 mr = WinDefWindowProc(hwnd, msg, mp1, mp2);
1270 }
1271
1272 return mr;
1273}
1274
1275
1276//
1277// CPMScreen::CHotKeyItem
1278//
1279
1280CPMScreen::CHotKeyItem::CHotKeyItem(ULONG keycode, ULONG mask) :
1281 m_keycode(keycode),
1282 m_mask(mask)
1283{
1284 // do nothing
1285}
1286
1287ULONG
1288CPMScreen::CHotKeyItem::getVirtualKey() const
1289{
1290 return m_keycode;
1291}
1292
1293bool
1294CPMScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const
1295{
1296 return (m_keycode < x.m_keycode ||
1297 (m_keycode == x.m_keycode && m_mask < x.m_mask));
1298}
1299
1300
1301/*
1302 * Local Variables:
1303 * mode: c
1304 * c-file-style: "k&r"
1305 * c-basic-offset: 4
1306 * tab-width: 4
1307 * indent-tabs-mode: t
1308 * End:
1309 */
1310
Note: See TracBrowser for help on using the repository browser.