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:
parent
5709d8ddef
commit
4b28ffc5b2
4
all.dsp
4
all.dsp
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
@ -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,42 +73,84 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,24 +25,284 @@ CWin32Platform::~CWin32Platform()
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWin32Platform::installDaemon(/* FIXME */)
|
bool CWin32Platform::isWindows95Family()
|
||||||
{
|
{
|
||||||
// FIXME
|
OSVERSIONINFO version;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWin32Platform::setStatus(
|
||||||
|
SERVICE_STATUS_HANDLE handle,
|
||||||
|
DWORD state)
|
||||||
|
{
|
||||||
|
setStatus(handle, state, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWin32Platform::setStatus(
|
||||||
|
SERVICE_STATUS_HANDLE handle,
|
||||||
|
DWORD state, DWORD step, DWORD waitHint)
|
||||||
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWin32Platform::uninstallDaemon(/* FIXME */)
|
// construct entry
|
||||||
{
|
CString value;
|
||||||
// FIXME
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWin32Platform::daemonize(const char* name)
|
// create the servie
|
||||||
{
|
SC_HANDLE service = CreateService(mgr,
|
||||||
// FIXME
|
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;
|
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
|
||||||
{
|
{
|
||||||
if (pathname == NULL) {
|
if (pathname == NULL) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_ENDSESSION:
|
||||||
|
if (m_is95Family) {
|
||||||
|
if (wParam == TRUE && lParam == 0) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
return 0;
|
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[] =
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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,36 +91,79 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue