win32 changes. changed names of binaries. added support for

running as (and installing/installing) a service.  added
support for multiple desktops (NT only, 95 doesn't support
multiple desktops).
This commit is contained in:
crs 2002-06-08 21:48:00 +00:00
parent 5709d8ddef
commit 4b28ffc5b2
36 changed files with 2948 additions and 855 deletions

View File

@ -23,8 +23,8 @@ CFG=all - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
MTL=midl.exe MTL=midl.exe
!IF "$(CFG)" == "all - Win32 Release" !IF "$(CFG)" == "all - Win32 Release"

View File

@ -201,10 +201,8 @@ void CLog::output(int priority, char* msg)
// print it // print it
CHoldLock lock(s_lock); CHoldLock lock(s_lock);
if (s_outputter) { if (s_outputter == NULL ||
s_outputter(priority, msg + g_maxPriorityLength - n); !s_outputter(priority, msg + g_maxPriorityLength - n)) {
}
else {
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
openConsole(); openConsole();
#endif #endif

View File

@ -17,7 +17,11 @@ public:
kDEBUG2 kDEBUG2
}; };
typedef void (*Outputter)(int priority, const char*); // type of outputter function. return false if CLog should use
// the default outputter, true otherwise.
typedef bool (*Outputter)(int priority, const char*);
// type of lock/unlock function
typedef void (*Lock)(bool lock); typedef void (*Lock)(bool lock);
// //

View File

@ -23,8 +23,8 @@ CFG=base - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
RSC=rc.exe RSC=rc.exe

View File

@ -27,6 +27,9 @@
#pragma warning(disable: 4786) // identifier truncated in debug info #pragma warning(disable: 4786) // identifier truncated in debug info
#pragma warning(disable: 4514) // unreferenced inline function removed #pragma warning(disable: 4514) // unreferenced inline function removed
// this one's a little too aggressive
#pragma warning(disable: 4127) // conditional expression is constant
#endif // (_MSC_VER >= 1200) #endif // (_MSC_VER >= 1200)
#else #else

View File

@ -8,4 +8,5 @@
#pragma warning(disable: 4284) #pragma warning(disable: 4284)
#pragma warning(disable: 4146) // unary minus on unsigned value #pragma warning(disable: 4146) // unary minus on unsigned value
#pragma warning(disable: 4127) // conditional expression is constant #pragma warning(disable: 4127) // conditional expression is constant
#pragma warning(disable: 4701) // variable possibly used uninitialized
#endif #endif

View File

@ -120,6 +120,11 @@ void CClient::run(const CNetworkAddress& serverAddress)
} }
} }
void CClient::quit()
{
m_screen->stop();
}
void CClient::onClipboardChanged(ClipboardID id) void CClient::onClipboardChanged(ClipboardID id)
{ {
log((CLOG_DEBUG "sending clipboard %d changed", id)); log((CLOG_DEBUG "sending clipboard %d changed", id));

View File

@ -19,8 +19,12 @@ public:
// manipulators // manipulators
// start the client. does not return until quit() is called.
void run(const CNetworkAddress& serverAddress); void run(const CNetworkAddress& serverAddress);
// tell client to exit gracefully
void quit();
// handle events on client's screen // handle events on client's screen
void onClipboardChanged(ClipboardID); void onClipboardChanged(ClipboardID);
void onResolutionChanged(); void onResolutionChanged();

View File

@ -1,9 +1,12 @@
#include "CMSWindowsSecondaryScreen.h" #include "CMSWindowsSecondaryScreen.h"
#include "CMSWindowsClipboard.h" #include "CMSWindowsClipboard.h"
#include "CClient.h" #include "CClient.h"
#include "CPlatform.h"
#include "CClipboard.h" #include "CClipboard.h"
#include "CThread.h" #include "CLock.h"
#include "CLog.h" #include "CLog.h"
#include "CThread.h"
#include "XScreen.h"
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
@ -13,10 +16,18 @@
CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() :
m_client(NULL), m_client(NULL),
m_threadID(0),
m_desk(NULL),
m_deskName(),
m_window(NULL), m_window(NULL),
m_active(false),
m_nextClipboardWindow(NULL) m_nextClipboardWindow(NULL)
{ {
// do nothing m_is95Family = CPlatform::isWindows95Family();
// make sure this thread has a message queue
MSG dummy;
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
} }
CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
@ -26,21 +37,32 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
void CMSWindowsSecondaryScreen::run() void CMSWindowsSecondaryScreen::run()
{ {
// must call run() from same thread as open()
assert(m_threadID == GetCurrentThreadId());
// change our priority // change our priority
CThread::getCurrentThread().setPriority(-7); CThread::getCurrentThread().setPriority(-7);
// save thread id // poll input desktop to see if it changes (onPreTranslate()
m_threadID = GetCurrentThreadId(); // handles WM_TIMER)
UINT timer = 0;
if (!m_is95Family) {
SetTimer(NULL, 0, 200, NULL);
}
// run event loop // run event loop
log((CLOG_INFO "entering event loop")); log((CLOG_INFO "entering event loop"));
doRun(); doRun();
log((CLOG_INFO "exiting event loop")); log((CLOG_INFO "exiting event loop"));
// remove timer
if (!m_is95Family) {
KillTimer(NULL, timer);
}
} }
void CMSWindowsSecondaryScreen::stop() void CMSWindowsSecondaryScreen::stop()
{ {
log((CLOG_INFO "requesting event loop stop"));
doStop(); doStop();
} }
@ -49,8 +71,6 @@ void CMSWindowsSecondaryScreen::open(CClient* client)
assert(m_client == NULL); assert(m_client == NULL);
assert(client != NULL); assert(client != NULL);
log((CLOG_INFO "opening screen"));
// set the client // set the client
m_client = client; m_client = client;
@ -64,14 +84,16 @@ void CMSWindowsSecondaryScreen::open(CClient* client)
// assume primary has all clipboards // assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) for (ClipboardID id = 0; id < kClipboardEnd; ++id)
grabClipboard(id); grabClipboard(id);
// hide the cursor
m_active = true;
leave();
} }
void CMSWindowsSecondaryScreen::close() void CMSWindowsSecondaryScreen::close()
{ {
assert(m_client != NULL); assert(m_client != NULL);
log((CLOG_INFO "closing screen"));
// close the display // close the display
closeDisplay(); closeDisplay();
@ -82,12 +104,16 @@ void CMSWindowsSecondaryScreen::close()
void CMSWindowsSecondaryScreen::enter( void CMSWindowsSecondaryScreen::enter(
SInt32 x, SInt32 y, KeyModifierMask mask) SInt32 x, SInt32 y, KeyModifierMask mask)
{ {
CLock lock(&m_mutex);
assert(m_window != NULL); assert(m_window != NULL);
assert(m_active == false);
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
// attach thread input queues syncDesktop();
AttachThreadInput(GetCurrentThreadId(), m_threadID, TRUE);
// now active
m_active = true;
// update our keyboard state to reflect the local state // update our keyboard state to reflect the local state
updateKeys(); updateKeys();
@ -104,34 +130,25 @@ void CMSWindowsSecondaryScreen::enter(
toggleKey(VK_SCROLL, KeyModifierScrollLock); toggleKey(VK_SCROLL, KeyModifierScrollLock);
} }
// warp to requested location // hide mouse
SInt32 w, h; onEnter(x, y);
getScreenSize(&w, &h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(DWORD)((65535.99 * x) / (w - 1)),
(DWORD)((65535.99 * y) / (h - 1)),
0, 0);
// show cursor
log((CLOG_INFO "show cursor"));
ShowWindow(m_window, SW_HIDE);
} }
void CMSWindowsSecondaryScreen::leave() void CMSWindowsSecondaryScreen::leave()
{ {
CLock lock(&m_mutex);
assert(m_window != NULL); assert(m_window != NULL);
assert(m_active == true);
log((CLOG_INFO "leaving screen")); log((CLOG_INFO "leaving screen"));
// move hider window under the mouse (rather than moving the mouse syncDesktop();
// somewhere else on the screen)
POINT point;
GetCursorPos(&point);
MoveWindow(m_window, point.x, point.y, 1, 1, FALSE);
// raise and show the hider window. take activation. // hide mouse
log((CLOG_INFO "hide cursor")); onLeave();
ShowWindow(m_window, SW_SHOWNORMAL);
// not active anymore
m_active = false;
// if we think we own the clipboard but we don't then somebody // if we think we own the clipboard but we don't then somebody
// grabbed the clipboard on this screen without us knowing. // grabbed the clipboard on this screen without us knowing.
@ -160,6 +177,10 @@ void CMSWindowsSecondaryScreen::keyDown(
Keystrokes keys; Keystrokes keys;
UINT virtualKey; UINT virtualKey;
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
// get the sequence of keys to simulate key press and the final // get the sequence of keys to simulate key press and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kPress); m_mask = mapKey(keys, virtualKey, key, mask, kPress);
@ -195,6 +216,10 @@ void CMSWindowsSecondaryScreen::keyRepeat(
Keystrokes keys; Keystrokes keys;
UINT virtualKey; UINT virtualKey;
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
// get the sequence of keys to simulate key repeat and the final // get the sequence of keys to simulate key repeat and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); m_mask = mapKey(keys, virtualKey, key, mask, kRepeat);
@ -211,6 +236,10 @@ void CMSWindowsSecondaryScreen::keyUp(
Keystrokes keys; Keystrokes keys;
UINT virtualKey; UINT virtualKey;
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
// get the sequence of keys to simulate key release and the final // get the sequence of keys to simulate key release and the final
// modifier state. // modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRelease); m_mask = mapKey(keys, virtualKey, key, mask, kRelease);
@ -263,6 +292,10 @@ void CMSWindowsSecondaryScreen::keyUp(
void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) void CMSWindowsSecondaryScreen::mouseDown(ButtonID button)
{ {
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
// map button id to button flag // map button id to button flag
DWORD flags = mapButton(button, true); DWORD flags = mapButton(button, true);
@ -273,6 +306,10 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button)
void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) void CMSWindowsSecondaryScreen::mouseUp(ButtonID button)
{ {
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
// map button id to button flag // map button id to button flag
DWORD flags = mapButton(button, false); DWORD flags = mapButton(button, false);
@ -281,8 +318,13 @@ void CMSWindowsSecondaryScreen::mouseUp(ButtonID button)
mouse_event(flags, 0, 0, 0, 0); mouse_event(flags, 0, 0, 0, 0);
} }
void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) void CMSWindowsSecondaryScreen::mouseMove(
SInt32 x, SInt32 y)
{ {
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
SInt32 w, h; SInt32 w, h;
getScreenSize(&w, &h); getScreenSize(&w, &h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
@ -293,20 +335,27 @@ void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta)
{ {
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0);
} }
void CMSWindowsSecondaryScreen::setClipboard( void CMSWindowsSecondaryScreen::setClipboard(
ClipboardID id, const IClipboard* src) ClipboardID /*id*/, const IClipboard* src)
{ {
CLock lock(&m_mutex);
assert(m_window != NULL); assert(m_window != NULL);
CMSWindowsClipboard dst(m_window); CMSWindowsClipboard dst(m_window);
CClipboard::copy(&dst, src); CClipboard::copy(&dst, src);
} }
void CMSWindowsSecondaryScreen::grabClipboard(ClipboardID id) void CMSWindowsSecondaryScreen::grabClipboard(
ClipboardID /*id*/)
{ {
CLock lock(&m_mutex);
assert(m_window != NULL); assert(m_window != NULL);
CMSWindowsClipboard clipboard(m_window); CMSWindowsClipboard clipboard(m_window);
@ -321,6 +370,10 @@ void CMSWindowsSecondaryScreen::getMousePos(
assert(x != NULL); assert(x != NULL);
assert(y != NULL); assert(y != NULL);
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
POINT pos; POINT pos;
if (GetCursorPos(&pos)) { if (GetCursorPos(&pos)) {
*x = pos.x; *x = pos.x;
@ -344,8 +397,9 @@ SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const
} }
void CMSWindowsSecondaryScreen::getClipboard( void CMSWindowsSecondaryScreen::getClipboard(
ClipboardID id, IClipboard* dst) const ClipboardID /*id*/, IClipboard* dst) const
{ {
CLock lock(&m_mutex);
assert(m_window != NULL); assert(m_window != NULL);
CMSWindowsClipboard src(m_window); CMSWindowsClipboard src(m_window);
@ -356,44 +410,59 @@ void CMSWindowsSecondaryScreen::onOpenDisplay()
{ {
assert(m_window == NULL); assert(m_window == NULL);
// initialize clipboard owner to current owner. we don't want // save thread id. we'll need to pass this to the hook library.
// to take ownership of the clipboard just by starting up. m_threadID = GetCurrentThreadId();
m_clipboardOwner = GetClipboardOwner();
// create the cursor hiding window. this window is used to hide the // get the input desktop and switch to it
// cursor when it's not on the screen. the window is hidden as soon if (m_is95Family) {
// as the cursor enters the screen or the display's real cursor is if (!openDesktop()) {
// moved. throw XScreenOpenFailure();
m_window = CreateWindowEx(WS_EX_TOPMOST | }
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, }
(LPCTSTR)getClass(), "Synergy", else {
WS_POPUP, if (!switchDesktop(openInputDesktop())) {
0, 0, 1, 1, NULL, NULL, throw XScreenOpenFailure();
getInstance(), }
NULL); }
// hide the cursor
leave();
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
} }
void CMSWindowsSecondaryScreen::onCloseDisplay() void CMSWindowsSecondaryScreen::onCloseDisplay()
{ {
assert(m_window != NULL); // disconnect from desktop
if (m_is95Family) {
closeDesktop();
}
else {
switchDesktop(NULL);
}
// remove clipboard snooper // clear thread id
ChangeClipboardChain(m_window, m_nextClipboardWindow); m_threadID = 0;
m_nextClipboardWindow = NULL;
// destroy window assert(m_window == NULL);
DestroyWindow(m_window); assert(m_desk == NULL);
m_window = NULL;
} }
bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
{ {
// handle event
switch (msg->message) {
case WM_TIMER:
// if current desktop is not the input desktop then switch to it
if (!m_is95Family) {
HDESK desk = openInputDesktop();
if (desk != NULL) {
if (isCurrentDesktop(desk)) {
CloseDesktop(desk);
}
else {
switchDesktop(desk);
}
}
}
return true;
}
return false; return false;
} }
@ -402,7 +471,21 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
WPARAM wParam, LPARAM lParam) WPARAM wParam, LPARAM lParam)
{ {
switch (msg) { switch (msg) {
// FIXME -- handle display changes case WM_QUERYENDSESSION:
if (m_is95Family) {
return TRUE;
}
break;
case WM_ENDSESSION:
if (m_is95Family) {
if (wParam == TRUE && lParam == 0) {
stop();
}
return 0;
}
break;
case WM_PAINT: case WM_PAINT:
ValidateRect(hwnd, NULL); ValidateRect(hwnd, NULL);
return 0; return 0;
@ -410,7 +493,6 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
case WM_ACTIVATEAPP: case WM_ACTIVATEAPP:
if (wParam == FALSE) { if (wParam == FALSE) {
// some other app activated. hide the hider window. // some other app activated. hide the hider window.
log((CLOG_INFO "show cursor"));
ShowWindow(m_window, SW_HIDE); ShowWindow(m_window, SW_HIDE);
} }
break; break;
@ -423,9 +505,11 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
// now notify client that somebody changed the clipboard (unless // now notify client that somebody changed the clipboard (unless
// we're now the owner, in which case it's because we took // we're now the owner, in which case it's because we took
// ownership). // ownership, or now it's owned by nobody, which will happen if
// we owned it and switched desktops because we destroy our
// window to do that).
m_clipboardOwner = GetClipboardOwner(); m_clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != m_window) { if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
m_client->onClipboardChanged(kClipboardClipboard); m_client->onClipboardChanged(kClipboardClipboard);
m_client->onClipboardChanged(kClipboardSelection); m_client->onClipboardChanged(kClipboardSelection);
} }
@ -448,6 +532,176 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
return DefWindowProc(hwnd, msg, wParam, lParam); return DefWindowProc(hwnd, msg, wParam, lParam);
} }
void CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y)
{
// warp to requested location
SInt32 w, h;
getScreenSize(&w, &h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(DWORD)((65535.99 * x) / (w - 1)),
(DWORD)((65535.99 * y) / (h - 1)),
0, 0);
// show cursor
ShowWindow(m_window, SW_HIDE);
}
void CMSWindowsSecondaryScreen::onLeave()
{
// move hider window under the mouse (rather than moving the mouse
// somewhere else on the screen)
POINT point;
GetCursorPos(&point);
MoveWindow(m_window, point.x, point.y, 1, 1, FALSE);
// raise and show the hider window. take activation.
ShowWindow(m_window, SW_SHOWNORMAL);
}
bool CMSWindowsSecondaryScreen::openDesktop()
{
CLock lock(&m_mutex);
// initialize clipboard owner to current owner. we don't want
// to take ownership of the clipboard just by starting up.
m_clipboardOwner = GetClipboardOwner();
// create the cursor hiding window. this window is used to hide the
// cursor when it's not on the screen. the window is hidden as soon
// as the cursor enters the screen or the display's real cursor is
// moved.
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, 1, 1, NULL, NULL,
getInstance(),
NULL);
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
return true;
}
void CMSWindowsSecondaryScreen::closeDesktop()
{
CLock lock(&m_mutex);
if (m_window != NULL) {
// remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// destroy window
DestroyWindow(m_window);
m_window = NULL;
}
}
bool CMSWindowsSecondaryScreen::switchDesktop(HDESK desk)
{
CLock lock(&m_mutex);
bool ownClipboard = false;
if (m_window != NULL) {
// note if we own the clipboard
ownClipboard = (m_clipboardOwner == m_window);
// remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// destroy window
DestroyWindow(m_window);
m_window = NULL;
}
// done with desktop
if (m_desk != NULL) {
CloseDesktop(m_desk);
m_desk = NULL;
m_deskName = "";
}
// if no new desktop then we're done
if (desk == NULL) {
log((CLOG_INFO "disconnecting desktop"));
return true;
}
// set the desktop. can only do this when there are no windows
// and hooks on the current desktop owned by this thread.
if (SetThreadDesktop(desk) == 0) {
log((CLOG_ERR "failed to set desktop: %d", GetLastError()));
CloseDesktop(desk);
return false;
}
// initialize clipboard owner to current owner. we don't want
// to take ownership of the clipboard just by starting up.
m_clipboardOwner = GetClipboardOwner();
// create the cursor hiding window. this window is used to hide the
// cursor when it's not on the screen. the window is hidden as soon
// as the cursor enters the screen or the display's real cursor is
// moved.
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, 1, 1, NULL, NULL,
getInstance(),
NULL);
if (m_window == NULL) {
log((CLOG_ERR "failed to create window: %d", GetLastError()));
CloseDesktop(desk);
return false;
}
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
// if we owned the desktop then set the clipboard owner
if (ownClipboard) {
m_clipboardOwner = GetClipboardOwner();
}
// save new desktop
m_desk = desk;
m_deskName = getDesktopName(m_desk);
log((CLOG_INFO "switched to desktop %s", m_deskName.c_str()));
// get desktop up to date
if (!m_active) {
onLeave();
}
return true;
}
void CMSWindowsSecondaryScreen::syncDesktop() const
{
// note -- mutex must be locked on entry
DWORD threadID = GetCurrentThreadId();
if (!m_is95Family) {
if (GetThreadDesktop(threadID) != m_desk) {
// FIXME -- this doesn't work. if we set a desktop then
// sending events doesn't work.
if (SetThreadDesktop(m_desk) == 0) {
log((CLOG_ERR "failed to set desktop: %d", GetLastError()));
}
}
}
AttachThreadInput(threadID, m_threadID, TRUE);
}
CString CMSWindowsSecondaryScreen::getCurrentDesktopName() const
{
return m_deskName;
}
// these tables map KeyID (a X windows KeySym) to virtual key codes. // these tables map KeyID (a X windows KeySym) to virtual key codes.
// if the key is an extended key then the entry is the virtual key // if the key is an extended key then the entry is the virtual key
// code | 0x100. keys that map to normal characters have a 0 entry // code | 0x100. keys that map to normal characters have a 0 entry

View File

@ -3,7 +3,8 @@
#include "CMSWindowsScreen.h" #include "CMSWindowsScreen.h"
#include "ISecondaryScreen.h" #include "ISecondaryScreen.h"
#include "stdmap.h" #include "CMutex.h"
#include "CString.h"
#include "stdvector.h" #include "stdvector.h"
class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen {
@ -39,6 +40,7 @@ protected:
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
virtual void onOpenDisplay(); virtual void onOpenDisplay();
virtual void onCloseDisplay(); virtual void onCloseDisplay();
virtual CString getCurrentDesktopName() const;
private: private:
enum EKeyAction { kPress, kRelease, kRepeat }; enum EKeyAction { kPress, kRelease, kRepeat };
@ -50,6 +52,20 @@ private:
}; };
typedef std::vector<Keystroke> Keystrokes; typedef std::vector<Keystroke> Keystrokes;
void onEnter(SInt32 x, SInt32 y);
void onLeave();
// open/close desktop (for windows 95/98/me)
bool openDesktop();
void closeDesktop();
// make desk the thread desktop (for windows NT/2000/XP)
bool switchDesktop(HDESK desk);
// get calling thread to use the input desktop
void syncDesktop() const;
// key and button queries and operations
DWORD mapButton(ButtonID button, bool press) const; DWORD mapButton(ButtonID button, bool press) const;
KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID,
KeyModifierMask, EKeyAction) const; KeyModifierMask, EKeyAction) const;
@ -63,14 +79,29 @@ private:
void sendKeyEvent(UINT virtualKey, bool press); void sendKeyEvent(UINT virtualKey, bool press);
private: private:
CMutex m_mutex;
CClient* m_client; CClient* m_client;
// true if windows 95/98/me
bool m_is95Family;
// the main loop's thread id
DWORD m_threadID;
// the current desk and it's name
HDESK m_desk;
CString m_deskName;
// our window (for getting clipboard changes)
HWND m_window; HWND m_window;
// m_active is true if this screen has been entered
bool m_active;
// clipboard stuff
HWND m_nextClipboardWindow; HWND m_nextClipboardWindow;
HWND m_clipboardOwner; HWND m_clipboardOwner;
// thread id of the event loop thread
DWORD m_threadID;
// virtual key states // virtual key states
BYTE m_keys[256]; BYTE m_keys[256];

View File

@ -1,6 +1,8 @@
#include "CClient.h" #include "CClient.h"
#include "CString.h" #include "CString.h"
#include "CLog.h" #include "CLog.h"
#include "CCondVar.h"
#include "CLock.h"
#include "CMutex.h" #include "CMutex.h"
#include "CNetwork.h" #include "CNetwork.h"
#include "CNetworkAddress.h" #include "CNetworkAddress.h"
@ -11,6 +13,15 @@
#include "Version.h" #include "Version.h"
#include <assert.h> #include <assert.h>
// platform dependent name of a daemon
#if defined(CONFIG_PLATFORM_WIN32)
#define DAEMON "service"
#define DAEMON_NAME "Synergy Client"
#elif defined(CONFIG_PLATFORM_UNIX)
#define DAEMON "daemon"
#define DAEMON_NAME "synergy"
#endif
// //
// program arguments // program arguments
// //
@ -18,6 +29,8 @@
static const char* pname = NULL; static const char* pname = NULL;
static bool s_restartable = true; static bool s_restartable = true;
static bool s_daemon = true; static bool s_daemon = true;
static bool s_install = false;
static bool s_uninstall = false;
static const char* s_logFilter = NULL; static const char* s_logFilter = NULL;
static const char* s_serverName = NULL; static const char* s_serverName = NULL;
@ -42,13 +55,16 @@ static void logLock(bool lock)
// //
// main // platform independent main
// //
void realMain(const CString& name, static CClient* s_client = NULL;
const CString& hostname,
UInt16 port) static int realMain(CMutex* mutex)
{ {
static const UInt16 port = 50001; // FIXME
try {
// initialize threading library // initialize threading library
CThread::init(); CThread::init();
@ -57,30 +73,74 @@ void realMain(const CString& name,
s_logMutex = &logMutex; s_logMutex = &logMutex;
CLog::setLock(&logLock); CLog::setLock(&logLock);
CClient* client = NULL; bool locked = true;
try { try {
// initialize network library // initialize network library
CNetwork::init(); CNetwork::init();
// create client
CNetworkAddress addr(s_serverName, port);
s_client = new CClient("secondary"); // FIXME
// run client // run client
CNetworkAddress addr(hostname, port); if (mutex != NULL) {
client = new CClient(name); mutex->unlock();
client->run(addr); }
locked = false;
s_client->run(addr);
locked = true;
if (mutex != NULL) {
mutex->lock();
}
// clean up // clean up
delete client; delete s_client;
s_client = NULL;
CNetwork::cleanup(); CNetwork::cleanup();
CLog::setLock(NULL); CLog::setLock(NULL);
s_logMutex = NULL; s_logMutex = NULL;
} }
catch (...) { catch (...) {
// clean up // clean up
delete client; if (!locked && mutex != NULL) {
mutex->lock();
}
delete s_client;
s_client = NULL;
CNetwork::cleanup(); CNetwork::cleanup();
CLog::setLock(NULL); CLog::setLock(NULL);
s_logMutex = NULL; s_logMutex = NULL;
throw; throw;
} }
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
return 16;
}
catch (XThread&) {
// terminated
return 1;
}
return 0;
}
static int restartMain()
{
return realMain(NULL);
}
// invoke realMain and wait for it. if s_restartable then keep
// restarting realMain until it returns a terminate code.
static int restartableMain()
{
if (s_restartable) {
CPlatform platform;
return platform.restart(restartMain, 16);
}
else {
return realMain(NULL);
}
} }
@ -88,11 +148,9 @@ void realMain(const CString& name,
// command line parsing // command line parsing
// //
static void bye() #define BYE "\nTry `%s --help' for more information."
{
log((CLOG_PRINT "Try `%s --help' for more information.", pname)); static void (*bye)(int) = &exit;
exit(1);
}
static void version() static void version()
{ {
@ -113,32 +171,37 @@ static void help()
log((CLOG_PRINT log((CLOG_PRINT
"Usage: %s" "Usage: %s"
" [--debug <level>]" " [--debug <level>]"
" [--daemon|--no-daemon]" " [--"DAEMON"|--no-"DAEMON"]"
" [--restart|--no-restart]" " [--restart|--no-restart]"
" [--install]"
" <server-address>\n" " <server-address>\n"
"or\n"
" --uninstall\n"
"Start the synergy mouse/keyboard sharing server.\n" "Start the synergy mouse/keyboard sharing server.\n"
"\n" "\n"
" -d, --debug <level> filter out log messages with priorty below level.\n" " -d, --debug <level> filter out log messages with priorty below level.\n"
" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
" DEBUG, DEBUG1, DEBUG2.\n" " DEBUG, DEBUG1, DEBUG2.\n"
" -f, --no-daemon run the client in the foreground.\n" " -f, --no-"DAEMON" run the client in the foreground.\n"
" --daemon run the client as a daemon.\n" "* --"DAEMON" run the client as a "DAEMON".\n"
" -1, --no-restart do not try to restart the client if it fails for\n" " -1, --no-restart do not try to restart the client if it fails for\n"
" some reason.\n" " some reason.\n"
" --restart restart the client automatically if it fails.\n" "* --restart restart the client automatically if it fails.\n"
" --install install server as a "DAEMON".\n"
" --uninstall uninstall server "DAEMON".\n"
" -h, --help display this help and exit.\n" " -h, --help display this help and exit.\n"
" --version display version information and exit.\n" " --version display version information and exit.\n"
"\n" "\n"
"By default, the client is a restartable daemon.\n" "* marks defaults.\n"
"\n" "\n"
"Where log messages go depends on the platform and whether or not the\n" "Where log messages go depends on the platform and whether or not the\n"
"client is running as a daemon.", "client is running as a "DAEMON".",
pname)); pname));
} }
static bool isArg(int argi, static bool isArg(int argi,
int argc, char** argv, int argc, const char** argv,
const char* name1, const char* name1,
const char* name2, const char* name2,
int minRequiredParameters = 0) int minRequiredParameters = 0)
@ -147,9 +210,9 @@ static bool isArg(int argi,
(name2 != NULL && strcmp(argv[argi], name2) == 0)) { (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
// match. check args left. // match. check args left.
if (argi + minRequiredParameters >= argc) { if (argi + minRequiredParameters >= argc) {
log((CLOG_PRINT "%s: missing arguments for `%s'", log((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
pname, argv[argi])); pname, argv[argi], pname));
bye(); bye(2);
} }
return true; return true;
} }
@ -158,7 +221,7 @@ static bool isArg(int argi,
return false; return false;
} }
static void parse(int argc, char** argv) static void parse(int argc, const char** argv)
{ {
assert(pname != NULL); assert(pname != NULL);
assert(argv != NULL); assert(argv != NULL);
@ -172,12 +235,12 @@ static void parse(int argc, char** argv)
s_logFilter = argv[++i]; s_logFilter = argv[++i];
} }
else if (isArg(i, argc, argv, "-f", "--no-daemon")) { else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) {
// not a daemon // not a daemon
s_daemon = false; s_daemon = false;
} }
else if (isArg(i, argc, argv, NULL, "--daemon")) { else if (isArg(i, argc, argv, NULL, "--"DAEMON)) {
// daemonize // daemonize
s_daemon = true; s_daemon = true;
} }
@ -194,12 +257,42 @@ static void parse(int argc, char** argv)
else if (isArg(i, argc, argv, "-h", "--help")) { else if (isArg(i, argc, argv, "-h", "--help")) {
help(); help();
exit(1); bye(0);
} }
else if (isArg(i, argc, argv, NULL, "--version")) { else if (isArg(i, argc, argv, NULL, "--version")) {
version(); version();
exit(1); bye(0);
}
else if (isArg(i, argc, argv, NULL, "--install")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_install = true;
if (s_uninstall) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'"
" are mutually exclusive" BYE,
pname, argv[i], pname));
bye(2);
}
}
else if (isArg(i, argc, argv, NULL, "--uninstall")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_uninstall = true;
if (s_install) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'"
" are mutually exclusive" BYE,
pname, argv[i], pname));
bye(2);
}
} }
else if (isArg(i, argc, argv, "--", NULL)) { else if (isArg(i, argc, argv, "--", NULL)) {
@ -209,8 +302,9 @@ static void parse(int argc, char** argv)
} }
else if (argv[i][0] == '-') { else if (argv[i][0] == '-') {
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
bye(); pname, argv[i], pname));
bye(2);
} }
else { else {
@ -219,21 +313,51 @@ static void parse(int argc, char** argv)
} }
} }
// exactly one non-option argument: server-address // exactly one non-option argument (server-address) unless using
// --uninstall.
if (s_uninstall) {
if (i != argc) {
log((CLOG_PRINT "%s: unrecognized option `%s' to `%s'" BYE,
pname, argv[i], pname,
s_install ? "--install" : "--uninstall"));
bye(2);
}
}
else {
if (i == argc) { if (i == argc) {
log((CLOG_PRINT "%s: a server address or name is required", pname)); log((CLOG_PRINT "%s: a server address or name is required" BYE,
bye(); pname, pname));
bye(1);
} }
if (i + 1 != argc) { if (i + 1 != argc) {
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
bye(); pname, argv[i], pname));
bye(2);
} }
s_serverName = argv[i]; s_serverName = argv[i];
}
// increase default filter level for daemon. the user must
// explicitly request another level for a daemon.
if (s_daemon && s_logFilter == NULL) {
#if defined(CONFIG_PLATFORM_WIN32)
if (CPlatform::isWindows95Family()) {
// windows 95 has no place for logging so avoid showing
// the log console window.
s_logFilter = "FATAL";
}
else
#endif
{
s_logFilter = "NOTE";
}
}
// set log filter // set log filter
if (!CLog::setFilter(s_logFilter)) { if (!CLog::setFilter(s_logFilter)) {
log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter)); log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
bye(); pname, s_logFilter, pname));
bye(2);
} }
} }
@ -245,7 +369,71 @@ static void parse(int argc, char** argv)
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
#include "CMSWindowsScreen.h" #include "CMSWindowsScreen.h"
#include <string.h>
static bool logMessageBox(int priority, const char* msg)
{
if (priority <= CLog::kFATAL) {
MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING);
return true;
}
else {
return false;
}
}
static void byeThrow(int x)
{
throw CWin32Platform::CDaemonFailed(x);
}
static void daemonStop(void)
{
s_client->quit();
}
static int daemonStartup(IPlatform* iplatform,
int argc, const char** argv)
{
// get platform pointer
CWin32Platform* platform = static_cast<CWin32Platform*>(iplatform);
// catch errors that would normally exit
bye = &byeThrow;
// parse command line
s_install = false;
s_uninstall = false;
parse(argc, argv);
if (s_install || s_uninstall) {
// not allowed to install/uninstall from service
throw CWin32Platform::CDaemonFailed(1);
}
// run as a service
return platform->runDaemon(realMain, daemonStop);
}
static int daemonStartup95(IPlatform*, int, const char**)
{
return realMain(NULL);
}
static bool logDiscard(int, const char*)
{
return true;
}
static bool s_die = false;
static void checkParse(int e)
{
// anything over 1 means invalid args. 1 means missing args.
// 0 means graceful exit. we plan to exit for anything but
// 1 (missing args); the service control manager may supply
// the missing arguments so we don't exit in that case.
s_die = (e != 1);
throw s_die;
}
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{ {
@ -255,40 +443,115 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
CMSWindowsScreen::init(instance); CMSWindowsScreen::init(instance);
// get program name // get program name
pname = platform.getBasename(argv[0]); pname = platform.getBasename(__argv[0]);
// FIXME -- direct CLog to MessageBox // parse command line without reporting errors but recording if
// the app would've exited. this is too avoid showing a dialog
parse(__argc, __argv); // box if we're being started as a service because we shouldn't
// take too long to startup in that case. this mostly works but
// FIXME -- undirect CLog from MessageBox // will choke if the service control manager passes --install
// FIXME -- if daemon then use win32 event log (however that's done), // or --uninstall (but that's unlikely).
// otherwise do what? want to use console window for debugging but CLog::setOutputter(&logDiscard);
// not otherwise. bye = &checkParse;
// FIXME
try { try {
realMain("secondary", s_serverName, 50001); parse(__argc, const_cast<const char**>(__argv));
}
catch (...) {
// ignore
}
// if we're not starting as an NT service then reparse the command
// line normally.
if (s_die || !s_daemon || s_install || s_uninstall ||
CWin32Platform::isWindows95Family()) {
// send PRINT and FATAL output to a message box
CLog::setOutputter(&logMessageBox);
// exit on bye
bye = &exit;
// reparse
parse(__argc, const_cast<const char**>(__argv));
}
// if starting as a daemon then we ignore the startup command line
// here. we'll parse the command line passed in when the service
// control manager calls us back.
else {
// do nothing
}
// install/uninstall
if (s_install) {
// get the full path to this program
TCHAR path[MAX_PATH];
if (GetModuleFileName(NULL, path,
sizeof(path) / sizeof(path[0])) == 0) {
log((CLOG_CRIT "cannot determine absolute path to program"));
return 16;
}
// construct the command line to start the service with
CString commandLine = "--"DAEMON;
if (s_restartable) {
commandLine += " --restart";
}
else {
commandLine += " --no-restart";
}
if (s_logFilter != NULL) {
commandLine += " --debug ";
commandLine += s_logFilter;
}
commandLine += " ";
commandLine += s_serverName;
// install
if (!platform.installDaemon(DAEMON_NAME,
"Shares this system's mouse and keyboard with others.",
path, commandLine.c_str())) {
log((CLOG_CRIT "failed to install service"));
return 16;
}
log((CLOG_PRINT "installed successfully"));
return 0; return 0;
} }
catch (XBase& e) { else if (s_uninstall) {
log((CLOG_CRIT "failed: %s", e.what())); if (!platform.uninstallDaemon(DAEMON_NAME)) {
CString msg = "failed: "; log((CLOG_CRIT "failed to uninstall service"));
msg += e.what(); return 16;
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
return 1;
} }
catch (XThread&) { log((CLOG_PRINT "uninstalled successfully"));
// terminated return 0;
return 1;
} }
// daemonize if requested
int result;
if (s_daemon) {
if (CWin32Platform::isWindows95Family()) {
result = platform.daemonize(DAEMON_NAME, &daemonStartup95);
}
else {
result = platform.daemonize(DAEMON_NAME, &daemonStartup);
}
if (result == -1) {
log((CLOG_CRIT "failed to start as a service"));
return 16;
}
}
else {
result = restartableMain();
}
return result;
} }
#elif defined(CONFIG_PLATFORM_UNIX) #elif defined(CONFIG_PLATFORM_UNIX)
#include <unistd.h> static int daemonStartup(IPlatform*, int, const char**)
#include <sys/types.h> {
#include <sys/wait.h> return restartableMain();
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
@ -298,63 +561,22 @@ int main(int argc, char** argv)
pname = platform.getBasename(argv[0]); pname = platform.getBasename(argv[0]);
// parse command line // parse command line
parse(argc, argv); parse(argc, const_cast<const char**>(argv));
// daemonize if requested // daemonize if requested
int result;
if (s_daemon) { if (s_daemon) {
if (!platform.daemonize("synergy")) { result = platform.daemonize(DAEMON_NAME, &daemonStartup);
if (result == -1) {
log((CLOG_CRIT "failed to daemonize")); log((CLOG_CRIT "failed to daemonize"));
return 16; return 16;
} }
} }
else {
// run the server. if running as a daemon then run it in a child result = restartableMain();
// process and restart it as necessary. we have to do this in case
// the X server restarts because our process cannot recover from
// that.
for (;;) {
// don't fork if not restartable
switch (s_restartable ? fork() : 0) {
default: {
// parent process. wait for child to exit.
int status;
if (wait(&status) == -1) {
// wait failed. this is unexpected so bail.
log((CLOG_CRIT "wait() failed"));
return 16;
} }
// what happened? if the child exited normally with a return result;
// status less than 16 then the child was deliberately
// terminated so we also terminate. otherwise, we
// loop.
if (WIFEXITED(status) && WEXITSTATUS(status) < 16) {
return 0;
}
break;
}
case -1:
// fork() failed. log the error and proceed as a child
log((CLOG_WARN "fork() failed; cannot automatically restart on error"));
// fall through
case 0:
// child process
try {
realMain("secondary", s_serverName, 50001);
return 0;
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
return 16;
}
catch (XThread&) {
// terminated
return 1;
}
}
}
} }
#else #else

View File

@ -23,8 +23,8 @@ CFG=client - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
MTL=midl.exe MTL=midl.exe
RSC=rc.exe RSC=rc.exe
@ -40,6 +40,7 @@ RSC=rc.exe
# PROP Use_Debug_Libraries 0 # PROP Use_Debug_Libraries 0
# PROP Output_Dir "../Release" # PROP Output_Dir "../Release"
# PROP Intermediate_Dir "Release" # PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "" # PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
@ -53,7 +54,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo # ADD BSC32 /nologo
LINK32=link.exe LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../Release/synergy.exe"
!ELSEIF "$(CFG)" == "client - Win32 Debug" !ELSEIF "$(CFG)" == "client - Win32 Debug"
@ -66,6 +67,7 @@ LINK32=link.exe
# PROP Use_Debug_Libraries 1 # PROP Use_Debug_Libraries 1
# PROP Output_Dir "../Debug" # PROP Output_Dir "../Debug"
# PROP Intermediate_Dir "Debug" # PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "" # PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c
@ -79,7 +81,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo # ADD BSC32 /nologo
LINK32=link.exe LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../Debug/synergy.exe" /pdbtype:sept
!ENDIF !ENDIF

View File

@ -23,8 +23,8 @@ CFG=http - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
RSC=rc.exe RSC=rc.exe

View File

@ -23,8 +23,8 @@ CFG=io - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
RSC=rc.exe RSC=rc.exe

View File

@ -614,17 +614,9 @@ unsigned int __stdcall CThreadRep::threadFunc(void* arg)
{ {
CThreadRep* rep = (CThreadRep*)arg; CThreadRep* rep = (CThreadRep*)arg;
// initialize OLE
const HRESULT hr = OleInitialize(NULL);
// run thread // run thread
rep->doThreadFunc(); rep->doThreadFunc();
// close OLE
if (!FAILED(hr)) {
OleUninitialize();
}
// signal termination // signal termination
SetEvent(rep->m_exit); SetEvent(rep->m_exit);

View File

@ -23,8 +23,8 @@ CFG=mt - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
RSC=rc.exe RSC=rc.exe

View File

@ -23,8 +23,8 @@ CFG=net - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
RSC=rc.exe RSC=rc.exe

View File

@ -152,6 +152,38 @@ void CMSWindowsScreen::getScreenSize(
*h = m_h; *h = m_h;
} }
HDESK CMSWindowsScreen::openInputDesktop() const
{
return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE,
DESKTOP_CREATEWINDOW |
DESKTOP_HOOKCONTROL |
GENERIC_WRITE);
}
CString CMSWindowsScreen::getDesktopName(
HDESK desk) const
{
if (desk == NULL) {
return CString();
}
else {
DWORD size;
GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
TCHAR* name = new TCHAR[size / sizeof(TCHAR) + 1];
GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
CString result(name);
delete[] name;
return result;
}
}
bool CMSWindowsScreen::isCurrentDesktop(
HDESK desk) const
{
return CStringUtil::CaselessCmp::equal(getDesktopName(desk),
getCurrentDesktopName());
}
void CMSWindowsScreen::getEvent(MSG* msg) const void CMSWindowsScreen::getEvent(MSG* msg) const
{ {
// wait for an event in a cancellable way // wait for an event in a cancellable way

View File

@ -18,6 +18,11 @@ public:
static void init(HINSTANCE); static void init(HINSTANCE);
// accessors
// get the application instance handle
static HINSTANCE getInstance();
protected: protected:
// runs an event loop and returns when WM_QUIT is received // runs an event loop and returns when WM_QUIT is received
void doRun(); void doRun();
@ -35,9 +40,7 @@ protected:
// is closed. // is closed.
void closeDisplay(); void closeDisplay();
// get the application instance handle and the registered window // get the registered window class atom
// class atom
static HINSTANCE getInstance();
ATOM getClass() const; ATOM getClass() const;
// update screen size cache // update screen size cache
@ -46,6 +49,17 @@ protected:
// get the size of the screen // get the size of the screen
void getScreenSize(SInt32* w, SInt32* h) const; void getScreenSize(SInt32* w, SInt32* h) const;
// get the input desktop. caller must CloseDesktop() the result.
// do not call under windows 95/98/me.
HDESK openInputDesktop() const;
// get the desktop's name. do not call under windows 95/98/me.
CString getDesktopName(HDESK) const;
// returns true iff desk is the current desk. do not call under
// windows 95/98/me.
bool isCurrentDesktop(HDESK desk) const;
// wait for and get the next message. cancellable. // wait for and get the next message. cancellable.
void getEvent(MSG*) const; void getEvent(MSG*) const;
@ -62,6 +76,9 @@ protected:
// called by closeDisplay() to // called by closeDisplay() to
virtual void onCloseDisplay() = 0; virtual void onCloseDisplay() = 0;
// called by isCurrentDesktop() to get the current desktop name
virtual CString getCurrentDesktopName() const = 0;
private: private:
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);

View File

@ -5,9 +5,11 @@
#include <pwd.h> #include <pwd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h> #include <fcntl.h>
#include <syslog.h> #include <syslog.h>
// //
// CUnixPlatform // CUnixPlatform
// //
@ -22,26 +24,31 @@ CUnixPlatform::~CUnixPlatform()
// do nothing // do nothing
} }
bool CUnixPlatform::installDaemon(/* FIXME */) bool CUnixPlatform::installDaemon(
const char*,
const char*,
const char*,
const char*)
{ {
// daemons don't require special installation // daemons don't require special installation
return true; return true;
} }
bool CUnixPlatform::uninstallDaemon(/* FIXME */) bool CUnixPlatform::uninstallDaemon(const char*)
{ {
// daemons don't require special installation // daemons don't require special installation
return true; return true;
} }
bool CUnixPlatform::daemonize(const char* name) int CUnixPlatform::daemonize(
const char* name, DaemonFunc func)
{ {
// fork so shell thinks we're done and so we're not a process // fork so shell thinks we're done and so we're not a process
// group leader // group leader
switch (fork()) { switch (fork()) {
case -1: case -1:
// failed // failed
return false; return -1;
case 0: case 0:
// child // child
@ -75,7 +82,44 @@ bool CUnixPlatform::daemonize(const char* name)
// hook up logger // hook up logger
setDaemonLogger(name); setDaemonLogger(name);
return true; // invoke function
return func(this, 1, &name);
}
int CUnixPlatform::restart(
RestartFunc func, int minErrorCode)
{
for (;;) {
switch (fork()) {
default: {
// parent process. wait for child to exit.
int status;
if (wait(&status) == -1) {
// wait failed. this is unexpected so bail.
log((CLOG_CRIT "wait() failed"));
return minErrorCode;
}
// what happened? if the child exited normally with a
// status less than 16 then the child was deliberately
// terminated so we also terminate. otherwise, we
// loop.
if (WIFEXITED(status) && WEXITSTATUS(status) < minErrorCode) {
return WEXITSTATUS(status);
}
break;
}
case -1:
// fork() failed. log the error and proceed as a child
log((CLOG_WARN "fork() failed; cannot automatically restart on error"));
// fall through
case 0:
// child process
return func();
}
}
} }
const char* CUnixPlatform::getBasename(const char* pathname) const const char* CUnixPlatform::getBasename(const char* pathname) const
@ -117,7 +161,9 @@ CString CUnixPlatform::addPathComponent(
CString path; CString path;
path.reserve(prefix.size() + 1 + suffix.size()); path.reserve(prefix.size() + 1 + suffix.size());
path += prefix; path += prefix;
if (path.size() == 0 || path[path.size() - 1] != '/') {
path += '/'; path += '/';
}
path += suffix; path += suffix;
return path; return path;
} }
@ -128,7 +174,7 @@ void CUnixPlatform::setDaemonLogger(const char* name)
CLog::setOutputter(&CUnixPlatform::deamonLogger); CLog::setOutputter(&CUnixPlatform::deamonLogger);
} }
void CUnixPlatform::deamonLogger( bool CUnixPlatform::deamonLogger(
int priority, const char* msg) int priority, const char* msg)
{ {
// convert priority // convert priority
@ -157,4 +203,5 @@ void CUnixPlatform::deamonLogger(
// log it // log it
syslog(priority, "%s", msg); syslog(priority, "%s", msg);
return true;
} }

View File

@ -9,9 +9,13 @@ public:
virtual ~CUnixPlatform(); virtual ~CUnixPlatform();
// IPlatform overrides // IPlatform overrides
virtual bool installDaemon(/* FIXME */); virtual bool installDaemon(const char* name,
virtual bool uninstallDaemon(/* FIXME */); const char* description,
virtual bool daemonize(const char* name); const char* pathname,
const char* commandLine);
virtual bool uninstallDaemon(const char* name);
virtual int daemonize(const char* name, DaemonFunc);
virtual int restart(RestartFunc, int minErrorCode);
virtual const char* getBasename(const char* pathname) const; virtual const char* getBasename(const char* pathname) const;
virtual CString getUserDirectory() const; virtual CString getUserDirectory() const;
virtual CString getSystemDirectory() const; virtual CString getSystemDirectory() const;
@ -23,7 +27,7 @@ protected:
virtual void setDaemonLogger(const char* name); virtual void setDaemonLogger(const char* name);
private: private:
static void deamonLogger(int, const char*); static bool deamonLogger(int, const char*);
}; };
#endif #endif

View File

@ -1,12 +1,20 @@
#include "CWin32Platform.h" #include "CWin32Platform.h"
#include "CLock.h"
#include "CThread.h"
#include "CLog.h" #include "CLog.h"
#include "stdvector.h"
#include <string.h> #include <string.h>
#include <windows.h> #include <shlobj.h>
#include <tchar.h>
#include <assert.h>
// //
// CWin32Platform // CWin32Platform
// //
HANDLE CWin32Platform::s_eventLog = NULL;
CWin32Platform* CWin32Platform::s_daemonPlatform = NULL;
CWin32Platform::CWin32Platform() CWin32Platform::CWin32Platform()
{ {
// do nothing // do nothing
@ -17,22 +25,282 @@ CWin32Platform::~CWin32Platform()
// do nothing // do nothing
} }
bool CWin32Platform::installDaemon(/* FIXME */) bool CWin32Platform::isWindows95Family()
{ {
// FIXME OSVERSIONINFO version;
return false; version.dwOSVersionInfoSize = sizeof(version);
if (GetVersionEx(&version) == 0) {
log((CLOG_WARN "cannot determine OS: %d", GetLastError()));
return true;
}
return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
} }
bool CWin32Platform::uninstallDaemon(/* FIXME */) void CWin32Platform::setStatus(
SERVICE_STATUS_HANDLE handle,
DWORD state)
{ {
// FIXME setStatus(handle, state, 0, 0);
return false;
} }
bool CWin32Platform::daemonize(const char* name) void CWin32Platform::setStatus(
SERVICE_STATUS_HANDLE handle,
DWORD state, DWORD step, DWORD waitHint)
{ {
// FIXME SERVICE_STATUS status;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
SERVICE_INTERACTIVE_PROCESS;
status.dwCurrentState = state;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = NO_ERROR;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = step;
status.dwWaitHint = waitHint;
SetServiceStatus(handle, &status);
}
void CWin32Platform::setStatusError(
SERVICE_STATUS_HANDLE handle,
DWORD error)
{
SERVICE_STATUS status;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
SERVICE_INTERACTIVE_PROCESS;
status.dwCurrentState = SERVICE_STOPPED;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
status.dwServiceSpecificExitCode = error;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
SetServiceStatus(handle, &status);
}
bool CWin32Platform::installDaemon(
const char* name,
const char* description,
const char* pathname,
const char* commandLine)
{
// windows 95 family services
if (isWindows95Family()) {
// open registry
HKEY key = open95ServicesKey();
if (key == NULL) {
log((CLOG_ERR "cannot open RunServices registry key", GetLastError()));
return false; return false;
}
// construct entry
CString value;
value += "\"";
value += pathname;
value += "\" ";
value += commandLine;
// install entry
setValue(key, name, value);
// clean up
closeKey(key);
return true;
}
// windows NT family services
else {
// open service manager
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
if (mgr == NULL) {
log((CLOG_ERR "OpenSCManager failed with %d", GetLastError()));
return false;
}
// create the servie
SC_HANDLE service = CreateService(mgr,
name,
name,
0,
SERVICE_WIN32_OWN_PROCESS |
SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
pathname,
NULL,
NULL,
NULL,
NULL,
NULL);
// done with service and manager
if (service != NULL) {
CloseServiceHandle(service);
CloseServiceHandle(mgr);
}
else {
log((CLOG_ERR "CreateService failed with %d", GetLastError()));
CloseServiceHandle(mgr);
return false;
}
// open the registry key for this service
HKEY key = openNTServicesKey();
key = openKey(key, name);
if (key == NULL) {
// can't open key
uninstallDaemon(name);
return false;
}
// set the description
setValue(key, "Description", description);
// set command line
key = openKey(key, "Parameters");
if (key == NULL) {
// can't open key
uninstallDaemon(name);
return false;
}
setValue(key, "CommandLine", commandLine);
// done with registry
closeKey(key);
return true;
}
}
bool CWin32Platform::uninstallDaemon(const char* name)
{
// windows 95 family services
if (isWindows95Family()) {
// open registry
HKEY key = open95ServicesKey();
if (key == NULL) {
log((CLOG_ERR "cannot open RunServices registry key", GetLastError()));
return false;
}
// remove entry
deleteValue(key, name);
// clean up
closeKey(key);
return true;
}
// windows NT family services
else {
// remove parameters for this service
HKEY key = openNTServicesKey();
key = openKey(key, name);
if (key != NULL) {
deleteKey(key, "Parameters");
closeKey(key);
}
// open service manager
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
if (mgr == NULL) {
log((CLOG_ERR "OpenSCManager failed with %d", GetLastError()));
return false;
}
// open the service. oddly, you must open a service to delete it.
bool success;
SC_HANDLE service = OpenService(mgr, name, DELETE);
if (service == NULL) {
log((CLOG_ERR "OpenService failed with %d", GetLastError()));
success = false;
}
else {
success = (DeleteService(service) != 0);
CloseServiceHandle(service);
}
// close the manager
CloseServiceHandle(mgr);
return success;
}
}
int CWin32Platform::daemonize(
const char* name,
DaemonFunc func)
{
assert(name != NULL);
assert(func != NULL);
// windows 95 family services
if (isWindows95Family()) {
typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD);
// mark this process as a service so it's not killed when the
// user logs off.
HINSTANCE kernel = LoadLibrary("kernel32.dll");
if (kernel == NULL) {
log((CLOG_ERR "LoadLibrary failed with %d", GetLastError()));
return -1;
}
RegisterServiceProcessT RegisterServiceProcess =
reinterpret_cast<RegisterServiceProcessT>(
GetProcAddress(kernel,
_T("RegisterServiceProcess")));
if (RegisterServiceProcess == NULL) {
log((CLOG_ERR "can't lookup RegisterServiceProcess: %d", GetLastError()));
FreeLibrary(kernel);
return -1;
}
if (RegisterServiceProcess(NULL, 1) == 0) {
log((CLOG_ERR "RegisterServiceProcess failed with %d", GetLastError()));
FreeLibrary(kernel);
return -1;
}
FreeLibrary(kernel);
// now simply call the daemon function
return func(this, 1, &name);
}
// windows NT family services
else {
// save daemon function
m_daemonFunc = func;
// construct the service entry
SERVICE_TABLE_ENTRY entry[2];
entry[0].lpServiceName = const_cast<char*>(name);
entry[0].lpServiceProc = &CWin32Platform::serviceMainEntry;
entry[1].lpServiceName = NULL;
entry[1].lpServiceProc = NULL;
// hook us up to the service control manager. this won't return
// (if successful) until the processes have terminated.
s_daemonPlatform = this;
if (StartServiceCtrlDispatcher(entry)) {
s_daemonPlatform = NULL;
return m_daemonResult;
}
log((CLOG_ERR "StartServiceCtrlDispatcher failed with %d", GetLastError()));
s_daemonPlatform = NULL;
return -1;
}
}
int CWin32Platform::restart(
RestartFunc func, int /*minErrorCode*/)
{
// FIXME -- start in separate process or thread. note that this
// isn't too critical as win32 doesn't force us to terminate for
// any reason so we should never have to restart.
return func();
} }
const char* CWin32Platform::getBasename(const char* pathname) const const char* CWin32Platform::getBasename(const char* pathname) const
@ -61,14 +329,58 @@ const char* CWin32Platform::getBasename(const char* pathname) const
CString CWin32Platform::getUserDirectory() const CString CWin32Platform::getUserDirectory() const
{ {
// FIXME // try %HOMEPATH%
return CString(); TCHAR dir[MAX_PATH];
DWORD size = sizeof(dir) / sizeof(TCHAR);
DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size);
if (result != 0 && result <= size) {
// sanity check -- if dir doesn't appear to start with a
// drive letter and isn't a UNC name then don't use it
// FIXME -- allow UNC names
if (dir[0] != '\0' && (dir[1] == ':' ||
((dir[0] == '\\' || dir[0] == '/') &&
(dir[1] == '\\' || dir[1] == '/')))) {
return dir;
}
}
// get the location of the personal files. that's as close to
// a home directory as we're likely to find.
ITEMIDLIST* idl;
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) {
TCHAR* path = NULL;
if (SHGetPathFromIDList(idl, dir)) {
DWORD attr = GetFileAttributes(dir);
if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
path = dir;
}
IMalloc* shalloc;
if (SUCCEEDED(SHGetMalloc(&shalloc))) {
shalloc->Free(idl);
shalloc->Release();
}
if (path != NULL) {
return path;
}
}
// use root of C drive as a default
return "C:";
} }
CString CWin32Platform::getSystemDirectory() const CString CWin32Platform::getSystemDirectory() const
{ {
// FIXME // get windows directory
return ""; char dir[MAX_PATH];
if (GetWindowsDirectory(dir, sizeof(dir)) != 0) {
return dir;
}
else {
// can't get it. use C:\ as a default.
return "C:";
}
} }
CString CWin32Platform::addPathComponent( CString CWin32Platform::addPathComponent(
@ -78,13 +390,453 @@ CString CWin32Platform::addPathComponent(
CString path; CString path;
path.reserve(prefix.size() + 1 + suffix.size()); path.reserve(prefix.size() + 1 + suffix.size());
path += prefix; path += prefix;
if (path.size() == 0 ||
(path[path.size() - 1] != '\\' &&
path[path.size() - 1] != '/')) {
path += '\\'; path += '\\';
}
path += suffix; path += suffix;
return path; return path;
} }
void CWin32Platform::serviceLogger( HKEY CWin32Platform::openKey(
HKEY key, const char* keyName)
{
// open next key
HKEY newKey;
LONG result = RegOpenKeyEx(key, keyName, 0,
KEY_WRITE | KEY_QUERY_VALUE, &newKey);
if (result != ERROR_SUCCESS) {
DWORD disp;
result = RegCreateKeyEx(key, keyName, 0, _T(""),
0, KEY_WRITE | KEY_QUERY_VALUE,
NULL, &newKey, &disp);
}
if (result != ERROR_SUCCESS) {
RegCloseKey(key);
return NULL;
}
// switch to new key
RegCloseKey(key);
return newKey;
}
HKEY CWin32Platform::openKey(
HKEY key, const char** keyNames)
{
for (UInt32 i = 0; key != NULL && keyNames[i] != NULL; ++i) {
// open next key
key = openKey(key, keyNames[i]);
}
return key;
}
void CWin32Platform::closeKey(HKEY key)
{
assert(key != NULL);
RegCloseKey(key);
}
void CWin32Platform::deleteKey(HKEY key, const char* name)
{
assert(key != NULL);
assert(name != NULL);
RegDeleteKey(key, name);
}
void CWin32Platform::deleteValue(HKEY key, const char* name)
{
assert(key != NULL);
assert(name != NULL);
RegDeleteValue(key, name);
}
void CWin32Platform::setValue(HKEY key,
const char* name, const CString& value)
{
assert(key != NULL);
assert(name != NULL);
RegSetValueEx(key, name, 0, REG_SZ,
reinterpret_cast<const BYTE*>(value.c_str()),
value.size() + 1);
}
CString CWin32Platform::readValueString(HKEY key,
const char* name)
{
// get the size of the string
DWORD type;
DWORD size = 0;
LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size);
if (result != ERROR_SUCCESS || type != REG_SZ) {
return CString();
}
// allocate space
char* buffer = new char[size];
// read it
result = RegQueryValueEx(key, name, 0, &type,
reinterpret_cast<BYTE*>(buffer), &size);
if (result != ERROR_SUCCESS || type != REG_SZ) {
delete[] buffer;
return CString();
}
// clean up and return value
CString value(buffer);
delete[] buffer;
return value;
}
HKEY CWin32Platform::openNTServicesKey()
{
static const char* s_keyNames[] = {
_T("SYSTEM"),
_T("CurrentControlSet"),
_T("Services"),
NULL
};
return openKey(HKEY_LOCAL_MACHINE, s_keyNames);
}
HKEY CWin32Platform::open95ServicesKey()
{
static const char* s_keyNames[] = {
_T("Software"),
_T("Microsoft"),
_T("Windows"),
_T("CurrentVersion"),
_T("RunServices"),
NULL
};
return openKey(HKEY_LOCAL_MACHINE, s_keyNames);
}
int CWin32Platform::runDaemon(RunFunc run, StopFunc stop)
{
// should only be called from DaemonFunc
assert(m_serviceMutex != NULL);
CLock lock(m_serviceMutex);
try {
int result;
m_stop = stop;
m_serviceHandlerWaiting = false;
m_serviceRunning = false;
for (;;) {
// mark server as running
setStatus(m_statusHandle, SERVICE_RUNNING);
// run callback
m_serviceRunning = true;
result = run(m_serviceMutex);
m_serviceRunning = false;
// notify handler that the server stopped. if handler
// isn't waiting then we stopped unexpectedly and we
// quit.
if (m_serviceHandlerWaiting) {
m_serviceHandlerWaiting = false;
m_serviceState->broadcast();
}
else {
break;
}
// wait until we're told what to do next
while (*m_serviceState != SERVICE_RUNNING &&
*m_serviceState != SERVICE_STOPPED) {
m_serviceState->wait();
}
// exit loop if we've been told to stop
if (*m_serviceState == SERVICE_STOPPED) {
break;
}
}
// prevent daemonHandler from changing state
*m_serviceState = SERVICE_STOPPED;
// tell service control that the service is stopped.
// FIXME -- hopefully this will ensure that our handler won't
// be called again but i can't find documentation that
// verifies that. if it does it'll crash on the mutex that
// we're about to destroy.
setStatus(m_statusHandle, *m_serviceState);
// clean up
m_stop = NULL;
return result;
}
catch (...) {
// FIXME -- report error
// prevent serviceHandler from changing state
*m_serviceState = SERVICE_STOPPED;
// set status
setStatusError(m_statusHandle, 0);
// wake up serviceHandler if it's waiting then wait for it
if (m_serviceHandlerWaiting) {
m_serviceHandlerWaiting = false;
m_serviceState->broadcast();
m_serviceState->wait();
// serviceHandler has exited by now
}
throw;
}
}
void CWin32Platform::serviceMain(
DWORD argc, LPTSTR* argvIn)
{
typedef std::vector<LPCTSTR> ArgList;
typedef std::vector<CString> Arguments;
const char** argv = const_cast<const char**>(argvIn);
// open event log and direct log messages to it
if (s_eventLog == NULL) {
s_eventLog = RegisterEventSource(NULL, argv[0]);
if (s_eventLog != NULL) {
CLog::setOutputter(&CWin32Platform::serviceLogger);
}
}
// create synchronization objects
CThread::init();
m_serviceMutex = new CMutex;
m_serviceState = new CCondVar<DWORD>(m_serviceMutex, SERVICE_RUNNING);
// register our service handler functiom
m_statusHandle = RegisterServiceCtrlHandler(argv[0],
&CWin32Platform::serviceHandlerEntry);
if (m_statusHandle == NULL) {
// cannot start as service
m_daemonResult = -1;
delete m_serviceState;
delete m_serviceMutex;
return;
}
// tell service control manager that we're starting
setStatus(m_statusHandle, SERVICE_START_PENDING, 0, 1000);
// if no arguments supplied then try getting them from the registry.
// the first argument doesn't count because it's the service name.
Arguments args;
ArgList myArgv;
if (argc <= 1) {
// read command line
CString commandLine;
HKEY key = openNTServicesKey();
key = openKey(key, argv[0]);
key = openKey(key, "Parameters");
if (key != NULL) {
commandLine = readValueString(key, "CommandLine");
}
// if the command line isn't empty then parse and use it
if (!commandLine.empty()) {
// parse, honoring double quoted substrings
CString::size_type i = commandLine.find_first_not_of(" \t");
while (i != CString::npos && i != commandLine.size()) {
// find end of string
CString::size_type e;
if (commandLine[i] == '\"') {
// quoted. find closing quote.
++i;
e = commandLine.find("\"", i);
// whitespace must follow closing quote
if (e == CString::npos ||
(e + 1 != commandLine.size() &&
commandLine[e + 1] != ' ' &&
commandLine[e + 1] != '\t')) {
args.clear();
break;
}
// extract
args.push_back(commandLine.substr(i, e - i));
i = e + 1;
}
else {
// unquoted. find next whitespace.
e = commandLine.find_first_of(" \t", i);
if (e == CString::npos) {
e = commandLine.size();
}
// extract
args.push_back(commandLine.substr(i, e - i));
i = e + 1;
}
// next argument
i = commandLine.find_first_not_of(" \t", i);
}
// service name goes first
myArgv.push_back(argv[0]);
// get pointers
for (UInt32 i = 0; i < args.size(); ++i) {
myArgv.push_back(args[i].c_str());
}
// adjust argc/argv
argc = myArgv.size();
argv = &myArgv[0];
}
}
try {
// invoke daemon function
m_daemonResult = m_daemonFunc(this, static_cast<int>(argc), argv);
}
catch (CDaemonFailed& e) {
setStatusError(m_statusHandle, e.m_result);
m_daemonResult = -1;
}
catch (...) {
setStatusError(m_statusHandle, 1);
m_daemonResult = -1;
}
// clean up
delete m_serviceState;
delete m_serviceMutex;
// FIXME -- close event log?
}
void WINAPI CWin32Platform::serviceMainEntry(
DWORD argc, LPTSTR* argv)
{
s_daemonPlatform->serviceMain(argc, argv);
}
void CWin32Platform::serviceHandler(DWORD ctrl)
{
assert(m_serviceMutex != NULL);
assert(m_serviceState != NULL);
CLock lock(m_serviceMutex);
// ignore request if service is already stopped
if (*m_serviceState == SERVICE_STOPPED) {
setStatus(m_statusHandle, *m_serviceState);
return;
}
switch (ctrl) {
case SERVICE_CONTROL_PAUSE:
// update state
*m_serviceState = SERVICE_PAUSE_PENDING;
setStatus(m_statusHandle, *m_serviceState, 0, 1000);
// stop run callback if running and wait for it to finish
if (m_serviceRunning) {
m_serviceHandlerWaiting = true;
m_stop();
m_serviceState->wait();
}
// update state if service hasn't stopped while we were waiting
if (*m_serviceState != SERVICE_STOPPED) {
*m_serviceState = SERVICE_PAUSED;
}
m_serviceState->broadcast();
break;
case SERVICE_CONTROL_CONTINUE:
// required status update
setStatus(m_statusHandle, *m_serviceState);
// update state but let main loop send RUNNING notification
*m_serviceState = SERVICE_RUNNING;
m_serviceState->broadcast();
return;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
// update state
*m_serviceState = SERVICE_STOP_PENDING;
setStatus(m_statusHandle, *m_serviceState, 0, 1000);
// stop run callback if running and wait for it to finish
if (m_serviceRunning) {
m_serviceHandlerWaiting = true;
m_stop();
m_serviceState->wait();
}
// update state
*m_serviceState = SERVICE_STOPPED;
m_serviceState->broadcast();
break;
default:
log((CLOG_WARN "unknown service command: %d", ctrl));
// fall through
case SERVICE_CONTROL_INTERROGATE:
break;
}
// send update
setStatus(m_statusHandle, *m_serviceState);
}
void WINAPI CWin32Platform::serviceHandlerEntry(DWORD ctrl)
{
s_daemonPlatform->serviceHandler(ctrl);
}
bool CWin32Platform::serviceLogger(
int priority, const char* msg) int priority, const char* msg)
{ {
// FIXME if (s_eventLog == NULL) {
return false;
}
// convert priority
WORD type;
switch (priority) {
case CLog::kFATAL:
case CLog::kERROR:
type = EVENTLOG_ERROR_TYPE;
break;
case CLog::kWARNING:
type = EVENTLOG_WARNING_TYPE;
break;
default:
type = EVENTLOG_INFORMATION_TYPE;
break;
}
// log it
// FIXME -- win32 wants to use a message table to look up event
// strings. log messages aren't organized that way so we'll
// just dump our string into the raw data section of the event
// so users can at least see the message. note that we use our
// priority as the event category.
ReportEvent(s_eventLog, type, static_cast<WORD>(priority),
0, // event ID
NULL,
0,
strlen(msg + 1), // raw data size
NULL,
const_cast<char*>(msg));// raw data
return true;
} }

