Moved CPrimaryScreen and CSecondaryScreen to the lib/synergy

and the platform specific implementations to lib/platform.
Added an lib/arch method to query the platform's native wide
character encoding and changed CUnicode to use it.  All
platform dependent code is now in lib/arch, lib/platform,
and the programs under cmd.  Also added more documentation.
This commit is contained in:
crs
2003-01-05 21:48:54 +00:00
parent f65921bc3f
commit e9cc0b434e
70 changed files with 672 additions and 156 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,133 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CMSWINDOWSPRIMARYSCREEN_H
#define CMSWINDOWSPRIMARYSCREEN_H
#include "CPrimaryScreen.h"
#include "IMSWindowsScreenEventHandler.h"
#include "CSynergyHook.h"
#include "MouseTypes.h"
#include "CString.h"
class CMSWindowsScreen;
class IScreenReceiver;
class IPrimaryScreenReceiver;
//! Microsoft windows primary screen implementation
class CMSWindowsPrimaryScreen :
public CPrimaryScreen, public IMSWindowsScreenEventHandler {
public:
typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM);
CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*);
virtual ~CMSWindowsPrimaryScreen();
// CPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 x, SInt32 y);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual KeyModifierMask getToggleMask() const;
virtual bool isLockedToScreen() const;
virtual IScreen* getScreen() const;
// IMSWindowsScreenEventHandler overrides
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND);
protected:
// CPrimaryScreen overrides
virtual void onPreMainLoop();
virtual void onPreOpen();
virtual void onPostOpen();
virtual void onPostClose();
virtual void onPreEnter();
virtual void onPostEnter();
virtual void onPreLeave();
virtual void onPostLeave(bool);
virtual void createWindow();
virtual void destroyWindow();
virtual bool showWindow();
virtual void hideWindow();
virtual void warpCursorToCenter();
virtual void updateKeys();
private:
void enterNoWarp();
// warp cursor without discarding queued events
void warpCursorNoFlush(SInt32 x, SInt32 y);
// discard posted messages
void nextMark();
// test if event should be ignored
bool ignore() const;
// key and button queries
KeyID mapKey(WPARAM keycode, LPARAM info,
KeyModifierMask* maskOut);
ButtonID mapButton(WPARAM button) const;
void updateKey(UINT vkCode, bool press);
private:
IPrimaryScreenReceiver* m_receiver;
CMSWindowsScreen* m_screen;
// true if windows 95/98/me
bool m_is95Family;
// the main loop's thread id
DWORD m_threadID;
// our window
HWND m_window;
// used to discard queued messages that are no longer needed
UInt32 m_mark;
UInt32 m_markReceived;
// map of key state
BYTE m_keys[256];
// last mouse position
SInt32 m_x, m_y;
// position of center pixel of screen
SInt32 m_xCenter, m_yCenter;
// hook library stuff
HINSTANCE m_hookLibrary;
InitFunc m_init;
CleanupFunc m_cleanup;
InstallFunc m_install;
UninstallFunc m_uninstall;
SetSidesFunc m_setSides;
SetZoneFunc m_setZone;
SetRelayFunc m_setRelay;
// stuff for restoring active window
HWND m_lastForegroundWindow;
HWND m_lastActiveWindow;
DWORD m_lastActiveThread;
};
#endif

View File

@@ -1,281 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CPrimaryScreen.h"
#include "IScreen.h"
#include "IScreenReceiver.h"
#include "ProtocolTypes.h"
#include "CLock.h"
#include "CThread.h"
#include "CLog.h"
//
// CPrimaryScreen
//
CPrimaryScreen::CPrimaryScreen(IScreenReceiver* receiver) :
m_receiver(receiver),
m_active(false)
{
// do nothing
}
CPrimaryScreen::~CPrimaryScreen()
{
// do nothing
}
void
CPrimaryScreen::mainLoop()
{
// change our priority
CThread::getCurrentThread().setPriority(-3);
// run event loop
try {
LOG((CLOG_DEBUG "entering event loop"));
onPreMainLoop();
getScreen()->mainLoop();
onPostMainLoop();
LOG((CLOG_DEBUG "exiting event loop"));
}
catch (...) {
onPostMainLoop();
LOG((CLOG_DEBUG "exiting event loop"));
throw;
}
}
void
CPrimaryScreen::exitMainLoop()
{
getScreen()->exitMainLoop();
}
void
CPrimaryScreen::open()
{
CClientInfo info;
try {
// subclass hook
onPreOpen();
// open the screen
getScreen()->open();
// create and prepare our window
createWindow();
// collect screen info
getScreen()->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
getScreen()->getCursorPos(info.m_mx, info.m_my);
info.m_zoneSize = getJumpZoneSize();
// update keyboard state
updateKeys();
// get notified of screen saver activation/deactivation
getScreen()->openScreensaver(true);
// subclass hook
onPostOpen();
// reset options
resetOptions();
}
catch (...) {
close();
throw;
}
// enter the screen
{
CLock lock(&m_mutex);
enterNoWarp();
}
// send screen info
m_receiver->onInfoChanged(info);
}
void
CPrimaryScreen::close()
{
onPreClose();
getScreen()->closeScreensaver();
destroyWindow();
getScreen()->close();
onPostClose();
}
void
CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver)
{
LOG((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : ""));
CLock lock(&m_mutex);
assert(m_active == true);
if (!forScreensaver) {
warpCursor(x, y);
}
else {
onEnterScreensaver();
}
enterNoWarp();
}
void
CPrimaryScreen::enterNoWarp()
{
// note -- must be locked on entry
// not active anymore
m_active = false;
// subclass hook
onPreEnter();
// restore active window and hide our window
hideWindow();
// subclass hook
onPostEnter();
}
bool
CPrimaryScreen::leave()
{
LOG((CLOG_INFO "leaving primary"));
CLock lock(&m_mutex);
assert(m_active == false);
// subclass hook
onPreLeave();
// show our window
if (!showWindow()) {
onPostLeave(false);
return false;
}
// get keyboard state as we leave
updateKeys();
// subclass hook
onPostLeave(true);
// warp mouse to center
warpCursorToCenter();
// local client now active
m_active = true;
// make sure our idea of clipboard ownership is correct
getScreen()->checkClipboards();
return true;
}
void
CPrimaryScreen::setClipboard(ClipboardID id,
const IClipboard* clipboard)
{
getScreen()->setClipboard(id, clipboard);
}
void
CPrimaryScreen::grabClipboard(ClipboardID id)
{
getScreen()->setClipboard(id, NULL);
}
bool
CPrimaryScreen::isActive() const
{
CLock lock(&m_mutex);
return m_active;
}
void
CPrimaryScreen::getClipboard(ClipboardID id,
IClipboard* clipboard) const
{
getScreen()->getClipboard(id, clipboard);
}
void
CPrimaryScreen::onPreMainLoop()
{
// do nothing
}
void
CPrimaryScreen::onPostMainLoop()
{
// do nothing
}
void
CPrimaryScreen::onPreOpen()
{
// do nothing
}
void
CPrimaryScreen::onPostOpen()
{
// do nothing
}
void
CPrimaryScreen::onPreClose()
{
// do nothing
}
void
CPrimaryScreen::onPostClose()
{
// do nothing
}
void
CPrimaryScreen::onPreEnter()
{
// do nothing
}
void
CPrimaryScreen::onPostEnter()
{
// do nothing
}
void
CPrimaryScreen::onEnterScreensaver()
{
// do nothing
}
void
CPrimaryScreen::onPreLeave()
{
// do nothing
}
void
CPrimaryScreen::onPostLeave(bool)
{
// do nothing
}

View File

@@ -1,340 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CPRIMARYSCREEN_H
#define CPRIMARYSCREEN_H
#include "ClipboardTypes.h"
#include "KeyTypes.h"
#include "OptionTypes.h"
#include "CMutex.h"
class IClipboard;
class IScreen;
class IScreenReceiver;
//! Generic server-side screen
/*!
This is a platform independent base class for primary screen
implementations. A primary screen is a server-side screen.
Each platform will derive a class from CPrimaryScreen to handle
platform dependent operations.
*/
class CPrimaryScreen {
public:
CPrimaryScreen(IScreenReceiver*);
virtual ~CPrimaryScreen();
//! @name manipulators
//@{
//! Open screen
/*!
Opens the screen. This includes initializing the screen, opening
the screen saver, synchronizing keyboard state, and causing events
to be reported to an IPrimaryScreenReceiver (set through another
interface). Calls close() before returning (rethrowing) if it
fails for any reason.
*/
void open();
//! Run event loop
/*!
Run the screen's event loop. This returns when it detects
the application should terminate or when exitMainLoop() is called.
mainLoop() may only be called between open() and close().
*/
void mainLoop();
//! Exit event loop
/*!
Force mainLoop() to return. This call can return before
mainLoop() does (i.e. asynchronously).
*/
void exitMainLoop();
//! Close screen
/*!
Closes the screen. This close the screen saver and the screen.
*/
void close();
//! Enter screen
/*!
Called when the user navigates to the primary screen. Warps
the cursor to the absolute coordinates \c x,y and unhides
it. If \c forScreensaver is true then we're entering because
the screen saver started and the cursor is not warped.
*/
void enter(SInt32 x, SInt32 y, bool forScreensaver);
//! Leave screen
/*!
Called when the user navigates off the primary screen. Returns
true iff successful.
*/
bool leave();
//! Update configuration
/*!
This is called when the configuration has changed. \c activeSides
is a bitmask of EDirectionMask indicating which sides of the
primary screen are linked to clients. Override to handle the
possible change in jump zones.
*/
virtual void reconfigure(UInt32 activeSides) = 0;
//! Warp cursor
/*!
Warp the cursor to the absolute coordinates \c x,y. Also
discard input events up to and including the warp before
returning.
*/
virtual void warpCursor(SInt32 x, SInt32 y) = 0;
//! Set clipboard
/*!
Sets the system's clipboard contents. This is usually called
soon after an enter().
*/
void setClipboard(ClipboardID, const IClipboard*);
//! Grab clipboard
/*!
Grabs (i.e. take ownership of) the system clipboard.
*/
void grabClipboard(ClipboardID);
//! Notify of options changes
/*!
Reset all options to their default values.
*/
virtual void resetOptions() = 0;
//! Notify of options changes
/*!
Set options to given values. Ignore unknown options and don't
modify our options that aren't given in \c options.
*/
virtual void setOptions(const COptionsList& options) = 0;
//@}
//! @name accessors
//@{
//! Test if active
/*!
Returns true iff the screen is active (i.e. the user has left
the screen). Note this is the reverse of a secdonary screen.
*/
bool isActive() const;
//! Get clipboard
/*!
Saves the contents of the system clipboard indicated by \c id.
*/
void getClipboard(ClipboardID, IClipboard*) const;
//! Get jump zone size
/*!
Return the jump zone size, the size of the regions on the edges of
the screen that cause the cursor to jump to another screen.
*/
virtual SInt32 getJumpZoneSize() const = 0;
//! Get toggle key state
/*!
Return the primary screen's current toggle modifier key state.
The returned mask should have the corresponding bit set for
each toggle key that is active. For example, if caps lock is
on then the returned mask should have \c KeyModifierCapsLock set.
*/
virtual KeyModifierMask getToggleMask() const = 0;
//! Get screen lock state
/*!
Return true if any key or button is being pressed or if there's
any other reason that the user should not be allowed to switch
screens. Active toggle keys (including the scroll lock key)
should not be counted as reasons to lock to the screen.
*/
virtual bool isLockedToScreen() const = 0;
//! Get screen
/*!
Return the platform dependent screen.
*/
virtual IScreen* getScreen() const = 0;
//@}
protected:
//! Pre-mainLoop() hook
/*!
Called on entry to mainLoop(). Override to perform platform specific
operations. Default does nothing. May throw.
*/
virtual void onPreMainLoop();
//! Post-mainLoop() hook
/*!
Called on exit from mainLoop(). Override to perform platform specific
operations. Default does nothing. May \b not throw.
*/
virtual void onPostMainLoop();
//! Pre-open() hook
/*!
Called on entry to open(). Override to perform platform specific
operations. Default does nothing. May throw.
*/
virtual void onPreOpen();
//! Post-open() hook
/*!
Called on exit from open() iff the open was successful. Default
does nothing. May throw.
*/
virtual void onPostOpen();
//! Pre-close() hook
/*!
Called on entry to close(). Override to perform platform specific
operations. Default does nothing. May \b not throw.
*/
virtual void onPreClose();
//! Post-close() hook
/*!
Called on exit from close(). Override to perform platform specific
operations. Default does nothing. May \b not throw.
*/
virtual void onPostClose();
//! Pre-enter() hook
/*!
Called from enter() after the cursor has been warped or, if
\c forScreensaver is true, onEnterScreensaver() was called. Override
to perform platform specific operations. Default does nothing. May
\b not throw.
*/
virtual void onPreEnter();
//! Post-enter() hook
/*!
Called on exit from enter(). Override to perform platform specific
operations. Default does nothing. May \b not throw.
*/
virtual void onPostEnter();
//! Pre-enter() for screen saver hook
/*!
Called on entry to enter() if the \c forScreensaver passed to it was
true. Override to perform platform specific operations. Default
does nothing. May \b not throw.
*/
virtual void onEnterScreensaver();
//! Pre-leave() hook
/*!
Called on entry to leave() after desktop synchronization. Override
to perform platform specific operations. Default does nothing. May
\b not throw.
*/
virtual void onPreLeave();
//! Post-leave() hook
/*!
Called on exit from leave(). \c success is the value returned by
showWindow(). Override to perform platform specific operations.
Default does nothing. May \b not throw.
*/
virtual void onPostLeave(bool success);
//! Create window
/*!
Called to create the window. This window is generally used to
receive events, hide the cursor, and to capture keyboard and mouse
input.
*/
virtual void createWindow() = 0;
//! Destroy window
/*!
Called to destroy the window created by createWindow().
*/
virtual void destroyWindow() = 0;
//! Show window
/*!
Called when the user navigates off the primary screen. Hide the
cursor and grab exclusive access to the input devices. Every call
to showWindow() has a matching call to hideWindow() which preceeds
it. Return true iff successful (in particular, iff the input
devices were grabbed).
After a successful showWindow(), user input events and
screensaver activation/deactivation should be reported to an
IPrimaryScreenReceiver (set through another interface) until
hideWindow() is called. Report mouse motion to its
onMouseMoveSecondary(). User input should not be delivered to
any application except this one.
*/
virtual bool showWindow() = 0;
//! Hide window
/*!
Called when the user navigates back to the primary screen. Show
the cursor and ungrab the input devices.
After hideWindow(), user input events should be delivered normally
to other applications. Mouse motion over (at least) the jump zones
must be reported to an IPrimaryScreenReceiver's onMouseMovePrimary().
*/
virtual void hideWindow() = 0;
//! Warp cursor for relative motion
/*!
Prepare the cursor to report relative motion. When the user has
navigated to another screen, synergy requires the cursor motion
deltas, not the absolute coordinates. Typically this is done by
warping the cursor to the center of the primary screen and then
every time it moves compute the motion and warp back to the
center (but without reporting that warp as motion). This is
only called after a successful showWindow().
*/
virtual void warpCursorToCenter() = 0;
//! Synchronize key state
/*!
Check the current keyboard state. Normally a screen will save
the keyboard state in this method and use this shadow state
when handling user input and in methods like isLockedToScreen().
*/
virtual void updateKeys() = 0;
private:
void enterNoWarp();
private:
CMutex m_mutex;
// object to notify of changes
IScreenReceiver* m_receiver;
// m_active is true if this screen has been left
bool m_active;
};
#endif