View File

@ -2,16 +2,52 @@
#define CWIN32PLATFORM_H #define CWIN32PLATFORM_H
#include "IPlatform.h" #include "IPlatform.h"
#include "CCondVar.h"
#include "CMutex.h"
#include <windows.h>
class CWin32Platform : public IPlatform { class CWin32Platform : public IPlatform {
public: public:
typedef int (*RunFunc)(CMutex*);
typedef void (*StopFunc)(void);
CWin32Platform(); CWin32Platform();
virtual ~CWin32Platform(); virtual ~CWin32Platform();
// returns true iff the platform is win95/98/me
static bool isWindows95Family();
// utility for calling SetServiceStatus()
static void setStatus(SERVICE_STATUS_HANDLE, DWORD state);
static void setStatus(SERVICE_STATUS_HANDLE,
DWORD state, DWORD step, DWORD waitHint);
static void setStatusError(SERVICE_STATUS_HANDLE, DWORD error);
// run a service. the RunFunc should unlock the passed in mutex
// (which will be locked on entry) when not initializing or
// shutting down (i.e. when running its loop). StopFunc should
// cause the RunFunc() to return. returns what RunFunc returns.
// RunFunc should throw CDaemonFailed if the service fails.
int runDaemon(RunFunc, StopFunc);
// thrown by RunFunc on service failure. result is the error
// code reported by the service.
class CDaemonFailed {
public:
CDaemonFailed(int result) : m_result(result) { }
public:
int m_result;
};
// IPlatform overrides // IPlatform overrides
virtual bool installDaemon(/* FIXME */); virtual bool installDaemon(const char* name,
virtual bool uninstallDaemon(/* FIXME */); const char* description,
virtual bool daemonize(const char* name); const char* pathname,
const char* commandLine);
virtual bool uninstallDaemon(const char* name);
virtual int daemonize(const char* name, DaemonFunc);
virtual int restart(RestartFunc, int minErrorCode);
virtual const char* getBasename(const char* pathname) const; virtual const char* getBasename(const char* pathname) const;
virtual CString getUserDirectory() const; virtual CString getUserDirectory() const;
virtual CString getSystemDirectory() const; virtual CString getSystemDirectory() const;
@ -20,7 +56,40 @@ public:
const CString& suffix) const; const CString& suffix) const;
private: private:
static void serviceLogger(int, const char*); static HKEY openKey(HKEY parent, const char*);
static HKEY openKey(HKEY parent, const char**);
static void closeKey(HKEY);
static void deleteKey(HKEY, const char* name);
static void deleteValue(HKEY, const char* name);
static void setValue(HKEY, const char* name,
const CString& value);
static CString readValueString(HKEY, const char* name);
static HKEY openNTServicesKey();
static HKEY open95ServicesKey();
void serviceMain(DWORD, LPTSTR*);
static void WINAPI serviceMainEntry(DWORD, LPTSTR*);
void serviceHandler(DWORD ctrl);
static void WINAPI serviceHandlerEntry(DWORD ctrl);
static bool serviceLogger(int, const char*);
private:
DaemonFunc m_daemonFunc;
int m_daemonResult;
SERVICE_STATUS_HANDLE m_statusHandle;
CMutex* m_serviceMutex;
CCondVar<DWORD>* m_serviceState;
bool m_serviceHandlerWaiting;
bool m_serviceRunning;
StopFunc m_stop;
static HANDLE s_eventLog;
static CWin32Platform* s_daemonPlatform;
}; };
#endif #endif

View File

@ -7,19 +7,48 @@
class IPlatform : public IInterface { class IPlatform : public IInterface {
public: public:
typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv);
typedef int (*RestartFunc)();
// manipulators // manipulators
// install/uninstall a daemon. // install/uninstall a daemon. commandLine should *not*
// include the name of program as the first argument.
// FIXME -- throw on error? will get better error messages that way. // FIXME -- throw on error? will get better error messages that way.
virtual bool installDaemon(/* FIXME */) = 0; virtual bool installDaemon(const char* name,
virtual bool uninstallDaemon(/* FIXME */) = 0; const char* description,
const char* pathname,
const char* commandLine) = 0;
virtual bool uninstallDaemon(const char* name) = 0;
// daemonize. this should have the side effect of sending log // daemonize. this should have the side effect of sending log
// messages to a system message logger since messages can no // messages to a system message logger since messages can no
// longer go to the console. returns true iff successful. // longer go to the console. returns true iff successful.
// the name is the name of the daemon. // the name is the name of the daemon.
// FIXME -- win32 services will require a more complex interface
virtual bool daemonize(const char* name) = 0; // daemonize. this should have the side effect of sending log
// messages to a system message logger since messages can no
// longer go to the console. name is the name of the daemon.
// once daemonized, func is invoked and daemonize returns when
// and what func does. daemonize() returns -1 on error.
//
// exactly what happens when daemonizing depends on the platform.
// unix:
// detaches from terminal. func gets one argument, the name
// passed to daemonize().
// win32:
// becomes a service. argument 0 is the name of the service
// and the rest are the arguments passed to StartService().
// func is only called when the service is actually started.
// func must behave like a proper ServiceMain() function; in
// particular, it must call RegisterServiceCtrlHandler() and
// SetServiceStatus().
virtual int daemonize(const char* name, DaemonFunc func) = 0;
// continually restart the given function in a separate process
// or thread until it exits normally with a code less than the
// given code then return the code.
virtual int restart(RestartFunc, int minErrorCode) = 0;
// accessors // accessors

139
platform/platform.dsp Normal file
View File

@ -0,0 +1,139 @@
# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=platform - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "platform.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "platform.mak" CFG="platform - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "platform - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "platform - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "platform - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ENDIF
# Begin Target
# Name "platform - Win32 Release"
# Name "platform - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\CMSWindowsClipboard.cpp
# End Source File
# Begin Source File
SOURCE=.\CMSWindowsScreen.cpp
# End Source File
# Begin Source File
SOURCE=.\CPlatform.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\CMSWindowsClipboard.h
# End Source File
# Begin Source File
SOURCE=.\CMSWindowsScreen.h
# End Source File
# Begin Source File
SOURCE=.\CPlatform.h
# End Source File
# Begin Source File
SOURCE=.\CWin32Platform.h
# End Source File
# Begin Source File
SOURCE=.\IPlatform.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# Begin Group "Included Files"
# PROP Default_Filter "inc"
# Begin Source File
SOURCE=.\CWin32Platform.cpp
# PROP Exclude_From_Build 1
# End Source File
# End Group
# End Target
# End Project

View File

@ -1,10 +1,11 @@
#include "CMSWindowsPrimaryScreen.h" #include "CMSWindowsPrimaryScreen.h"
#include "CMSWindowsClipboard.h" #include "CMSWindowsClipboard.h"
#include "CServer.h" #include "CServer.h"
#include "CSynergyHook.h" #include "CPlatform.h"
#include "XScreen.h"
#include "XSynergy.h" #include "XSynergy.h"
#include "CThread.h"
#include "CLog.h" #include "CLog.h"
#include "CThread.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@ -14,38 +15,76 @@
CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() :
m_server(NULL), m_server(NULL),
m_active(false), m_threadID(0),
m_desk(NULL),
m_deskName(),
m_window(NULL), m_window(NULL),
m_nextClipboardWindow(NULL), m_active(false),
m_clipboardOwner(NULL),
m_hookLibrary(NULL),
m_mark(0), m_mark(0),
m_markReceived(0) m_markReceived(0),
m_nextClipboardWindow(NULL),
m_clipboardOwner(NULL)
{ {
// detect operating system // load the hook library
OSVERSIONINFO version; m_hookLibrary = LoadLibrary("synrgyhk");
version.dwOSVersionInfoSize = sizeof(version); if (m_hookLibrary == NULL) {
if (GetVersionEx(&version) == 0) { log((CLOG_ERR "failed to load hook library"));
log((CLOG_WARN "cannot determine OS: %d", GetLastError())); throw XScreenOpenFailure();
} }
m_is95Family = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone");
m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay");
m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install");
m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall");
if (m_setZone == NULL ||
m_setRelay == NULL ||
m_install == NULL ||
m_uninstall == NULL) {
log((CLOG_ERR "invalid hook library"));
FreeLibrary(m_hookLibrary);
throw XScreenOpenFailure();
}
// detect operating system
m_is95Family = CPlatform::isWindows95Family();
// make sure this thread has a message queue
MSG dummy;
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
} }
CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen()
{ {
assert(m_hookLibrary != NULL);
assert(m_window == NULL); assert(m_window == NULL);
assert(m_hookLibrary == NULL);
// done with hook library
FreeLibrary(m_hookLibrary);
} }
void CMSWindowsPrimaryScreen::run() void CMSWindowsPrimaryScreen::run()
{ {
// must call run() from same thread as open()
assert(m_threadID == GetCurrentThreadId());
// change our priority // change our priority
CThread::getCurrentThread().setPriority(-3); CThread::getCurrentThread().setPriority(-3);
// poll input desktop to see if it changes (preTranslateMessage()
// handles WM_TIMER)
UINT timer = 0;
if (!m_is95Family) {
SetTimer(NULL, 0, 200, NULL);
}
// run event loop // run event loop
log((CLOG_INFO "entering event loop")); log((CLOG_INFO "entering event loop"));
doRun(); doRun();
log((CLOG_INFO "exiting event loop")); log((CLOG_INFO "exiting event loop"));
// remove timer
if (!m_is95Family) {
KillTimer(NULL, timer);
}
} }
void CMSWindowsPrimaryScreen::stop() void CMSWindowsPrimaryScreen::stop()
@ -64,16 +103,25 @@ void CMSWindowsPrimaryScreen::open(CServer* server)
// open the display // open the display
openDisplay(); openDisplay();
// get keyboard state // initialize marks
updateKeys(); m_mark = 0;
m_markReceived = 0;
nextMark();
// send screen info // send screen info
SInt32 w, h; SInt32 w, h;
getScreenSize(&w, &h); getScreenSize(&w, &h);
m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); m_server->setInfo(w, h, getJumpZoneSize(), 0, 0);
// compute center pixel of screen
m_xCenter = w >> 1;
m_yCenter = h >> 1;
// get keyboard state
updateKeys();
// enter the screen // enter the screen
doEnter(); enterNoWarp();
} }
void CMSWindowsPrimaryScreen::close() void CMSWindowsPrimaryScreen::close()
@ -92,115 +140,48 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
log((CLOG_INFO "entering primary at %d,%d", x, y)); log((CLOG_INFO "entering primary at %d,%d", x, y));
assert(m_active == true); assert(m_active == true);
doEnter(); // enter the screen
enterNoWarp();
// warp to requested location // warp to requested location
warpCursor(x, y); warpCursor(x, y);
} }
void CMSWindowsPrimaryScreen::doEnter()
{
// not active anymore
m_active = false;
// release keyboard/mouse and set the zones that should cause a jump
/* FIXME
if (UnregisterHotKey(m_window, 0x0001) != 0) {
log((CLOG_INFO "released hot key"));
}
else {
log((CLOG_INFO "failed to release hot key: %d", GetLastError()));
}
*/
if (m_is95Family) {
DWORD dummy = 0;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0);
}
SInt32 w, h;
getScreenSize(&w, &h);
SetZoneFunc setZone = (SetZoneFunc)GetProcAddress(
m_hookLibrary, "setZone");
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
// restore the active window and hide our window. we can only set
// the active window for another thread if we first attach our input
// to that thread.
ReleaseCapture();
if (m_lastActiveWindow != NULL) {
DWORD myThread = GetCurrentThreadId();
if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) {
// FIXME -- shouldn't raise window if X-Mouse is enabled
// but i have no idea how to do that or check if enabled.
SetActiveWindow(m_lastActiveWindow);
AttachThreadInput(myThread, m_lastActiveThread, FALSE);
}
}
ShowWindow(m_window, SW_HIDE);
// all messages prior to now are invalid
nextMark();
}
bool CMSWindowsPrimaryScreen::leave() bool CMSWindowsPrimaryScreen::leave()
{ {
log((CLOG_INFO "leaving primary")); log((CLOG_INFO "leaving primary"));
assert(m_active == false); assert(m_active == false);
// do non-warp enter stuff
// get state of keys as we leave
updateKeys();
// all messages prior to now are invalid // all messages prior to now are invalid
nextMark(); nextMark();
// remember the active window before we leave. GetActiveWindow() // save active window, show ours, and grab mouse/keyboard
// will only return the active window for the thread's queue (i.e. if (!onLeave()) {
// our app) but we need the globally active window. get that by return false;
// attaching input to the foreground window's thread then calling
// GetActiveWindow() and then detaching our input.
m_lastActiveWindow = NULL;
m_lastForegroundWindow = GetForegroundWindow();
m_lastActiveThread = GetWindowThreadProcessId(
m_lastForegroundWindow, NULL);
if (m_lastActiveThread != 0) {
DWORD myThread = GetCurrentThreadId();
if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) {
m_lastActiveWindow = GetActiveWindow();
AttachThreadInput(myThread, m_lastActiveThread, FALSE);
} }
}
// show our window
ShowWindow(m_window, SW_SHOW);
// relay all mouse and keyboard events // relay all mouse and keyboard events
SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( m_setRelay();
m_hookLibrary, "setRelay");
setRelay(); // get state of keys as we leave
updateKeys();
// warp mouse to center of screen
warpCursor(m_xCenter, m_yCenter);
// ignore this many mouse motion events (not including the already
// queued events). on (at least) the win2k login desktop, one
// motion event is reported using a position from before the above
// warpCursor(). i don't know why it does that and other desktops
// don't have the same problem. anyway, simply ignoring that event
// works around it.
m_mouseMoveIgnore = 1;
// disable ctrl+alt+del, alt+tab, etc
if (m_is95Family) { if (m_is95Family) {
// disable ctrl+alt+del, alt+tab, ctrl+esc
DWORD dummy = 0; DWORD dummy = 0;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0);
} }
/* FIXME
if (RegisterHotKey(m_window, 0x0001, MOD_ALT, VK_TAB) != 0) {
log((CLOG_INFO "got hot key"));
}
else {
log((CLOG_INFO "failed to get hot key: %d", GetLastError()));
}
*/
// get keyboard input and capture mouse
SetActiveWindow(m_window);
SetFocus(m_window);
SetCapture(m_window);
// warp the mouse to the center of the screen
getScreenSize(&m_xCenter, &m_yCenter);
m_xCenter >>= 1;
m_yCenter >>= 1;
warpCursor(m_xCenter, m_yCenter);
// discard messages until after the warp // discard messages until after the warp
nextMark(); nextMark();
@ -238,12 +219,11 @@ log((CLOG_INFO "failed to get hot key: %d", GetLastError()));
void CMSWindowsPrimaryScreen::onConfigure() void CMSWindowsPrimaryScreen::onConfigure()
{ {
if (!m_active) { if ((m_is95Family || m_desk != NULL) && !m_active) {
SInt32 w, h; SInt32 w, h;
getScreenSize(&w, &h); getScreenSize(&w, &h);
SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( m_setZone(m_server->getActivePrimarySides(),
m_hookLibrary, "setZone"); w, h, getJumpZoneSize());
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
} }
} }
@ -254,7 +234,7 @@ void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
} }
void CMSWindowsPrimaryScreen::setClipboard( void CMSWindowsPrimaryScreen::setClipboard(
ClipboardID id, const IClipboard* src) ClipboardID /*id*/, const IClipboard* src)
{ {
assert(m_window != NULL); assert(m_window != NULL);
@ -262,7 +242,8 @@ void CMSWindowsPrimaryScreen::setClipboard(
CClipboard::copy(&dst, src); CClipboard::copy(&dst, src);
} }
void CMSWindowsPrimaryScreen::grabClipboard(ClipboardID id) void CMSWindowsPrimaryScreen::grabClipboard(
ClipboardID /*id*/)
{ {
assert(m_window != NULL); assert(m_window != NULL);
@ -284,7 +265,7 @@ SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const
} }
void CMSWindowsPrimaryScreen::getClipboard( void CMSWindowsPrimaryScreen::getClipboard(
ClipboardID id, IClipboard* dst) const ClipboardID /*id*/, IClipboard* dst) const
{ {
assert(m_window != NULL); assert(m_window != NULL);
@ -332,80 +313,37 @@ void CMSWindowsPrimaryScreen::onOpenDisplay()
assert(m_window == NULL); assert(m_window == NULL);
assert(m_server != NULL); assert(m_server != NULL);
// initialize clipboard owner to current owner. we don't want // save thread id. we'll need to pass this to the hook library.
// to take ownership of the clipboard just by starting up. m_threadID = GetCurrentThreadId();
m_clipboardOwner = GetClipboardOwner();
// get screen size // get the input desktop and switch to it
// note -- we use a fullscreen window to grab input. it should if (m_is95Family) {
// be possible to use a 1x1 window but i've run into problems if (!openDesktop()) {
// with losing keyboard input (focus?) in that case. throw XScreenOpenFailure();
// unfortunately, hiding the full screen window causes all other
// windows to redraw.
SInt32 w, h;
getScreenSize(&w, &h);
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, w, h, NULL, NULL,
getInstance(),
NULL);
assert(m_window != NULL);
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
// load the hook library
bool hooked = false;
m_hookLibrary = LoadLibrary("synrgyhk");
if (m_hookLibrary != NULL) {
// install input hooks
InstallFunc install = (InstallFunc)GetProcAddress(
m_hookLibrary, "install");
if (install != NULL) {
hooked = (install(m_window) != 0);
} }
} }
if (!hooked) { else {
log((CLOG_ERR "failed to install hooks")); if (!switchDesktop(openInputDesktop())) {
ChangeClipboardChain(m_window, m_nextClipboardWindow); throw XScreenOpenFailure();
m_nextClipboardWindow = NULL; }
DestroyWindow(m_window);
m_window = NULL;
// FIXME -- throw
} }
// initialize marks
m_mark = 0;
m_markReceived = 0;
nextMark();
} }
void CMSWindowsPrimaryScreen::onCloseDisplay() void CMSWindowsPrimaryScreen::onCloseDisplay()
{ {
assert(m_window != NULL); // disconnect from desktop
if (m_is95Family) {
// uninstall input hooks closeDesktop();
UninstallFunc uninstall = (UninstallFunc)GetProcAddress( }
m_hookLibrary, "uninstall"); else {
if (uninstall != NULL) { switchDesktop(NULL);
uninstall();
} }
// done with hook library // clear thread id
FreeLibrary(m_hookLibrary); m_threadID = 0;
m_hookLibrary = NULL;
// remove clipboard snooper assert(m_window == NULL);
ChangeClipboardChain(m_window, m_nextClipboardWindow); assert(m_desk == NULL);
m_nextClipboardWindow = NULL;
// destroy window
DestroyWindow(m_window);
m_window = NULL;
} }
bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
@ -417,8 +355,8 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
return true; return true;
case SYNERGY_MSG_KEY: case SYNERGY_MSG_KEY:
// ignore if not at current mark // ignore message if posted prior to last mark change
if (m_mark == m_markReceived) { if (m_markReceived == m_mark) {
KeyModifierMask mask; KeyModifierMask mask;
const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); const KeyID key = mapKey(msg->wParam, msg->lParam, &mask);
if (key != kKeyNone) { if (key != kKeyNone) {
@ -453,8 +391,8 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
return true; return true;
case SYNERGY_MSG_MOUSE_BUTTON: case SYNERGY_MSG_MOUSE_BUTTON:
// ignore if not at current mark // ignore message if posted prior to last mark change
if (m_mark == m_markReceived) { if (m_markReceived == m_mark) {
const ButtonID button = mapButton(msg->wParam); const ButtonID button = mapButton(msg->wParam);
switch (msg->wParam) { switch (msg->wParam) {
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
@ -479,20 +417,19 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
return true; return true;
case SYNERGY_MSG_MOUSE_WHEEL: case SYNERGY_MSG_MOUSE_WHEEL:
// ignore if not at current mark // ignore message if posted prior to last mark change
if (m_mark == m_markReceived) { if (m_markReceived == m_mark) {
log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam));
m_server->onMouseWheel(msg->wParam); m_server->onMouseWheel(msg->wParam);
} }
return true; return true;
case SYNERGY_MSG_MOUSE_MOVE: case SYNERGY_MSG_MOUSE_MOVE:
// ignore if not at current mark // ignore message if posted prior to last mark change
if (m_mark == m_markReceived) { if (m_markReceived == m_mark) {
SInt32 x = (SInt32)msg->wParam; SInt32 x = static_cast<SInt32>(msg->wParam);
SInt32 y = (SInt32)msg->lParam; SInt32 y = static_cast<SInt32>(msg->lParam);
if (!m_active) { if (!m_active) {
log((CLOG_DEBUG2 "event: inactive move %d,%d", x, y));
m_server->onMouseMovePrimary(x, y); m_server->onMouseMovePrimary(x, y);
} }
else { else {
@ -500,10 +437,10 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
x -= m_xCenter; x -= m_xCenter;
y -= m_yCenter; y -= m_yCenter;
// ignore if the mouse didn't move // ignore if the mouse didn't move or we're ignoring
// motion.
if (m_mouseMoveIgnore == 0) {
if (x != 0 && y != 0) { if (x != 0 && y != 0) {
log((CLOG_DEBUG2 "event: active move %d,%d", x, y));
// warp mouse back to center // warp mouse back to center
warpCursor(m_xCenter, m_yCenter); warpCursor(m_xCenter, m_yCenter);
@ -511,6 +448,26 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
m_server->onMouseMoveSecondary(x, y); m_server->onMouseMoveSecondary(x, y);
} }
} }
else {
// ignored one more motion event
--m_mouseMoveIgnore;
}
}
}
return true;
case WM_TIMER:
// if current desktop is not the input desktop then switch to it
if (!m_is95Family) {
HDESK desk = openInputDesktop();
if (desk != NULL) {
if (isCurrentDesktop(desk)) {
CloseDesktop(desk);
}
else {
switchDesktop(desk);
}
}
} }
return true; return true;
} }
@ -523,11 +480,20 @@ LRESULT CMSWindowsPrimaryScreen::onEvent(
WPARAM wParam, LPARAM lParam) WPARAM wParam, LPARAM lParam)
{ {
switch (msg) { switch (msg) {
/* case WM_QUERYENDSESSION:
case WM_HOTKEY: if (m_is95Family) {
log((CLOG_INFO "hot key: %d, %d, %s %s %s", wParam, HIWORD(lParam), (LOWORD(lParam) & MOD_ALT) ? "ALT" : "", (LOWORD(lParam) & MOD_CONTROL) ? "CTRL" : "", (LOWORD(lParam) & MOD_SHIFT) ? "SHIFT" : "", (LOWORD(lParam) & MOD_WIN) ? "WIN" : "")); return TRUE;
return 0; }
*/ break;
case WM_ENDSESSION:
if (m_is95Family) {
if (wParam == TRUE && lParam == 0) {
stop();
}
return 0;
}
break;
case WM_PAINT: case WM_PAINT:
ValidateRect(hwnd, NULL); ValidateRect(hwnd, NULL);
@ -537,7 +503,9 @@ return 0;
log((CLOG_DEBUG "clipboard was taken")); log((CLOG_DEBUG "clipboard was taken"));
// first pass it on // first pass it on
if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow, msg, wParam, lParam); SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
}
// now notify server that somebody changed the clipboard. // now notify server that somebody changed the clipboard.
// skip that if we're the new owner. // skip that if we're the new owner.
@ -555,18 +523,25 @@ return 0;
return 0; return 0;
case WM_CHANGECBCHAIN: case WM_CHANGECBCHAIN:
if (m_nextClipboardWindow == (HWND)wParam) if (m_nextClipboardWindow == (HWND)wParam) {
m_nextClipboardWindow = (HWND)lParam; m_nextClipboardWindow = (HWND)lParam;
else }
else if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow, msg, wParam, lParam); SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
}
return 0; return 0;
case WM_DISPLAYCHANGE: { case WM_DISPLAYCHANGE:
// screen resolution has changed {
// screen resolution may have changed
SInt32 wOld, hOld;
getScreenSize(&wOld, &hOld);
SInt32 w, h; SInt32 w, h;
updateScreenSize(); updateScreenSize();
getScreenSize(&w, &h); getScreenSize(&w, &h);
// do nothing if resolution hasn't changed
if (w != wOld || h != hOld) {
// recompute center pixel of screen // recompute center pixel of screen
m_xCenter = w >> 1; m_xCenter = w >> 1;
m_yCenter = h >> 1; m_yCenter = h >> 1;
@ -578,15 +553,14 @@ return 0;
// tell hook about resize if not active // tell hook about resize if not active
else { else {
SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( onConfigure();
m_hookLibrary, "setZone");
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
} }
// send new screen info // send new screen info
POINT pos; POINT pos;
GetCursorPos(&pos); GetCursorPos(&pos);
m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y);
}
return 0; return 0;
} }
@ -595,11 +569,256 @@ return 0;
return DefWindowProc(hwnd, msg, wParam, lParam); return DefWindowProc(hwnd, msg, wParam, lParam);
} }
void CMSWindowsPrimaryScreen::enterNoWarp()
{
// not active anymore
m_active = false;
// reset motion ignore count
m_mouseMoveIgnore = 0;
// enable ctrl+alt+del, alt+tab, etc
if (m_is95Family) {
DWORD dummy = 0;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0);
}
// install jump zones
onConfigure();
// restore active window and hide our window
onEnter();
// all messages prior to now are invalid
nextMark();
}
void CMSWindowsPrimaryScreen::onEnter()
{
// restore the active window and hide our window. we can only set
// the active window for another thread if we first attach our input
// to that thread.
ReleaseCapture();
if (m_lastActiveWindow != NULL) {
DWORD myThread = GetCurrentThreadId();
if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) {
// FIXME -- shouldn't raise window if X-Mouse is enabled
// but i have no idea how to do that or check if enabled.
SetActiveWindow(m_lastActiveWindow);
AttachThreadInput(myThread, m_lastActiveThread, FALSE);
}
}
ShowWindow(m_window, SW_HIDE);
}
bool CMSWindowsPrimaryScreen::onLeave()
{
// remember the active window before we leave. GetActiveWindow()
// will only return the active window for the thread's queue (i.e.
// our app) but we need the globally active window. get that by
// attaching input to the foreground window's thread then calling
// GetActiveWindow() and then detaching our input.
m_lastActiveWindow = NULL;
m_lastForegroundWindow = GetForegroundWindow();
m_lastActiveThread = GetWindowThreadProcessId(
m_lastForegroundWindow, NULL);
if (m_lastActiveThread != 0) {
DWORD myThread = GetCurrentThreadId();
if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) {
m_lastActiveWindow = GetActiveWindow();
AttachThreadInput(myThread, m_lastActiveThread, FALSE);
}
}
// show our window
ShowWindow(m_window, SW_SHOW);
// get keyboard input and capture mouse
SetActiveWindow(m_window);
SetFocus(m_window);
SetCapture(m_window);
return true;
}
void CMSWindowsPrimaryScreen::nextMark() void CMSWindowsPrimaryScreen::nextMark()
{ {
assert(m_window != NULL); // next mark
++m_mark;
PostMessage(m_window, SYNERGY_MSG_MARK, ++m_mark, 0); // mark point in message queue where the mark was changed
PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0);
}
bool CMSWindowsPrimaryScreen::openDesktop()
{
// install hooks
m_install(m_threadID);
// note -- we use a fullscreen window to grab input. it should
// be possible to use a 1x1 window but i've run into problems
// with losing keyboard input (focus?) in that case.
// unfortunately, hiding the full screen window (when entering
// the scren causes all other windows to redraw).
SInt32 w, h;
getScreenSize(&w, &h);
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT |
WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(),
"Synergy",
WS_POPUP,
0, 0, w, h, NULL, NULL,
getInstance(),
NULL);
if (m_window == NULL) {
log((CLOG_ERR "failed to create window: %d", GetLastError()));
m_uninstall();
return false;
}
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
return true;
}
void CMSWindowsPrimaryScreen::closeDesktop()
{
// destroy old window
if (m_window != NULL) {
// restore active window and hide ours
if (m_active) {
onEnter();
}
// first remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// we no longer own the clipboard
if (m_clipboardOwner == m_window) {
m_clipboardOwner = NULL;
}
// now destroy window
DestroyWindow(m_window);
m_window = NULL;
}
// unhook
m_uninstall();
}
bool CMSWindowsPrimaryScreen::switchDesktop(HDESK desk)
{
// did we own the clipboard?
bool ownClipboard = (m_clipboardOwner == m_window);
// destroy old window
if (m_window != NULL) {
// restore active window and hide ours
if (m_active) {
onEnter();
}
// first remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// we no longer own the clipboard
if (ownClipboard) {
m_clipboardOwner = NULL;
}
// now destroy window
DestroyWindow(m_window);
m_window = NULL;
}
// unhook
if (m_desk != NULL) {
m_uninstall();
CloseDesktop(m_desk);
m_desk = NULL;
m_deskName = "";
}
// if no new desktop then we're done
if (desk == NULL) {
log((CLOG_INFO "disconnecting desktop"));
return true;
}
// set the desktop. can only do this when there are no windows
// and hooks on the current desktop owned by this thread.
if (SetThreadDesktop(desk) == 0) {
log((CLOG_ERR "failed to set desktop: %d", GetLastError()));
CloseDesktop(desk);
return false;
}
// install hooks
m_install(m_threadID);
// note -- we use a fullscreen window to grab input. it should
// be possible to use a 1x1 window but i've run into problems
// with losing keyboard input (focus?) in that case.
// unfortunately, hiding the full screen window (when entering
// the scren causes all other windows to redraw).
SInt32 w, h;
getScreenSize(&w, &h);
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT |
WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(),
"Synergy",
WS_POPUP,
0, 0, w, h, NULL, NULL,
getInstance(),
NULL);
if (m_window == NULL) {
log((CLOG_ERR "failed to create window: %d", GetLastError()));
m_uninstall();
CloseDesktop(desk);
return false;
}
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
// reassert clipboard ownership
if (ownClipboard) {
// FIXME
}
// save new desktop
m_desk = desk;
m_deskName = getDesktopName(m_desk);
log((CLOG_INFO "switched to desktop %s", m_deskName.c_str()));
// get active window and show ours
if (m_active) {
onLeave();
}
else {
// set jump zones
onConfigure();
// all messages prior to now are invalid
nextMark();
}
return true;
}
CString CMSWindowsPrimaryScreen::getCurrentDesktopName() const
{
return m_deskName;
} }
static const KeyID g_virtualKey[] = static const KeyID g_virtualKey[] =

View File

@ -1,10 +1,11 @@
#ifndef CMSWINDOWSPRIMARYSCREEN_H #ifndef CMSWINDOWSPRIMARYSCREEN_H
#define CMSWINDOWSPRIMARYSCREEN_H #define CMSWINDOWSPRIMARYSCREEN_H
#include "KeyTypes.h"
#include "MouseTypes.h"
#include "CMSWindowsScreen.h" #include "CMSWindowsScreen.h"
#include "IPrimaryScreen.h" #include "IPrimaryScreen.h"
#include "MouseTypes.h"
#include "CString.h"
#include "CSynergyHook.h"
class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen {
public: public:
@ -36,12 +37,24 @@ protected:
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
virtual void onOpenDisplay(); virtual void onOpenDisplay();
virtual void onCloseDisplay(); virtual void onCloseDisplay();
virtual CString getCurrentDesktopName() const;
private: private:
void doEnter(); void enterNoWarp();
void onEnter();
bool onLeave();
// discard posted messages
void nextMark(); void nextMark();
// open/close desktop (for windows 95/98/me)
bool openDesktop();
void closeDesktop();
// make desk the thread desktop (for windows NT/2000/XP)
bool switchDesktop(HDESK desk);
// key and button queries
KeyID mapKey(WPARAM keycode, LPARAM info, KeyID mapKey(WPARAM keycode, LPARAM info,
KeyModifierMask* maskOut); KeyModifierMask* maskOut);
ButtonID mapButton(WPARAM button) const; ButtonID mapButton(WPARAM button) const;
@ -50,19 +63,51 @@ private:
private: private:
CServer* m_server; CServer* m_server;
// true if windows 95/98/me
bool m_is95Family; bool m_is95Family;
bool m_active;
// the main loop's thread id
DWORD m_threadID;
// the current desk and it's name
HDESK m_desk;
CString m_deskName;
// our window (for getting clipboard changes)
HWND m_window; HWND m_window;
// m_active is true the hooks are relaying events
bool m_active;
// used to discard queued messages that are no longer needed
UInt32 m_mark;
UInt32 m_markReceived;
// clipboard stuff
HWND m_nextClipboardWindow; HWND m_nextClipboardWindow;
HWND m_clipboardOwner; HWND m_clipboardOwner;
// map of key state
BYTE m_keys[256];
// position of center pixel of screen
SInt32 m_xCenter, m_yCenter;
// used to ignore mouse motion
SInt32 m_mouseMoveIgnore;
// hook library stuff
HINSTANCE m_hookLibrary;
InstallFunc m_install;
UninstallFunc m_uninstall;
SetZoneFunc m_setZone;
SetRelayFunc m_setRelay;
// stuff for restoring active window
HWND m_lastForegroundWindow; HWND m_lastForegroundWindow;
HWND m_lastActiveWindow; HWND m_lastActiveWindow;
DWORD m_lastActiveThread; DWORD m_lastActiveThread;
HINSTANCE m_hookLibrary;
UInt32 m_mark;
UInt32 m_markReceived;
BYTE m_keys[256];
SInt32 m_xCenter, m_yCenter;
}; };
#endif #endif

View File