View File

@@ -1,805 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CXWindowsPrimaryScreen.h"
#include "CXWindowsScreen.h"
#include "CXWindowsUtil.h"
#include "IPrimaryScreenReceiver.h"
#include "XScreen.h"
#include "CThread.h"
#include "CLog.h"
#include "CStopwatch.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
#else
# include <X11/X.h>
# include <X11/Xutil.h>
# define XK_MISCELLANY
# define XK_XKB_KEYS
# include <X11/keysymdef.h>
#endif
#include "CArch.h"
//
// CXWindowsPrimaryScreen
//
CXWindowsPrimaryScreen::CXWindowsPrimaryScreen(
IScreenReceiver* receiver,
IPrimaryScreenReceiver* primaryReceiver) :
CPrimaryScreen(receiver),
m_receiver(primaryReceiver),
m_window(None)
{
m_screen = new CXWindowsScreen(receiver, this);
}
CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen()
{
assert(m_window == None);
delete m_screen;
}
void
CXWindowsPrimaryScreen::reconfigure(UInt32)
{
// do nothing
}
void
CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
{
CDisplayLock display(m_screen);
// warp mouse
warpCursorNoFlush(display, x, y);
// remove all input events before and including warp
XEvent event;
while (XCheckMaskEvent(display, PointerMotionMask |
ButtonPressMask | ButtonReleaseMask |
KeyPressMask | KeyReleaseMask |
KeymapStateMask,
&event)) {
// do nothing
}
// save position as last position
m_x = x;
m_y = y;
}
void
CXWindowsPrimaryScreen::resetOptions()
{
m_numLockHalfDuplex = false;
m_capsLockHalfDuplex = false;
}
void
CXWindowsPrimaryScreen::setOptions(const COptionsList& options)
{
for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
if (options[i] == kOptionHalfDuplexCapsLock) {
m_capsLockHalfDuplex = (options[i + 1] != 0);
LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off"));
}
else if (options[i] == kOptionHalfDuplexNumLock) {
m_numLockHalfDuplex = (options[i + 1] != 0);
LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off"));
}
}
}
KeyModifierMask
CXWindowsPrimaryScreen::getToggleMask() const
{
CDisplayLock display(m_screen);
// query the pointer to get the keyboard state
Window root, window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int state;
if (!XQueryPointer(display, m_window, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state)) {
return 0;
}
// convert to KeyModifierMask
KeyModifierMask mask = 0;
if (state & m_numLockMask) {
mask |= KeyModifierNumLock;
}
if (state & m_capsLockMask) {
mask |= KeyModifierCapsLock;
}
if (state & m_scrollLockMask) {
mask |= KeyModifierScrollLock;
}
return mask;
}
bool
CXWindowsPrimaryScreen::isLockedToScreen() const
{
CDisplayLock display(m_screen);
// query the pointer to get the button state
Window root, window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int state;
if (XQueryPointer(display, m_window, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state)) {
if ((state & (Button1Mask | Button2Mask | Button3Mask |
Button4Mask | Button5Mask)) != 0) {
return true;
}
}
// get logical keyboard state
char keyMap[32];
memset(keyMap, 0, sizeof(keyMap));
XQueryKeymap(display, keyMap);
// locked if any key is down
for (unsigned int i = 0; i < sizeof(keyMap); ++i) {
if (keyMap[i] != 0) {
// if any key is half-duplex then it'll be down when
// toggled on but shouldn't count as a reason to lock
// to the screen.
if (m_numLockHalfDuplex || m_capsLockHalfDuplex) {
for (unsigned int j = 0; j < 8; ++j) {
if ((keyMap[i] & (1 << j)) != 0) {
const KeyCode keycode = 8 * i + j;
const KeySym keysym = XKeycodeToKeysym(display,
keycode, 0);
if (m_numLockHalfDuplex && keysym == XK_Num_Lock) {
continue;
}
if (m_capsLockHalfDuplex && keysym == XK_Caps_Lock) {
continue;
}
// non-half-duplex key down
return true;
}
}
// only half-duplex keys down
continue;
}
return true;
}
}
// not locked
return false;
}
IScreen*
CXWindowsPrimaryScreen::getScreen() const
{
return m_screen;
}
void
CXWindowsPrimaryScreen::onScreensaver(bool activated)
{
m_receiver->onScreensaver(activated);
}
bool
CXWindowsPrimaryScreen::onPreDispatch(const CEvent*)
{
return false;
}
bool
CXWindowsPrimaryScreen::onEvent(CEvent* event)
{
assert(event != NULL);
XEvent& xevent = event->m_event;
// handle event
switch (xevent.type) {
case CreateNotify:
{
// select events on new window
CDisplayLock display(m_screen);
selectEvents(display, xevent.xcreatewindow.window);
}
return true;
case MappingNotify:
// keyboard mapping changed
updateKeys();
return true;
case KeyPress:
{
LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(&xevent.xkey);
if (key != kKeyNone) {
m_receiver->onKeyDown(key, mask);
if (key == kKeyCapsLock && m_capsLockHalfDuplex) {
m_receiver->onKeyUp(key, mask | KeyModifierCapsLock);
}
else if (key == kKeyNumLock && m_numLockHalfDuplex) {
m_receiver->onKeyUp(key, mask | KeyModifierNumLock);
}
}
}
return true;
case KeyRelease:
{
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(&xevent.xkey);
if (key != kKeyNone) {
// check if this is a key repeat by getting the next
// KeyPress event that has the same key and time as
// this release event, if any. first prepare the
// filter info.
CKeyEventInfo filter;
filter.m_event = KeyPress;
filter.m_window = xevent.xkey.window;
filter.m_time = xevent.xkey.time;
filter.m_keycode = xevent.xkey.keycode;
// now check for event
bool hasPress;
{
XEvent xevent2;
CDisplayLock display(m_screen);
hasPress = (XCheckIfEvent(display, &xevent2,
&CXWindowsPrimaryScreen::findKeyEvent,
(XPointer)&filter) == True);
}
if (!hasPress) {
// no press event follows so it's a plain release
LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
if (key == kKeyCapsLock && m_capsLockHalfDuplex) {
m_receiver->onKeyDown(key, mask);
}
else if (key == kKeyNumLock && m_numLockHalfDuplex) {
m_receiver->onKeyDown(key, mask);
}
m_receiver->onKeyUp(key, mask);
}
else {
// found a press event following so it's a repeat.
// we could attempt to count the already queued
// repeats but we'll just send a repeat of 1.
// note that we discard the press event.
LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
m_receiver->onKeyRepeat(key, mask, 1);
}
}
}
return true;
case ButtonPress:
{
LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button));
const ButtonID button = mapButton(xevent.xbutton.button);
if (button != kButtonNone) {
m_receiver->onMouseDown(button);
}
}
return true;
case ButtonRelease:
{
LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button));
const ButtonID button = mapButton(xevent.xbutton.button);
if (button != kButtonNone) {
m_receiver->onMouseUp(button);
}
else if (xevent.xbutton.button == 4) {
// wheel forward (away from user)
m_receiver->onMouseWheel(120);
}
else if (xevent.xbutton.button == 5) {
// wheel backward (toward user)
m_receiver->onMouseWheel(-120);
}
}
return true;
case MotionNotify:
{
LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root));
// compute motion delta (relative to the last known
// mouse position)
SInt32 x = xevent.xmotion.x_root - m_x;
SInt32 y = xevent.xmotion.y_root - m_y;
// save position to compute delta of next motion
m_x = xevent.xmotion.x_root;
m_y = xevent.xmotion.y_root;
if (xevent.xmotion.send_event) {
// we warped the mouse. discard events until we
// find the matching sent event. see
// warpCursorNoFlush() for where the events are
// sent. we discard the matching sent event and
// can be sure we've skipped the warp event.
CDisplayLock display(m_screen);
do {
XMaskEvent(display, PointerMotionMask, &xevent);
} while (!xevent.xmotion.send_event);
}
else if (!isActive()) {
// motion on primary screen
m_receiver->onMouseMovePrimary(m_x, m_y);
}
else {
// motion on secondary screen. warp mouse back to
// center.
//
// my lombard (powerbook g3) running linux and
// using the adbmouse driver has two problems:
// first, the driver only sends motions of +/-2
// pixels and, second, it seems to discard some
// physical input after a warp. the former isn't a
// big deal (we're just limited to every other
// pixel) but the latter is a PITA. to work around
// it we only warp when the mouse has moved more
// than s_size pixels from the center.
static const SInt32 s_size = 32;
if (xevent.xmotion.x_root - m_xCenter < -s_size ||
xevent.xmotion.x_root - m_xCenter > s_size ||
xevent.xmotion.y_root - m_yCenter < -s_size ||
xevent.xmotion.y_root - m_yCenter > s_size) {
CDisplayLock display(m_screen);
warpCursorNoFlush(display, m_xCenter, m_yCenter);
}
// send event if mouse moved. do this after warping
// back to center in case the motion takes us onto
// the primary screen. if we sent the event first
// in that case then the warp would happen after
// warping to the primary screen's enter position,
// effectively overriding it.
if (x != 0 || y != 0) {
m_receiver->onMouseMoveSecondary(x, y);
}
}
}
return true;
}
return false;
}
SInt32
CXWindowsPrimaryScreen::getJumpZoneSize() const
{
return 1;
}
void
CXWindowsPrimaryScreen::onPreMainLoop()
{
assert(m_window != None);
}
void
CXWindowsPrimaryScreen::onPreOpen()
{
assert(m_window == None);
}
void
CXWindowsPrimaryScreen::onPostOpen()
{
assert(m_window != None);
// get cursor info
m_screen->getCursorPos(m_x, m_y);
m_screen->getCursorCenter(m_xCenter, m_yCenter);
}
void
CXWindowsPrimaryScreen::onPreEnter()
{
assert(m_window != None);
}
void
CXWindowsPrimaryScreen::onPreLeave()
{
assert(m_window != None);
}
void
CXWindowsPrimaryScreen::onEnterScreenSaver()
{
CDisplayLock display(m_screen);
// set keyboard focus to root window. the screensaver should then
// pick up key events for when the user enters a password to unlock.
XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime);
}
void
CXWindowsPrimaryScreen::createWindow()
{
assert(m_window == None);
// get size of screen
SInt32 x, y, w, h;
m_screen->getShape(x, y, w, h);
// grab window attributes. this window is used to capture user
// input when the user is focused on another client. don't let
// the window manager mess with it.
XSetWindowAttributes attr;
attr.event_mask = PointerMotionMask |
ButtonPressMask | ButtonReleaseMask |
KeyPressMask | KeyReleaseMask |
KeymapStateMask | PropertyChangeMask;
attr.do_not_propagate_mask = 0;
attr.override_redirect = True;
attr.cursor = m_screen->getBlankCursor();
{
// create the grab window
CDisplayLock display(m_screen);
m_window = XCreateWindow(display, m_screen->getRoot(),
x, y, w, h, 0, 0,
InputOnly, CopyFromParent,
CWDontPropagate | CWEventMask |
CWOverrideRedirect | CWCursor,
&attr);
if (m_window == None) {
throw XScreenOpenFailure();
}
LOG((CLOG_DEBUG "window is 0x%08x", m_window));
// start watching for events on other windows
selectEvents(display, m_screen->getRoot());
}
// tell generic screen about the window
m_screen->setWindow(m_window);
}
void
CXWindowsPrimaryScreen::destroyWindow()
{
// display can be NULL if the server unexpectedly disconnected
if (m_window != None) {
m_screen->setWindow(None);
CDisplayLock display(m_screen);
if (display != NULL) {
XDestroyWindow(display, m_window);
}
m_window = None;
}
}
bool
CXWindowsPrimaryScreen::showWindow()
{
assert(m_window != None);
CDisplayLock display(m_screen);
// raise and show the input window
XMapRaised(display, m_window);
// grab the mouse and keyboard. keep trying until we get them.
// if we can't grab one after grabbing the other then ungrab
// and wait before retrying. give up after s_timeout seconds.
static const double s_timeout = 1.0;
int result;
CStopwatch timer;
do {
// keyboard first
do {
result = XGrabKeyboard(display, m_window, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
assert(result != GrabNotViewable);
if (result != GrabSuccess) {
LOG((CLOG_DEBUG2 "waiting to grab keyboard"));
ARCH->sleep(0.05);
if (timer.getTime() >= s_timeout) {
LOG((CLOG_DEBUG2 "grab keyboard timed out"));
XUnmapWindow(display, m_window);
return false;
}
}
} while (result != GrabSuccess);
LOG((CLOG_DEBUG2 "grabbed keyboard"));
// now the mouse
result = XGrabPointer(display, m_window, True, 0,
GrabModeAsync, GrabModeAsync,
m_window, None, CurrentTime);
assert(result != GrabNotViewable);
if (result != GrabSuccess) {
// back off to avoid grab deadlock
XUngrabKeyboard(display, CurrentTime);
LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer"));
ARCH->sleep(0.05);
if (timer.getTime() >= s_timeout) {
LOG((CLOG_DEBUG2 "grab pointer timed out"));
XUnmapWindow(display, m_window);
return false;
}
}
} while (result != GrabSuccess);
LOG((CLOG_DEBUG1 "grabbed pointer and keyboard"));
return true;
}
void
CXWindowsPrimaryScreen::hideWindow()
{
CDisplayLock display(m_screen);
// unmap the grab window. this also ungrabs the mouse and keyboard.
XUnmapWindow(display, m_window);
}
void
CXWindowsPrimaryScreen::warpCursorToCenter()
{
warpCursor(m_xCenter, m_yCenter);
}
void
CXWindowsPrimaryScreen::warpCursorNoFlush(
Display* display, SInt32 x, SInt32 y)
{
assert(display != NULL);
assert(m_window != None);
// send an event that we can recognize before the mouse warp
XEvent eventBefore;
eventBefore.type = MotionNotify;
eventBefore.xmotion.display = display;
eventBefore.xmotion.window = m_window;
eventBefore.xmotion.root = m_screen->getRoot();
eventBefore.xmotion.subwindow = m_window;
eventBefore.xmotion.time = CurrentTime;
eventBefore.xmotion.x = x;
eventBefore.xmotion.y = y;
eventBefore.xmotion.x_root = x;
eventBefore.xmotion.y_root = y;
eventBefore.xmotion.state = 0;
eventBefore.xmotion.is_hint = False;
eventBefore.xmotion.same_screen = True;
XEvent eventAfter = eventBefore;
XSendEvent(display, m_window, False, 0, &eventBefore);
// warp mouse
XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y);
// send an event that we can recognize after the mouse warp
XSendEvent(display, m_window, False, 0, &eventAfter);
XSync(display, False);
LOG((CLOG_DEBUG2 "warped to %d,%d", x, y));
}
void
CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const
{
// ignore errors while we adjust event masks. windows could be
// destroyed at any time after the XQueryTree() in doSelectEvents()
// so we must ignore BadWindow errors.
CXWindowsUtil::CErrorLock lock(display);
// adjust event masks
doSelectEvents(display, w);
}
void
CXWindowsPrimaryScreen::doSelectEvents(Display* display, Window w) const
{
// we want to track the mouse everywhere on the display. to achieve
// that we select PointerMotionMask on every window. we also select
// SubstructureNotifyMask in order to get CreateNotify events so we
// select events on new windows too.
//
// note that this can break certain clients due a design flaw of X.
// X will deliver a PointerMotion event to the deepest window in the
// hierarchy that contains the pointer and has PointerMotionMask
// selected by *any* client. if another client doesn't select
// motion events in a subwindow so the parent window will get them
// then by selecting for motion events on the subwindow we break
// that client because the parent will no longer get the events.
// FIXME -- should provide some workaround for event selection
// design flaw. perhaps only select for motion events on windows
// that already do or are top-level windows or don't propagate
// pointer events. or maybe an option to simply poll the mouse.
// we don't want to adjust our grab window
if (w == m_window) {
return;
}
// select events of interest. do this before querying the tree so
// we'll get notifications of children created after the XQueryTree()
// so we won't miss them.
XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask);
// recurse on child windows
Window rw, pw, *cw;
unsigned int nc;
if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) {
for (unsigned int i = 0; i < nc; ++i) {
doSelectEvents(display, cw[i]);
}
XFree(cw);
}
}
KeyModifierMask
CXWindowsPrimaryScreen::mapModifier(unsigned int state) const
{
KeyModifierMask mask = 0;
if (state & ShiftMask)
mask |= KeyModifierShift;
if (state & LockMask)
mask |= KeyModifierCapsLock;
if (state & ControlMask)
mask |= KeyModifierControl;
if (state & m_altMask)
mask |= KeyModifierAlt;
if (state & m_metaMask)
mask |= KeyModifierMeta;
if (state & m_superMask)
mask |= KeyModifierSuper;
if (state & m_modeSwitchMask)
mask |= KeyModifierModeSwitch;
if (state & m_numLockMask)
mask |= KeyModifierNumLock;
if (state & m_scrollLockMask)
mask |= KeyModifierScrollLock;
return mask;
}
KeyID
CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const
{
CDisplayLock display(m_screen);
// convert to a keysym
// FIXME -- we're not properly handling unicode
KeySym keysym;
char dummy[1];
XLookupString(event, dummy, 0, &keysym, NULL);
// convert key
switch (keysym & 0xffffff00) {
case 0x0000:
// Latin-1
return static_cast<KeyID>(keysym);
case 0xfe00:
// ISO 9995 Function and Modifier Keys
if (keysym == XK_ISO_Left_Tab) {
return kKeyLeftTab;
}
return kKeyNone;
case 0xff00:
// MISCELLANY
return static_cast<KeyID>(keysym - 0xff00 + 0xef00);
default:
// FIXME -- support unicode characters
return kKeyNone;
}
}
ButtonID
CXWindowsPrimaryScreen::mapButton(unsigned int button) const
{
// FIXME -- should use button mapping?
if (button >= 1 && button <= 3) {
return static_cast<ButtonID>(button);
}
else {
return kButtonNone;
}
}
void
CXWindowsPrimaryScreen::updateKeys()
{
CDisplayLock display(m_screen);
// get modifier map from server
XModifierKeymap* keymap = XGetModifierMapping(display);
// initialize
m_altMask = 0;
m_metaMask = 0;
m_superMask = 0;
m_modeSwitchMask = 0;
m_numLockMask = 0;
m_capsLockMask = 0;
m_scrollLockMask = 0;
// work around for my system, which reports this state bit when
// mode switch is down, instead of the appropriate modifier bit.
// should have no effect on other systems. -crs 9/02.
m_modeSwitchMask |= (1 << 13);
// set keycodes and masks
for (unsigned int i = 0; i < 8; ++i) {
const unsigned int bit = (1 << i);
for (int j = 0; j < keymap->max_keypermod; ++j) {
KeyCode keycode = keymap->modifiermap[i *
keymap->max_keypermod + j];
// note mask for particular modifiers
const KeySym keysym = XKeycodeToKeysym(display, keycode, 0);
switch (keysym) {
case XK_Alt_L:
case XK_Alt_R:
m_altMask |= bit;
break;
case XK_Meta_L:
case XK_Meta_R:
m_metaMask |= bit;
break;
case XK_Super_L:
case XK_Super_R:
m_superMask |= bit;
break;
case XK_Mode_switch:
m_modeSwitchMask |= bit;
break;
case XK_Num_Lock:
m_numLockMask |= bit;
break;
case XK_Caps_Lock:
m_capsLockMask |= bit;
break;
case XK_Scroll_Lock:
m_scrollLockMask |= bit;
break;
}
}
}
XFreeModifiermap(keymap);
}
Bool
CXWindowsPrimaryScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
{
CKeyEventInfo* filter = reinterpret_cast<CKeyEventInfo*>(arg);
return (xevent->type == filter->m_event &&
xevent->xkey.window == filter->m_window &&
xevent->xkey.time == filter->m_time &&
xevent->xkey.keycode == filter->m_keycode) ? True : False;
}