@ -298,7 +298,7 @@ void CServer::setInfoNoLock(const CString& screen,
info->m_width = w; info->m_width = w;
info->m_height = h; info->m_height = h;
info->m_zoneSize = zoneSize; info->m_zoneSize = zoneSize;
log((CLOG_NOTE "screen \"%s\" size=%dx%d zone=%d pos=%d,%d", screen.c_str(), w, h, zoneSize, x, y)); log((CLOG_INFO "screen \"%s\" size=%dx%d zone=%d pos=%d,%d", screen.c_str(), w, h, zoneSize, x, y));
// send acknowledgement (if screen isn't the primary) // send acknowledgement (if screen isn't the primary)
if (info->m_protocol != NULL) { if (info->m_protocol != NULL) {
@ -353,7 +353,7 @@ void CServer::grabClipboardNoLock(
} }
// mark screen as owning clipboard // mark screen as owning clipboard
log((CLOG_NOTE "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str()));
clipboard.m_clipboardOwner = screen; clipboard.m_clipboardOwner = screen;
clipboard.m_clipboardSeqNum = seqNum; clipboard.m_clipboardSeqNum = seqNum;
@ -404,7 +404,7 @@ void CServer::setClipboard(ClipboardID id,
} }
// unmarshall into our clipboard buffer // unmarshall into our clipboard buffer
log((CLOG_NOTE "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); log((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id));
clipboard.m_clipboardReady = true; clipboard.m_clipboardReady = true;
clipboard.m_clipboardData = data; clipboard.m_clipboardData = data;
clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0);
@ -603,17 +603,25 @@ void CServer::onMouseMoveSecondaryNoLock(
if (!isLockedToScreenNoLock()) { if (!isLockedToScreenNoLock()) {
// find direction of neighbor // find direction of neighbor
CConfig::EDirection dir; CConfig::EDirection dir;
if (m_x < 0) if (m_x < 0) {
dir = CConfig::kLeft; dir = CConfig::kLeft;
else if (m_x > m_active->m_width - 1) }
else if (m_x > m_active->m_width - 1) {
dir = CConfig::kRight; dir = CConfig::kRight;
else if (m_y < 0) }
else if (m_y < 0) {
dir = CConfig::kTop; dir = CConfig::kTop;
else if (m_y > m_active->m_height - 1) }
else if (m_y > m_active->m_height - 1) {
dir = CConfig::kBottom; dir = CConfig::kBottom;
else }
else {
newScreen = m_active; newScreen = m_active;
// keep compiler quiet about unset variable
dir = CConfig::kLeft;
}
// get neighbor if we should switch // get neighbor if we should switch
if (newScreen == NULL) { if (newScreen == NULL) {
log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CConfig::dirName(dir))); log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CConfig::dirName(dir)));
@ -709,7 +717,7 @@ void CServer::switchScreen(CScreenInfo* dst,
assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height); assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height);
assert(m_active != NULL); assert(m_active != NULL);
log((CLOG_NOTE "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y));
// FIXME -- we're not locked here but we probably should be // FIXME -- we're not locked here but we probably should be
// record new position // record new position
@ -1485,7 +1493,7 @@ void CServer::removeConnection(const CString& name)
m_y = m_primaryInfo->m_height >> 1; m_y = m_primaryInfo->m_height >> 1;
// don't notify active screen since it probably already disconnected // don't notify active screen since it probably already disconnected
log((CLOG_NOTE "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y));
// cut over // cut over
m_active = m_primaryInfo; m_active = m_primaryInfo;

View File

@ -37,7 +37,7 @@ static HINSTANCE g_hinstance = NULL;
static DWORD g_process = NULL; static DWORD g_process = NULL;
static EWheelSupport g_wheelSupport = kWheelNone; static EWheelSupport g_wheelSupport = kWheelNone;
static UINT g_wmMouseWheel = 0; static UINT g_wmMouseWheel = 0;
static HWND g_hwnd = NULL; static DWORD g_threadID = 0;
static HHOOK g_keyboard = NULL; static HHOOK g_keyboard = NULL;
static HHOOK g_mouse = NULL; static HHOOK g_mouse = NULL;
static HHOOK g_cbt = NULL; static HHOOK g_cbt = NULL;
@ -54,8 +54,6 @@ static SInt32 g_hScreen = 0;
static HCURSOR g_cursor = NULL; static HCURSOR g_cursor = NULL;
static DWORD g_cursorThread = 0; static DWORD g_cursorThread = 0;
static int foo = 0;
#pragma data_seg() #pragma data_seg()
// //
@ -91,16 +89,7 @@ static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam)
if (code >= 0) { if (code >= 0) {
if (g_relay) { if (g_relay) {
// forward message to our window // forward message to our window
PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam);
/* XXX -- this doesn't seem to work/help with lost key events
// if the active window isn't our window then make it
// active.
const bool wrongFocus = (GetActiveWindow() != g_hwnd);
if (wrongFocus) {
SetForegroundWindow(g_hwnd);
}
*/
// let certain keys pass through // let certain keys pass through
switch (wParam) { switch (wParam) {
@ -133,10 +122,12 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam)
case WM_LBUTTONUP: case WM_LBUTTONUP:
case WM_MBUTTONUP: case WM_MBUTTONUP:
case WM_RBUTTONUP: case WM_RBUTTONUP:
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); PostThreadMessage(g_threadID,
SYNERGY_MSG_MOUSE_BUTTON, wParam, 0);
return 1; return 1;
case WM_MOUSEWHEEL: { case WM_MOUSEWHEEL:
{
// win2k and other systems supporting WM_MOUSEWHEEL in // win2k and other systems supporting WM_MOUSEWHEEL in
// the mouse hook are gratuitously different (and poorly // the mouse hook are gratuitously different (and poorly
// documented). // documented).
@ -144,27 +135,31 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam)
case kWheelModern: { case kWheelModern: {
const MOUSEHOOKSTRUCT* info = const MOUSEHOOKSTRUCT* info =
(const MOUSEHOOKSTRUCT*)lParam; (const MOUSEHOOKSTRUCT*)lParam;
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL,
static_cast<short>(LOWORD(info->dwExtraInfo)), 0); static_cast<short>(
LOWORD(info->dwExtraInfo)), 0);
break; break;
} }
case kWheelWin2000: { case kWheelWin2000: {
const MOUSEHOOKSTRUCTWin2000* info = const MOUSEHOOKSTRUCTWin2000* info =
(const MOUSEHOOKSTRUCTWin2000*)lParam; (const MOUSEHOOKSTRUCTWin2000*)lParam;
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL,
static_cast<short>(HIWORD(info->mouseData)), 0); static_cast<short>(
HIWORD(info->mouseData)), 0);
break; break;
} }
default: default:
break; break;
} }
return 1;
} }
return 1;
case WM_MOUSEMOVE: { case WM_MOUSEMOVE:
const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; {
const MOUSEHOOKSTRUCT* info =
(const MOUSEHOOKSTRUCT*)lParam;
SInt32 x = (SInt32)info->pt.x; SInt32 x = (SInt32)info->pt.x;
SInt32 y = (SInt32)info->pt.y; SInt32 y = (SInt32)info->pt.y;
@ -182,9 +177,9 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam)
} }
// relay the motion // relay the motion
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return 1;
} }
return 1;
} }
} }
else { else {
@ -209,7 +204,7 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam)
// if inside then eat event and notify our window // if inside then eat event and notify our window
if (inside) { if (inside) {
restoreCursor(); restoreCursor();
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return 1; return 1;
} }
} }
@ -222,15 +217,7 @@ static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam)
{ {
if (code >= 0) { if (code >= 0) {
if (g_relay) { if (g_relay) {
switch (code) { // do nothing for now. may add something later.
case HCBT_ACTIVATE:
case HCBT_SETFOCUS:
// discard unless activating our window
if (reinterpret_cast<HWND>(wParam) != g_hwnd) {
return 1;
}
break;
}
} }
} }
@ -244,7 +231,8 @@ static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam)
MSG* msg = reinterpret_cast<MSG*>(lParam); MSG* msg = reinterpret_cast<MSG*>(lParam);
if (msg->message == g_wmMouseWheel) { if (msg->message == g_wmMouseWheel) {
// post message to our window // post message to our window
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0); PostThreadMessage(g_threadID,
SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0);
// zero out the delta in the message so it's (hopefully) // zero out the delta in the message so it's (hopefully)
// ignored // ignored
@ -296,7 +284,8 @@ static LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam)
// FIXME -- bit 30 should be set if key was already down // FIXME -- bit 30 should be set if key was already down
// forward message to our window // forward message to our window
PostMessage(g_hwnd, SYNERGY_MSG_KEY, info->vkCode, lParam); PostThreadMessage(g_threadID,
SYNERGY_MSG_KEY, info->vkCode, lParam);
// discard event // discard event
return 1; return 1;
@ -436,16 +425,18 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
extern "C" { extern "C" {
int install(HWND hwnd) int install(DWORD threadID)
{ {
assert(g_threadID == 0);
assert(g_hinstance != NULL); assert(g_hinstance != NULL);
assert(g_keyboard == NULL); assert(g_keyboard == NULL);
assert(g_mouse == NULL); assert(g_mouse == NULL);
assert(g_cbt == NULL); assert(g_cbt == NULL);
assert(g_wheelSupport != kWheelOld || g_getMessage == NULL); assert(g_wheelSupport != kWheelOld || g_getMessage == NULL);
// save window // save thread id. we'll post messages to this thread's
g_hwnd = hwnd; // message queue.
g_threadID = threadID;
// set defaults // set defaults
g_relay = false; g_relay = false;
@ -465,7 +456,7 @@ int install(HWND hwnd)
g_hinstance, g_hinstance,
0); 0);
if (g_keyboard == NULL) { if (g_keyboard == NULL) {
g_hwnd = NULL; g_threadID = NULL;
return 0; return 0;
} }
@ -478,7 +469,7 @@ int install(HWND hwnd)
// uninstall keyboard hook before failing // uninstall keyboard hook before failing
UnhookWindowsHookEx(g_keyboard); UnhookWindowsHookEx(g_keyboard);
g_keyboard = NULL; g_keyboard = NULL;
g_hwnd = NULL; g_threadID = NULL;
return 0; return 0;
} }
@ -493,7 +484,7 @@ int install(HWND hwnd)
UnhookWindowsHookEx(g_mouse); UnhookWindowsHookEx(g_mouse);
g_keyboard = NULL; g_keyboard = NULL;
g_mouse = NULL; g_mouse = NULL;
g_hwnd = NULL; g_threadID = NULL;
return 0; return 0;
} }
@ -563,7 +554,7 @@ int uninstall(void)
g_mouse = NULL; g_mouse = NULL;
g_cbt = NULL; g_cbt = NULL;
g_getMessage = NULL; g_getMessage = NULL;
g_hwnd = NULL; g_threadID = 0;
// show the cursor // show the cursor
restoreCursor(); restoreCursor();

View File