View File

@@ -1,118 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CXWINDOWSPRIMARYSCREEN_H
#define CXWINDOWSPRIMARYSCREEN_H
#include "CPrimaryScreen.h"
#include "IScreenEventHandler.h"
#include "MouseTypes.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
class CXWindowsScreen;
class IScreenReceiver;
class IPrimaryScreenReceiver;
//! X11 primary screen implementation
class CXWindowsPrimaryScreen :
public CPrimaryScreen, public IScreenEventHandler {
public:
CXWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*);
virtual ~CXWindowsPrimaryScreen();
// CPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 x, SInt32 y);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual KeyModifierMask getToggleMask() const;
virtual bool isLockedToScreen() const;
virtual IScreen* getScreen() const;
// IScreenEventHandler overrides
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual SInt32 getJumpZoneSize() const;
protected:
// CPrimaryScreen overrides
virtual void onPreMainLoop();
virtual void onPreOpen();
virtual void onPostOpen();
virtual void onPreEnter();
virtual void onPreLeave();
virtual void onEnterScreenSaver();
virtual void createWindow();
virtual void destroyWindow();
virtual bool showWindow();
virtual void hideWindow();
virtual void warpCursorToCenter();
virtual void updateKeys();
private:
void warpCursorNoFlush(Display*,
SInt32 xAbsolute, SInt32 yAbsolute);
void selectEvents(Display*, Window) const;
void doSelectEvents(Display*, Window) const;
KeyModifierMask mapModifier(unsigned int state) const;
KeyID mapKey(XKeyEvent*) const;
ButtonID mapButton(unsigned int button) const;
class CKeyEventInfo {
public:
int m_event;
Window m_window;
Time m_time;
KeyCode m_keycode;
};
static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg);
private:
CXWindowsScreen* m_screen;
IPrimaryScreenReceiver* m_receiver;
// our window
Window m_window;
// note toggle keys that toggle on up/down (false) or on
// transition (true)
bool m_numLockHalfDuplex;
bool m_capsLockHalfDuplex;
// modifier masks
unsigned int m_altMask;
unsigned int m_metaMask;
unsigned int m_superMask;
unsigned int m_modeSwitchMask;
unsigned int m_numLockMask;
unsigned int m_capsLockMask;
unsigned int m_scrollLockMask;
// last mouse position
SInt32 m_x, m_y;
// position of center pixel of screen
SInt32 m_xCenter, m_yCenter;
};
#endif

View File

@@ -1,39 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef IPRIMARYSCREENFACTORY_H
#define IPRIMARYSCREENFACTORY_H
#include "IInterface.h"
class CPrimaryScreen;
class IPrimaryScreenReceiver;
class IScreenReceiver;
//! Primary screen factory interface
/*!
This interface provides factory methods to create primary screens.
*/
class IPrimaryScreenFactory : public IInterface {
public:
//! Create screen
/*!
Create and return a primary screen. The caller must delete the
returned object.
*/
virtual CPrimaryScreen*
create(IScreenReceiver*, IPrimaryScreenReceiver*) = 0;
};
#endif

View File

@@ -17,8 +17,6 @@ VDEPTH = ./$(VPATH)/$(DEPTH)
EXTRA_DIST = \
server.dsp \
CMSWindowsPrimaryScreen.cpp \
CMSWindowsPrimaryScreen.h \
$(NULL)
MAINTAINERCLEANFILES = \
@@ -32,18 +30,13 @@ libserver_a_SOURCES = \
CConfig.cpp \
CHTTPServer.cpp \
CPrimaryClient.cpp \
CPrimaryScreen.cpp \
CServer.cpp \
CXWindowsPrimaryScreen.cpp \
CClientProxy.h \
CClientProxy1_0.h \
CConfig.h \
CHTTPServer.h \
CPrimaryClient.h \
CPrimaryScreen.h \
CServer.h \
CXWindowsPrimaryScreen.h \
IPrimaryScreenFactory.h \
$(NULL)
INCLUDES = \
-I$(VDEPTH)/lib/common \

View File

@@ -103,18 +103,10 @@ SOURCE=.\CHTTPServer.cpp
# End Source File
# Begin Source File
SOURCE=.\CMSWindowsPrimaryScreen.cpp
# End Source File
# Begin Source File
SOURCE=.\CPrimaryClient.cpp
# End Source File
# Begin Source File
SOURCE=.\CPrimaryScreen.cpp
# End Source File
# Begin Source File
SOURCE=.\CServer.cpp
# End Source File
# End Group
@@ -139,24 +131,12 @@ SOURCE=.\CHTTPServer.h
# End Source File
# Begin Source File
SOURCE=.\CMSWindowsPrimaryScreen.h
# End Source File
# Begin Source File
SOURCE=.\CPrimaryClient.h
# End Source File
# Begin Source File
SOURCE=.\CPrimaryScreen.h
# End Source File
# Begin Source File
SOURCE=.\CServer.h
# End Source File
# Begin Source File
SOURCE=.\IPrimaryScreenFactory.h
# End Source File
# End Group
# Begin Group "Resource Files"