@ -20,14 +20,14 @@
#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y #define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y
#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0015 // delta; <unused> #define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0015 // delta; <unused>
typedef int (*InstallFunc)(HWND); extern "C" {
typedef int (*InstallFunc)(DWORD targetQueueThreadID);
typedef int (*UninstallFunc)(void); typedef int (*UninstallFunc)(void);
typedef void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32); typedef void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32);
typedef void (*SetRelayFunc)(void); typedef void (*SetRelayFunc)(void);
extern "C" { CSYNERGYHOOK_API int install(DWORD);
CSYNERGYHOOK_API int install(HWND);
CSYNERGYHOOK_API int uninstall(void); CSYNERGYHOOK_API int uninstall(void);
CSYNERGYHOOK_API void setZone(UInt32 sides, CSYNERGYHOOK_API void setZone(UInt32 sides,
SInt32 w, SInt32 h, SInt32 jumpZoneSize); SInt32 w, SInt32 h, SInt32 jumpZoneSize);

View File

@ -23,8 +23,8 @@ CFG=makehook - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
MTL=midl.exe MTL=midl.exe
!IF "$(CFG)" == "makehook - Win32 Release" !IF "$(CFG)" == "makehook - Win32 Release"

View File

@ -1,6 +1,7 @@
#include "CServer.h" #include "CServer.h"
#include "CConfig.h" #include "CConfig.h"
#include "CLog.h" #include "CLog.h"
#include "CLock.h"
#include "CMutex.h" #include "CMutex.h"
#include "CNetwork.h" #include "CNetwork.h"
#include "CPlatform.h" #include "CPlatform.h"
@ -11,15 +12,20 @@
#include "stdfstream.h" #include "stdfstream.h"
#include <assert.h> #include <assert.h>
// platform dependent name of a daemon
#if defined(CONFIG_PLATFORM_WIN32)
#define DAEMON "service"
#define DAEMON_NAME "Synergy Server"
#elif defined(CONFIG_PLATFORM_UNIX)
#define DAEMON "daemon"
#define DAEMON_NAME "synergyd"
#endif
// configuration file name // configuration file name
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
#define CONFIG_NAME "synergy.sgc" #define CONFIG_NAME "synergy.sgc"
#define CONFIG_USER_DIR "%HOME%/"
#define CONFIG_SYS_DIR ""
#elif defined(CONFIG_PLATFORM_UNIX) #elif defined(CONFIG_PLATFORM_UNIX)
#define CONFIG_NAME "synergy.conf" #define CONFIG_NAME "synergy.conf"
#define CONFIG_USER_DIR "~/"
#define CONFIG_SYS_DIR "/etc/"
#endif #endif
// //
@ -29,6 +35,8 @@
static const char* pname = NULL; static const char* pname = NULL;
static bool s_restartable = true; static bool s_restartable = true;
static bool s_daemon = true; static bool s_daemon = true;
static bool s_install = false;
static bool s_uninstall = false;
static const char* s_configFile = NULL; static const char* s_configFile = NULL;
static const char* s_logFilter = NULL; static const char* s_logFilter = NULL;
static CConfig s_config; static CConfig s_config;
@ -54,11 +62,16 @@ static void logLock(bool lock)
// //
// main // platform independent main
// //
void realMain() static CServer* s_server = NULL;
static int realMain(CMutex* mutex)
{ {
// s_serverLock should have mutex locked on entry
try {
// initialize threading library // initialize threading library
CThread::init(); CThread::init();
@ -67,7 +80,7 @@ void realMain()
s_logMutex = &logMutex; s_logMutex = &logMutex;
CLog::setLock(&logLock); CLog::setLock(&logLock);
CServer* server = NULL; bool locked = true;
try { try {
// initialize network library // initialize network library
CNetwork::init(); CNetwork::init();
@ -78,24 +91,69 @@ void realMain()
s_config.addScreen("primary"); s_config.addScreen("primary");
} }
// run server // create server
server = new CServer(); s_server = new CServer();
server->setConfig(s_config);
server->run(); // run server (unlocked)
if (mutex != NULL) {
mutex->unlock();
}
locked = false;
s_server->setConfig(s_config);
s_server->run();
locked = true;
if (mutex != NULL) {
mutex->lock();
}
// clean up // clean up
delete server; delete s_server;
s_server = NULL;
CNetwork::cleanup(); CNetwork::cleanup();
CLog::setLock(NULL); CLog::setLock(NULL);
s_logMutex = NULL; s_logMutex = NULL;
} }
catch (...) { catch (...) {
delete server; // clean up
if (!locked && mutex != NULL) {
mutex->lock();
}
delete s_server;
s_server = NULL;
CNetwork::cleanup(); CNetwork::cleanup();
CLog::setLock(NULL); CLog::setLock(NULL);
s_logMutex = NULL; s_logMutex = NULL;
throw; throw;
} }
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
return 16;
}
catch (XThread&) {
// terminated
return 1;
}
return 0;
}
static int restartMain()
{
return realMain(NULL);
}
// invoke realMain and wait for it. if s_restartable then keep
// restarting realMain until it returns a terminate code.
static int restartableMain()
{
if (s_restartable) {
CPlatform platform;
return platform.restart(restartMain, 16);
}
else {
return realMain(NULL);
}
} }
@ -103,11 +161,9 @@ void realMain()
// command line parsing // command line parsing
// //
static void bye() #define BYE "\nTry `%s --help' for more information."
{
log((CLOG_PRINT "Try `%s --help' for more information.", pname)); static void (*bye)(int) = &exit;
exit(1);
}
static void version() static void version()
{ {
@ -125,12 +181,18 @@ static void version()
static void help() static void help()
{ {
CPlatform platform;
log((CLOG_PRINT log((CLOG_PRINT
"Usage: %s" "Usage: %s"
" [--config <pathname>]" " [--config <pathname>]"
" [--debug <level>]" " [--debug <level>]"
" [--daemon|--no-daemon]" " [--"DAEMON"|--no-"DAEMON"]"
" [--restart|--no-restart]\n" " [--restart|--no-restart]\n"
"or\n"
" --install\n"
" --uninstall\n"
"\n"
"Start the synergy mouse/keyboard sharing server.\n" "Start the synergy mouse/keyboard sharing server.\n"
"\n" "\n"
" -c, --config <pathname> use the named configuration file instead\n" " -c, --config <pathname> use the named configuration file instead\n"
@ -138,58 +200,38 @@ static void help()
" -d, --debug <level> filter out log messages with priorty below level.\n" " -d, --debug <level> filter out log messages with priorty below level.\n"
" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
" DEBUG, DEBUG1, DEBUG2.\n" " DEBUG, DEBUG1, DEBUG2.\n"
" -f, --no-daemon run the server in the foreground.\n" " -f, --no-"DAEMON" run the server in the foreground.\n"
" --daemon run the server as a daemon.\n" "* --"DAEMON" run the server as a "DAEMON".\n"
" -1, --no-restart do not try to restart the server if it fails for\n" " -1, --no-restart do not try to restart the server if it fails for\n"
" some reason.\n" " some reason.\n"
" --restart restart the server automatically if it fails.\n" "* --restart restart the server automatically if it fails.\n"
" --install install server as a "DAEMON".\n"
" --uninstall uninstall server "DAEMON".\n"
" -h, --help display this help and exit.\n" " -h, --help display this help and exit.\n"
" --version display version information and exit.\n" " --version display version information and exit.\n"
"\n" "\n"
"By default, the server is a restartable daemon. If no configuration file\n" "* marks defaults.\n"
"pathname is provided then the first of the following to load sets the\n" "\n"
"configuration:\n" "If no configuration file pathname is provided then the first of the\n"
" " CONFIG_USER_DIR CONFIG_NAME "\n" "following to load sets the configuration:\n"
" " CONFIG_SYS_DIR CONFIG_NAME "\n" " %s\n"
" %s\n"
"If no configuration file can be loaded then the configuration uses its\n" "If no configuration file can be loaded then the configuration uses its\n"
"defaults with just the server screen.\n" "defaults with just the server screen.\n"
"\n" "\n"
"Where log messages go depends on the platform and whether or not the\n" "Where log messages go depends on the platform and whether or not the\n"
"server is running as a daemon.", "server is running as a "DAEMON".",
pname)); pname,
platform.addPathComponent(
} platform.getUserDirectory(),
CONFIG_NAME).c_str(),
static bool loadConfig(const char* pathname, bool require) platform.addPathComponent(
{ platform.getSystemDirectory(),
assert(pathname != NULL); CONFIG_NAME).c_str()));
try {
// load configuration
log((CLOG_DEBUG "opening configuration \"%s\"", pathname));
std::ifstream configStream(pathname);
if (!configStream) {
throw XConfigRead("cannot open configuration");
}
configStream >> s_config;
log((CLOG_DEBUG "configuration read successfully"));
return true;
}
catch (XConfigRead& e) {
if (require) {
log((CLOG_PRINT "%s: cannot read configuration '%s'",
pname, pathname));
exit(1);
}
else {
log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname));
}
}
return false;
} }
static bool isArg(int argi, static bool isArg(int argi,
int argc, char** argv, int argc, const char** argv,
const char* name1, const char* name1,
const char* name2, const char* name2,
int minRequiredParameters = 0) int minRequiredParameters = 0)
@ -198,9 +240,9 @@ static bool isArg(int argi,
(name2 != NULL && strcmp(argv[argi], name2) == 0)) { (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
// match. check args left. // match. check args left.
if (argi + minRequiredParameters >= argc) { if (argi + minRequiredParameters >= argc) {
log((CLOG_PRINT "%s: missing arguments for `%s'", log((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
pname, argv[argi])); pname, argv[argi], pname));
bye(); bye(2);
} }
return true; return true;
} }
@ -209,7 +251,7 @@ static bool isArg(int argi,
return false; return false;
} }
static void parse(int argc, char** argv) static void parse(int argc, const char** argv)
{ {
assert(pname != NULL); assert(pname != NULL);
assert(argv != NULL); assert(argv != NULL);
@ -228,12 +270,12 @@ static void parse(int argc, char** argv)
s_configFile = argv[++i]; s_configFile = argv[++i];
} }
else if (isArg(i, argc, argv, "-f", "--no-daemon")) { else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) {
// not a daemon // not a daemon
s_daemon = false; s_daemon = false;
} }
else if (isArg(i, argc, argv, NULL, "--daemon")) { else if (isArg(i, argc, argv, NULL, "--"DAEMON)) {
// daemonize // daemonize
s_daemon = true; s_daemon = true;
} }
@ -250,12 +292,42 @@ static void parse(int argc, char** argv)
else if (isArg(i, argc, argv, "-h", "--help")) { else if (isArg(i, argc, argv, "-h", "--help")) {
help(); help();
exit(1); bye(0);
} }
else if (isArg(i, argc, argv, NULL, "--version")) { else if (isArg(i, argc, argv, NULL, "--version")) {
version(); version();
exit(1); bye(0);
}
else if (isArg(i, argc, argv, NULL, "--install")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_install = true;
if (s_uninstall) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'"
" are mutually exclusive" BYE,
pname, argv[i], pname));
bye(2);
}
}
else if (isArg(i, argc, argv, NULL, "--uninstall")) {
#if !defined(CONFIG_PLATFORM_WIN32)
log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE,
pname, argv[i], pname));
bye(2);
#endif
s_uninstall = true;
if (s_install) {
log((CLOG_PRINT "%s: `--install' and `--uninstall'"
" are mutually exclusive" BYE,
pname, argv[i], pname));
bye(2);
}
} }
else if (isArg(i, argc, argv, "--", NULL)) { else if (isArg(i, argc, argv, "--", NULL)) {
@ -265,8 +337,9 @@ static void parse(int argc, char** argv)
} }
else if (argv[i][0] == '-') { else if (argv[i][0] == '-') {
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
bye(); pname, argv[i], pname));
bye(2);
} }
else { else {
@ -277,101 +350,76 @@ static void parse(int argc, char** argv)
// no non-option arguments are allowed // no non-option arguments are allowed
if (i != argc) { if (i != argc) {
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
bye(); pname, argv[i], pname));
bye(2);
}
// increase default filter level for daemon. the user must
// explicitly request another level for a daemon.
if (s_daemon && s_logFilter == NULL) {
#if defined(CONFIG_PLATFORM_WIN32)
if (CPlatform::isWindows95Family()) {
// windows 95 has no place for logging so avoid showing
// the log console window.
s_logFilter = "FATAL";
}
else
#endif
{
s_logFilter = "NOTE";
}
} }
// set log filter // set log filter
if (!CLog::setFilter(s_logFilter)) { if (!CLog::setFilter(s_logFilter)) {
log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter)); log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
bye(); pname, s_logFilter, pname));
bye(2);
} }
}
// load the config file, if any static bool loadConfig(const char* pathname, bool require)
{
assert(pathname != NULL);
try {
// load configuration
log((CLOG_DEBUG "opening configuration \"%s\"", pathname));
std::ifstream configStream(pathname);
if (!configStream) {
throw XConfigRead("cannot open configuration");
}
configStream >> s_config;
log((CLOG_DEBUG "configuration read successfully"));
return true;
}
catch (XConfigRead&) {
if (require) {
log((CLOG_PRINT "%s: cannot read configuration '%s'",
pname, pathname));
bye(3);
}
else {
log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname));
}
}
return false;
}
static void loadConfig()
{
// load the config file, if specified
if (s_configFile != NULL) { if (s_configFile != NULL) {
// require the user specified file to load correctly // require the user specified file to load correctly
loadConfig(s_configFile, true); loadConfig(s_configFile, true);
} }
}
// load the default configuration if no explicit file given
// else {
// platform dependent entry points
//
#if defined(CONFIG_PLATFORM_WIN32)
#include "CMSWindowsScreen.h"
#include <string.h>
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{
CPlatform platform;
// save instance
CMSWindowsScreen::init(instance);
// get program name
pname = platform.getBasename(argv[0]);
// FIXME -- direct CLog to MessageBox
parse(__argc, __argv);
// FIXME -- undirect CLog from MessageBox
// FIXME -- if daemon then use win32 event log (however that's done),
// otherwise do what? want to use console window for debugging but
// not otherwise.
// load the configuration file if we haven't already
if (s_configFile == NULL) {
// FIXME
}
// FIXME
if (__argc != 1) {
CString msg = "no arguments allowed. exiting.";
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
return 1;
}
try {
realMain();
return 0;
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
CString msg = "failed: ";
msg += e.what();
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
return 1;
}
catch (XThread&) {
// terminated
return 1;
}
}
#elif defined(CONFIG_PLATFORM_UNIX)
#include <unistd.h> // fork()
#include <sys/types.h> // wait()
#include <sys/wait.h> // wait()
int main(int argc, char** argv)
{
CPlatform platform;
// get program name
pname = platform.getBasename(argv[0]);
// parse command line
parse(argc, argv);
// load the configuration file if we haven't already
if (s_configFile == NULL) {
// get the user's home directory. use the effective user id // get the user's home directory. use the effective user id
// so a user can't get a setuid root program to load his file. // so a user can't get a setuid root program to load his file.
CPlatform platform;
bool loaded = false; bool loaded = false;
CString path = platform.getUserDirectory(); CString path = platform.getUserDirectory();
if (!path.empty()) { if (!path.empty()) {
@ -390,62 +438,236 @@ int main(int argc, char** argv)
} }
} }
} }
}
//
// platform dependent entry points
//
#if defined(CONFIG_PLATFORM_WIN32)
#include "CMSWindowsScreen.h"
static bool logMessageBox(int priority, const char* msg)
{
if (priority <= CLog::kFATAL) {
MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING);
return true;
}
else {
return false;
}
}
static void byeThrow(int x)
{
throw CWin32Platform::CDaemonFailed(x);
}
static void daemonStop(void)
{
s_server->quit();
}
static int daemonStartup(IPlatform* iplatform,
int argc, const char** argv)
{
// get platform pointer
CWin32Platform* platform = static_cast<CWin32Platform*>(iplatform);
// catch errors that would normally exit
bye = &byeThrow;
// parse command line
s_install = false;
s_uninstall = false;
parse(argc, argv);
if (s_install || s_uninstall) {
// not allowed to install/uninstall from service
throw CWin32Platform::CDaemonFailed(1);
}
// load configuration
loadConfig();
// run as a service
return platform->runDaemon(realMain, daemonStop);
}
static int daemonStartup95(IPlatform*, int, const char**)
{
return realMain(NULL);
}
static bool logDiscard(int, const char*)
{
return true;
}
static bool s_die = false;
static void checkParse(int e)
{
// anything over 1 means invalid args. 1 means missing args.
// 0 means graceful exit. we plan to exit for anything but
// 1 (missing args); the service control manager may supply
// the missing arguments so we don't exit in that case.
s_die = (e != 1);
throw s_die;
}
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{
CPlatform platform;
// save instance
CMSWindowsScreen::init(instance);
// get program name
pname = platform.getBasename(__argv[0]);
// parse command line without reporting errors but recording if
// the app would've exited. this is too avoid showing a dialog
// box if we're being started as a service because we shouldn't
// take to long to startup as a service. this mostly works but
// will choke if the service control manager passes --install
// or --uninstall (but that's unlikely).
CLog::setOutputter(&logDiscard);
bye = &checkParse;
try {
parse(__argc, const_cast<const char**>(__argv));
}
catch (...) {
// ignore
}
// if we're not starting as an NT service then reparse the command
// line normally.
if (s_die || !s_daemon || s_install || s_uninstall ||
CWin32Platform::isWindows95Family()) {
// send PRINT and FATAL output to a message box
CLog::setOutputter(&logMessageBox);
// exit on bye
bye = &exit;
// reparse
parse(__argc, const_cast<const char**>(__argv));
}
// if starting as a daemon then we ignore the startup command line
// here. we'll parse the command line passed in when the service
// control manager calls us back.
else {
// do nothing
}
// install/uninstall
if (s_install) {
// get the full path to this program
TCHAR path[MAX_PATH];
if (GetModuleFileName(NULL, path,
sizeof(path) / sizeof(path[0])) == 0) {
log((CLOG_CRIT "cannot determine absolute path to program"));
return 16;
}
// construct the command line to start the service with
CString commandLine;
commandLine += "--"DAEMON;
if (s_restartable) {
commandLine += " --restart";
}
else {
commandLine += " --no-restart";
}
if (s_logFilter != NULL) {
commandLine += " --debug ";
commandLine += s_logFilter;
}
if (s_configFile != NULL) {
commandLine += " --config \"";
commandLine += s_configFile;
commandLine += "\"";
}
// install
if (!platform.installDaemon(DAEMON_NAME,
"Shares this system's mouse and keyboard with others.",
path, commandLine.c_str())) {
log((CLOG_CRIT "failed to install service"));
return 16;
}
log((CLOG_PRINT "installed successfully"));
return 0;
}
else if (s_uninstall) {
if (!platform.uninstallDaemon(DAEMON_NAME)) {
log((CLOG_CRIT "failed to uninstall service"));
return 16;
}
log((CLOG_PRINT "uninstalled successfully"));
return 0;
}
// load configuration
loadConfig();
// daemonize if requested // daemonize if requested
int result;
if (s_daemon) { if (s_daemon) {
if (!platform.daemonize("synergyd")) { if (CWin32Platform::isWindows95Family()) {
result = platform.daemonize(DAEMON_NAME, &daemonStartup95);
}
else {
result = platform.daemonize(DAEMON_NAME, &daemonStartup);
}
if (result == -1) {
log((CLOG_CRIT "failed to start as a service"));
return 16;
}
}
else {
result = restartableMain();
}
return result;
}
#elif defined(CONFIG_PLATFORM_UNIX)
static int daemonStartup(IPlatform*, int, const char**)
{
return restartableMain();
}
int main(int argc, char** argv)
{
CPlatform platform;
// get program name
pname = platform.getBasename(argv[0]);
// parse command line
parse(argc, const_cast<const char**>(argv));
// load configuration
loadConfig();
// daemonize if requested
int result;
if (s_daemon) {
result = platform.daemonize(DAEMON_NAME, &daemonStartup);
if (result == -1) {
log((CLOG_CRIT "failed to daemonize")); log((CLOG_CRIT "failed to daemonize"));
return 16; return 16;
} }
} }
else {
// run the server. if running as a daemon then run it in a child result = restartableMain();
// process and restart it as necessary. we have to do this in case
// the X server restarts because our process cannot recover from
// that.
for (;;) {
// don't fork if not restartable
switch (s_restartable ? fork() : 0) {
default: {
// parent process. wait for child to exit.
int status;
if (wait(&status) == -1) {
// wait failed. this is unexpected so bail.
log((CLOG_CRIT "wait() failed"));
return 16;
} }
// what happened? if the child exited normally with a return result;
// status less than 16 then the child was deliberately
// terminated so we also terminate. otherwise, we
// loop.
if (WIFEXITED(status) && WEXITSTATUS(status) < 16) {
return 0;
}
break;
}
case -1:
// fork() failed. log the error and proceed as a child
log((CLOG_WARN "fork() failed; cannot automatically restart on error"));
// fall through
case 0:
// child process
try {
realMain();
return 0;
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
return 16;
}
catch (XThread&) {
// terminated
return 1;
}
}
}
} }
#else #else

View File

@ -23,8 +23,8 @@ CFG=server - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
MTL=midl.exe MTL=midl.exe
RSC=rc.exe RSC=rc.exe
@ -40,6 +40,7 @@ RSC=rc.exe
# PROP Use_Debug_Libraries 0 # PROP Use_Debug_Libraries 0
# PROP Output_Dir "../Release" # PROP Output_Dir "../Release"
# PROP Intermediate_Dir "Release" # PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "" # PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
@ -53,7 +54,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo # ADD BSC32 /nologo
LINK32=link.exe LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../Release/synergyd.exe"
!ELSEIF "$(CFG)" == "server - Win32 Debug" !ELSEIF "$(CFG)" == "server - Win32 Debug"
@ -66,6 +67,7 @@ LINK32=link.exe
# PROP Use_Debug_Libraries 1 # PROP Use_Debug_Libraries 1
# PROP Output_Dir "../Debug" # PROP Output_Dir "../Debug"
# PROP Intermediate_Dir "Debug" # PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "" # PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c
@ -79,7 +81,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo # ADD BSC32 /nologo
LINK32=link.exe LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../Debug/synergyd.exe" /pdbtype:sept
!ENDIF !ENDIF

View File

@ -23,8 +23,8 @@ CFG=synrgyhk - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
MTL=midl.exe MTL=midl.exe
RSC=rc.exe RSC=rc.exe

View File

@ -3,6 +3,7 @@
#include "IInterface.h" #include "IInterface.h"
#include "BasicTypes.h" #include "BasicTypes.h"
#include "KeyTypes.h"
#include "ClipboardTypes.h" #include "ClipboardTypes.h"
class CServer; class CServer;

View File

@ -23,8 +23,8 @@ CFG=synergy - Win32 Debug
# Begin Project # Begin Project
# PROP AllowPerConfigDependencies 0 # PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "millpond" # PROP Scc_ProjName ""
# PROP Scc_LocalPath "." # PROP Scc_LocalPath ""
CPP=cl.exe CPP=cl.exe
RSC=rc.exe RSC=rc.exe