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
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
MTL=midl.exe
|
||||
|
||||
!IF "$(CFG)" == "all - Win32 Release"
|
||||
|
|
|
@ -201,10 +201,8 @@ void CLog::output(int priority, char* msg)
|
|||
|
||||
// print it
|
||||
CHoldLock lock(s_lock);
|
||||
if (s_outputter) {
|
||||
s_outputter(priority, msg + g_maxPriorityLength - n);
|
||||
}
|
||||
else {
|
||||
if (s_outputter == NULL ||
|
||||
!s_outputter(priority, msg + g_maxPriorityLength - n)) {
|
||||
#if defined(CONFIG_PLATFORM_WIN32)
|
||||
openConsole();
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,11 @@ public:
|
|||
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);
|
||||
|
||||
//
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=base - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
#pragma warning(disable: 4786) // identifier truncated in debug info
|
||||
#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)
|
||||
|
||||
#else
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
#pragma warning(disable: 4284)
|
||||
#pragma warning(disable: 4146) // unary minus on unsigned value
|
||||
#pragma warning(disable: 4127) // conditional expression is constant
|
||||
#pragma warning(disable: 4701) // variable possibly used uninitialized
|
||||
#endif
|
||||
|
|
|
@ -120,6 +120,11 @@ void CClient::run(const CNetworkAddress& serverAddress)
|
|||
}
|
||||
}
|
||||
|
||||
void CClient::quit()
|
||||
{
|
||||
m_screen->stop();
|
||||
}
|
||||
|
||||
void CClient::onClipboardChanged(ClipboardID id)
|
||||
{
|
||||
log((CLOG_DEBUG "sending clipboard %d changed", id));
|
||||
|
|
|
@ -19,8 +19,12 @@ public:
|
|||
|
||||
// manipulators
|
||||
|
||||
// start the client. does not return until quit() is called.
|
||||
void run(const CNetworkAddress& serverAddress);
|
||||
|
||||
// tell client to exit gracefully
|
||||
void quit();
|
||||
|
||||
// handle events on client's screen
|
||||
void onClipboardChanged(ClipboardID);
|
||||
void onResolutionChanged();
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include "CMSWindowsSecondaryScreen.h"
|
||||
#include "CMSWindowsClipboard.h"
|
||||
#include "CClient.h"
|
||||
#include "CPlatform.h"
|
||||
#include "CClipboard.h"
|
||||
#include "CThread.h"
|
||||
#include "CLock.h"
|
||||
#include "CLog.h"
|
||||
#include "CThread.h"
|
||||
#include "XScreen.h"
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
@ -13,10 +16,18 @@
|
|||
|
||||
CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() :
|
||||
m_client(NULL),
|
||||
m_threadID(0),
|
||||
m_desk(NULL),
|
||||
m_deskName(),
|
||||
m_window(NULL),
|
||||
m_active(false),
|
||||
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()
|
||||
|
@ -26,21 +37,32 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
|
|||
|
||||
void CMSWindowsSecondaryScreen::run()
|
||||
{
|
||||
// must call run() from same thread as open()
|
||||
assert(m_threadID == GetCurrentThreadId());
|
||||
|
||||
// change our priority
|
||||
CThread::getCurrentThread().setPriority(-7);
|
||||
|
||||
// save thread id
|
||||
m_threadID = GetCurrentThreadId();
|
||||
// poll input desktop to see if it changes (onPreTranslate()
|
||||
// handles WM_TIMER)
|
||||
UINT timer = 0;
|
||||
if (!m_is95Family) {
|
||||
SetTimer(NULL, 0, 200, NULL);
|
||||
}
|
||||
|
||||
// run event loop
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
doRun();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
|
||||
// remove timer
|
||||
if (!m_is95Family) {
|
||||
KillTimer(NULL, timer);
|
||||
}
|
||||
}
|
||||
|
||||
void CMSWindowsSecondaryScreen::stop()
|
||||
{
|
||||
log((CLOG_INFO "requesting event loop stop"));
|
||||
doStop();
|
||||
}
|
||||
|
||||
|
@ -49,8 +71,6 @@ void CMSWindowsSecondaryScreen::open(CClient* client)
|
|||
assert(m_client == NULL);
|
||||
assert(client != NULL);
|
||||
|
||||
log((CLOG_INFO "opening screen"));
|
||||
|
||||
// set the client
|
||||
m_client = client;
|
||||
|
||||
|
@ -64,14 +84,16 @@ void CMSWindowsSecondaryScreen::open(CClient* client)
|
|||
// assume primary has all clipboards
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id)
|
||||
grabClipboard(id);
|
||||
|
||||
// hide the cursor
|
||||
m_active = true;
|
||||
leave();
|
||||
}
|
||||
|
||||
void CMSWindowsSecondaryScreen::close()
|
||||
{
|
||||
assert(m_client != NULL);
|
||||
|
||||
log((CLOG_INFO "closing screen"));
|
||||
|
||||
// close the display
|
||||
closeDisplay();
|
||||
|
||||
|
@ -82,12 +104,16 @@ void CMSWindowsSecondaryScreen::close()
|
|||
void CMSWindowsSecondaryScreen::enter(
|
||||
SInt32 x, SInt32 y, KeyModifierMask mask)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
assert(m_active == false);
|
||||
|
||||
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
|
||||
|
||||
// attach thread input queues
|
||||
AttachThreadInput(GetCurrentThreadId(), m_threadID, TRUE);
|
||||
syncDesktop();
|
||||
|
||||
// now active
|
||||
m_active = true;
|
||||
|
||||
// update our keyboard state to reflect the local state
|
||||
updateKeys();
|
||||
|
@ -104,34 +130,25 @@ void CMSWindowsSecondaryScreen::enter(
|
|||
toggleKey(VK_SCROLL, KeyModifierScrollLock);
|
||||
}
|
||||
|
||||
// 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
|
||||
log((CLOG_INFO "show cursor"));
|
||||
ShowWindow(m_window, SW_HIDE);
|
||||
// hide mouse
|
||||
onEnter(x, y);
|
||||
}
|
||||
|
||||
void CMSWindowsSecondaryScreen::leave()
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
assert(m_active == true);
|
||||
|
||||
log((CLOG_INFO "leaving screen"));
|
||||
|
||||
// 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);
|
||||
syncDesktop();
|
||||
|
||||
// raise and show the hider window. take activation.
|
||||
log((CLOG_INFO "hide cursor"));
|
||||
ShowWindow(m_window, SW_SHOWNORMAL);
|
||||
// hide mouse
|
||||
onLeave();
|
||||
|
||||
// not active anymore
|
||||
m_active = false;
|
||||
|
||||
// if we think we own the clipboard but we don't then somebody
|
||||
// grabbed the clipboard on this screen without us knowing.
|
||||
|
@ -160,6 +177,10 @@ void CMSWindowsSecondaryScreen::keyDown(
|
|||
Keystrokes keys;
|
||||
UINT virtualKey;
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
// get the sequence of keys to simulate key press and the final
|
||||
// modifier state.
|
||||
m_mask = mapKey(keys, virtualKey, key, mask, kPress);
|
||||
|
@ -195,6 +216,10 @@ void CMSWindowsSecondaryScreen::keyRepeat(
|
|||
Keystrokes keys;
|
||||
UINT virtualKey;
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
// get the sequence of keys to simulate key repeat and the final
|
||||
// modifier state.
|
||||
m_mask = mapKey(keys, virtualKey, key, mask, kRepeat);
|
||||
|
@ -211,6 +236,10 @@ void CMSWindowsSecondaryScreen::keyUp(
|
|||
Keystrokes keys;
|
||||
UINT virtualKey;
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
// get the sequence of keys to simulate key release and the final
|
||||
// modifier state.
|
||||
m_mask = mapKey(keys, virtualKey, key, mask, kRelease);
|
||||
|
@ -263,6 +292,10 @@ void CMSWindowsSecondaryScreen::keyUp(
|
|||
|
||||
void CMSWindowsSecondaryScreen::mouseDown(ButtonID button)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
// map button id to button flag
|
||||
DWORD flags = mapButton(button, true);
|
||||
|
||||
|
@ -273,6 +306,10 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button)
|
|||
|
||||
void CMSWindowsSecondaryScreen::mouseUp(ButtonID button)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
// map button id to button flag
|
||||
DWORD flags = mapButton(button, false);
|
||||
|
||||
|
@ -281,8 +318,13 @@ void CMSWindowsSecondaryScreen::mouseUp(ButtonID button)
|
|||
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;
|
||||
getScreenSize(&w, &h);
|
||||
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
||||
|
@ -293,20 +335,27 @@ void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
|
|||
|
||||
void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0);
|
||||
}
|
||||
|
||||
void CMSWindowsSecondaryScreen::setClipboard(
|
||||
ClipboardID id, const IClipboard* src)
|
||||
ClipboardID /*id*/, const IClipboard* src)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
|
||||
CMSWindowsClipboard dst(m_window);
|
||||
CClipboard::copy(&dst, src);
|
||||
}
|
||||
|
||||
void CMSWindowsSecondaryScreen::grabClipboard(ClipboardID id)
|
||||
void CMSWindowsSecondaryScreen::grabClipboard(
|
||||
ClipboardID /*id*/)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
|
||||
CMSWindowsClipboard clipboard(m_window);
|
||||
|
@ -321,6 +370,10 @@ void CMSWindowsSecondaryScreen::getMousePos(
|
|||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
POINT pos;
|
||||
if (GetCursorPos(&pos)) {
|
||||
*x = pos.x;
|
||||
|
@ -344,8 +397,9 @@ SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const
|
|||
}
|
||||
|
||||
void CMSWindowsSecondaryScreen::getClipboard(
|
||||
ClipboardID id, IClipboard* dst) const
|
||||
ClipboardID /*id*/, IClipboard* dst) const
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
assert(m_window != NULL);
|
||||
|
||||
CMSWindowsClipboard src(m_window);
|
||||
|
@ -356,44 +410,59 @@ void CMSWindowsSecondaryScreen::onOpenDisplay()
|
|||
{
|
||||
assert(m_window == NULL);
|
||||
|
||||
// initialize clipboard owner to current owner. we don't want
|
||||
// to take ownership of the clipboard just by starting up.
|
||||
m_clipboardOwner = GetClipboardOwner();
|
||||
// save thread id. we'll need to pass this to the hook library.
|
||||
m_threadID = GetCurrentThreadId();
|
||||
|
||||
// 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);
|
||||
|
||||
// hide the cursor
|
||||
leave();
|
||||
|
||||
// install our clipboard snooper
|
||||
m_nextClipboardWindow = SetClipboardViewer(m_window);
|
||||
// get the input desktop and switch to it
|
||||
if (m_is95Family) {
|
||||
if (!openDesktop()) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!switchDesktop(openInputDesktop())) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMSWindowsSecondaryScreen::onCloseDisplay()
|
||||
{
|
||||
assert(m_window != NULL);
|
||||
// disconnect from desktop
|
||||
if (m_is95Family) {
|
||||
closeDesktop();
|
||||
}
|
||||
else {
|
||||
switchDesktop(NULL);
|
||||
}
|
||||
|
||||
// remove clipboard snooper
|
||||
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
||||
m_nextClipboardWindow = NULL;
|
||||
// clear thread id
|
||||
m_threadID = 0;
|
||||
|
||||
// destroy window
|
||||
DestroyWindow(m_window);
|
||||
m_window = NULL;
|
||||
assert(m_window == NULL);
|
||||
assert(m_desk == NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -402,7 +471,21 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
|
|||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
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:
|
||||
ValidateRect(hwnd, NULL);
|
||||
return 0;
|
||||
|
@ -410,7 +493,6 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
|
|||
case WM_ACTIVATEAPP:
|
||||
if (wParam == FALSE) {
|
||||
// some other app activated. hide the hider window.
|
||||
log((CLOG_INFO "show cursor"));
|
||||
ShowWindow(m_window, SW_HIDE);
|
||||
}
|
||||
break;
|
||||
|
@ -423,9 +505,11 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
|
|||
|
||||
// now notify client that somebody changed the clipboard (unless
|
||||
// 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();
|
||||
if (m_clipboardOwner != m_window) {
|
||||
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||
m_client->onClipboardChanged(kClipboardClipboard);
|
||||
m_client->onClipboardChanged(kClipboardSelection);
|
||||
}
|
||||
|
@ -448,6 +532,176 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
|
|||
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.
|
||||
// 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
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "CMSWindowsScreen.h"
|
||||
#include "ISecondaryScreen.h"
|
||||
#include "stdmap.h"
|
||||
#include "CMutex.h"
|
||||
#include "CString.h"
|
||||
#include "stdvector.h"
|
||||
|
||||
class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen {
|
||||
|
@ -39,6 +40,7 @@ protected:
|
|||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
|
||||
virtual void onOpenDisplay();
|
||||
virtual void onCloseDisplay();
|
||||
virtual CString getCurrentDesktopName() const;
|
||||
|
||||
private:
|
||||
enum EKeyAction { kPress, kRelease, kRepeat };
|
||||
|
@ -49,7 +51,21 @@ private:
|
|||
bool m_repeat;
|
||||
};
|
||||
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;
|
||||
KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID,
|
||||
KeyModifierMask, EKeyAction) const;
|
||||
|
@ -63,14 +79,29 @@ private:
|
|||
void sendKeyEvent(UINT virtualKey, bool press);
|
||||
|
||||
private:
|
||||
CMutex m_mutex;
|
||||
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;
|
||||
|
||||
// m_active is true if this screen has been entered
|
||||
bool m_active;
|
||||
|
||||
// clipboard stuff
|
||||
HWND m_nextClipboardWindow;
|
||||
HWND m_clipboardOwner;
|
||||
|
||||
// thread id of the event loop thread
|
||||
DWORD m_threadID;
|
||||
|
||||
// virtual key states
|
||||
BYTE m_keys[256];
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "CClient.h"
|
||||
#include "CString.h"
|
||||
#include "CLog.h"
|
||||
#include "CCondVar.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CNetwork.h"
|
||||
#include "CNetworkAddress.h"
|
||||
|
@ -11,6 +13,15 @@
|
|||
#include "Version.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
|
||||
//
|
||||
|
@ -18,6 +29,8 @@
|
|||
static const char* pname = NULL;
|
||||
static bool s_restartable = 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_serverName = NULL;
|
||||
|
||||
|
@ -42,44 +55,91 @@ static void logLock(bool lock)
|
|||
|
||||
|
||||
//
|
||||
// main
|
||||
// platform independent main
|
||||
//
|
||||
|
||||
void realMain(const CString& name,
|
||||
const CString& hostname,
|
||||
UInt16 port)
|
||||
static CClient* s_client = NULL;
|
||||
|
||||
static int realMain(CMutex* mutex)
|
||||
{
|
||||
// initialize threading library
|
||||
CThread::init();
|
||||
static const UInt16 port = 50001; // FIXME
|
||||
|
||||
// make logging thread safe
|
||||
CMutex logMutex;
|
||||
s_logMutex = &logMutex;
|
||||
CLog::setLock(&logLock);
|
||||
|
||||
CClient* client = NULL;
|
||||
try {
|
||||
// initialize network library
|
||||
CNetwork::init();
|
||||
// initialize threading library
|
||||
CThread::init();
|
||||
|
||||
// run client
|
||||
CNetworkAddress addr(hostname, port);
|
||||
client = new CClient(name);
|
||||
client->run(addr);
|
||||
// make logging thread safe
|
||||
CMutex logMutex;
|
||||
s_logMutex = &logMutex;
|
||||
CLog::setLock(&logLock);
|
||||
|
||||
// clean up
|
||||
delete client;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
bool locked = true;
|
||||
try {
|
||||
// initialize network library
|
||||
CNetwork::init();
|
||||
|
||||
// create client
|
||||
CNetworkAddress addr(s_serverName, port);
|
||||
s_client = new CClient("secondary"); // FIXME
|
||||
|
||||
// run client
|
||||
if (mutex != NULL) {
|
||||
mutex->unlock();
|
||||
}
|
||||
locked = false;
|
||||
s_client->run(addr);
|
||||
locked = true;
|
||||
if (mutex != NULL) {
|
||||
mutex->lock();
|
||||
}
|
||||
|
||||
// clean up
|
||||
delete s_client;
|
||||
s_client = NULL;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
}
|
||||
catch (...) {
|
||||
// clean up
|
||||
if (!locked && mutex != NULL) {
|
||||
mutex->lock();
|
||||
}
|
||||
delete s_client;
|
||||
s_client = NULL;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// clean up
|
||||
delete client;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
throw;
|
||||
catch (XBase& e) {
|
||||
log((CLOG_CRIT "failed: %s", e.what()));
|
||||
return 16;
|
||||
}
|
||||
catch (XThread&) {
|
||||
// terminated
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int restartMain()
|
||||
{
|
||||
return realMain(NULL);
|
||||
}
|
||||
|
||||
// invoke realMain and wait for it. if s_restartable then keep
|
||||
// restarting realMain until it returns a terminate code.
|
||||
static int restartableMain()
|
||||
{
|
||||
if (s_restartable) {
|
||||
CPlatform platform;
|
||||
return platform.restart(restartMain, 16);
|
||||
}
|
||||
else {
|
||||
return realMain(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,11 +148,9 @@ void realMain(const CString& name,
|
|||
// command line parsing
|
||||
//
|
||||
|
||||
static void bye()
|
||||
{
|
||||
log((CLOG_PRINT "Try `%s --help' for more information.", pname));
|
||||
exit(1);
|
||||
}
|
||||
#define BYE "\nTry `%s --help' for more information."
|
||||
|
||||
static void (*bye)(int) = &exit;
|
||||
|
||||
static void version()
|
||||
{
|
||||
|
@ -113,32 +171,37 @@ static void help()
|
|||
log((CLOG_PRINT
|
||||
"Usage: %s"
|
||||
" [--debug <level>]"
|
||||
" [--daemon|--no-daemon]"
|
||||
" [--"DAEMON"|--no-"DAEMON"]"
|
||||
" [--restart|--no-restart]"
|
||||
" [--install]"
|
||||
" <server-address>\n"
|
||||
"or\n"
|
||||
" --uninstall\n"
|
||||
"Start the synergy mouse/keyboard sharing server.\n"
|
||||
"\n"
|
||||
" -d, --debug <level> filter out log messages with priorty below level.\n"
|
||||
" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
|
||||
" DEBUG, DEBUG1, DEBUG2.\n"
|
||||
" -f, --no-daemon run the client in the foreground.\n"
|
||||
" --daemon run the client as a daemon.\n"
|
||||
" -f, --no-"DAEMON" run the client in the foreground.\n"
|
||||
"* --"DAEMON" run the client as a "DAEMON".\n"
|
||||
" -1, --no-restart do not try to restart the client if it fails for\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"
|
||||
" --version display version information and exit.\n"
|
||||
"\n"
|
||||
"By default, the client is a restartable daemon.\n"
|
||||
"* marks defaults.\n"
|
||||
"\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));
|
||||
|
||||
}
|
||||
|
||||
static bool isArg(int argi,
|
||||
int argc, char** argv,
|
||||
int argc, const char** argv,
|
||||
const char* name1,
|
||||
const char* name2,
|
||||
int minRequiredParameters = 0)
|
||||
|
@ -147,9 +210,9 @@ static bool isArg(int argi,
|
|||
(name2 != NULL && strcmp(argv[argi], name2) == 0)) {
|
||||
// match. check args left.
|
||||
if (argi + minRequiredParameters >= argc) {
|
||||
log((CLOG_PRINT "%s: missing arguments for `%s'",
|
||||
pname, argv[argi]));
|
||||
bye();
|
||||
log((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
|
||||
pname, argv[argi], pname));
|
||||
bye(2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -158,7 +221,7 @@ static bool isArg(int argi,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void parse(int argc, char** argv)
|
||||
static void parse(int argc, const char** argv)
|
||||
{
|
||||
assert(pname != NULL);
|
||||
assert(argv != NULL);
|
||||
|
@ -172,12 +235,12 @@ static void parse(int argc, char** argv)
|
|||
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
|
||||
s_daemon = false;
|
||||
}
|
||||
|
||||
else if (isArg(i, argc, argv, NULL, "--daemon")) {
|
||||
else if (isArg(i, argc, argv, NULL, "--"DAEMON)) {
|
||||
// daemonize
|
||||
s_daemon = true;
|
||||
}
|
||||
|
@ -194,12 +257,42 @@ static void parse(int argc, char** argv)
|
|||
|
||||
else if (isArg(i, argc, argv, "-h", "--help")) {
|
||||
help();
|
||||
exit(1);
|
||||
bye(0);
|
||||
}
|
||||
|
||||
else if (isArg(i, argc, argv, NULL, "--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)) {
|
||||
|
@ -209,8 +302,9 @@ static void parse(int argc, char** argv)
|
|||
}
|
||||
|
||||
else if (argv[i][0] == '-') {
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i]));
|
||||
bye();
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
|
||||
pname, argv[i], pname));
|
||||
bye(2);
|
||||
}
|
||||
|
||||
else {
|
||||
|
@ -219,21 +313,51 @@ static void parse(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
// exactly one non-option argument: server-address
|
||||
if (i == argc) {
|
||||
log((CLOG_PRINT "%s: a server address or name is required", pname));
|
||||
bye();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
if (i + 1 != argc) {
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i]));
|
||||
bye();
|
||||
else {
|
||||
if (i == argc) {
|
||||
log((CLOG_PRINT "%s: a server address or name is required" BYE,
|
||||
pname, pname));
|
||||
bye(1);
|
||||
}
|
||||
if (i + 1 != argc) {
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
|
||||
pname, argv[i], pname));
|
||||
bye(2);
|
||||
}
|
||||
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";
|
||||
}
|
||||
}
|
||||
s_serverName = argv[i];
|
||||
|
||||
// set log filter
|
||||
if (!CLog::setFilter(s_logFilter)) {
|
||||
log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter));
|
||||
bye();
|
||||
log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
|
||||
pname, s_logFilter, pname));
|
||||
bye(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,7 +369,71 @@ static void parse(int argc, char** argv)
|
|||
#if defined(CONFIG_PLATFORM_WIN32)
|
||||
|
||||
#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)
|
||||
{
|
||||
|
@ -255,40 +443,115 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
|
|||
CMSWindowsScreen::init(instance);
|
||||
|
||||
// get program name
|
||||
pname = platform.getBasename(argv[0]);
|
||||
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.
|
||||
|
||||
// FIXME
|
||||
// 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 too long to startup in that case. 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 {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
catch (XThread&) {
|
||||
// terminated
|
||||
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)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
static int daemonStartup(IPlatform*, int, const char**)
|
||||
{
|
||||
return restartableMain();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
@ -298,63 +561,22 @@ int main(int argc, char** argv)
|
|||
pname = platform.getBasename(argv[0]);
|
||||
|
||||
// parse command line
|
||||
parse(argc, argv);
|
||||
parse(argc, const_cast<const char**>(argv));
|
||||
|
||||
// daemonize if requested
|
||||
int result;
|
||||
if (s_daemon) {
|
||||
if (!platform.daemonize("synergy")) {
|
||||
result = platform.daemonize(DAEMON_NAME, &daemonStartup);
|
||||
if (result == -1) {
|
||||
log((CLOG_CRIT "failed to daemonize"));
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
// run the server. if running as a daemon then run it in a child
|
||||
// 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
|
||||
// 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 {
|
||||
result = restartableMain();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=client - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
@ -40,6 +40,7 @@ RSC=rc.exe
|
|||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "../Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# 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
|
||||
|
@ -53,7 +54,7 @@ BSC32=bscmake.exe
|
|||
# ADD BSC32 /nologo
|
||||
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 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"
|
||||
|
||||
|
@ -66,6 +67,7 @@ LINK32=link.exe
|
|||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "../Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# 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 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
|
||||
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 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
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=http - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=io - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
|
|
|
@ -614,17 +614,9 @@ unsigned int __stdcall CThreadRep::threadFunc(void* arg)
|
|||
{
|
||||
CThreadRep* rep = (CThreadRep*)arg;
|
||||
|
||||
// initialize OLE
|
||||
const HRESULT hr = OleInitialize(NULL);
|
||||
|
||||
// run thread
|
||||
rep->doThreadFunc();
|
||||
|
||||
// close OLE
|
||||
if (!FAILED(hr)) {
|
||||
OleUninitialize();
|
||||
}
|
||||
|
||||
// signal termination
|
||||
SetEvent(rep->m_exit);
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=mt - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=net - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
|
|
|
@ -152,6 +152,38 @@ void CMSWindowsScreen::getScreenSize(
|
|||
*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
|
||||
{
|
||||
// wait for an event in a cancellable way
|
||||
|
|
|
@ -18,6 +18,11 @@ public:
|
|||
|
||||
static void init(HINSTANCE);
|
||||
|
||||
// accessors
|
||||
|
||||
// get the application instance handle
|
||||
static HINSTANCE getInstance();
|
||||
|
||||
protected:
|
||||
// runs an event loop and returns when WM_QUIT is received
|
||||
void doRun();
|
||||
|
@ -35,9 +40,7 @@ protected:
|
|||
// is closed.
|
||||
void closeDisplay();
|
||||
|
||||
// get the application instance handle and the registered window
|
||||
// class atom
|
||||
static HINSTANCE getInstance();
|
||||
// get the registered window class atom
|
||||
ATOM getClass() const;
|
||||
|
||||
// update screen size cache
|
||||
|
@ -46,6 +49,17 @@ protected:
|
|||
// get the size of the screen
|
||||
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.
|
||||
void getEvent(MSG*) const;
|
||||
|
||||
|
@ -62,6 +76,9 @@ protected:
|
|||
// called by closeDisplay() to
|
||||
virtual void onCloseDisplay() = 0;
|
||||
|
||||
// called by isCurrentDesktop() to get the current desktop name
|
||||
virtual CString getCurrentDesktopName() const = 0;
|
||||
|
||||
private:
|
||||
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
|
||||
|
||||
//
|
||||
// CUnixPlatform
|
||||
//
|
||||
|
@ -22,26 +24,31 @@ CUnixPlatform::~CUnixPlatform()
|
|||
// do nothing
|
||||
}
|
||||
|
||||
bool CUnixPlatform::installDaemon(/* FIXME */)
|
||||
bool CUnixPlatform::installDaemon(
|
||||
const char*,
|
||||
const char*,
|
||||
const char*,
|
||||
const char*)
|
||||
{
|
||||
// daemons don't require special installation
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CUnixPlatform::uninstallDaemon(/* FIXME */)
|
||||
bool CUnixPlatform::uninstallDaemon(const char*)
|
||||
{
|
||||
// daemons don't require special installation
|
||||
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
|
||||
// group leader
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
// failed
|
||||
return false;
|
||||
return -1;
|
||||
|
||||
case 0:
|
||||
// child
|
||||
|
@ -75,7 +82,44 @@ bool CUnixPlatform::daemonize(const char* name)
|
|||
// hook up logger
|
||||
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
|
||||
|
@ -117,7 +161,9 @@ CString CUnixPlatform::addPathComponent(
|
|||
CString path;
|
||||
path.reserve(prefix.size() + 1 + suffix.size());
|
||||
path += prefix;
|
||||
path += '/';
|
||||
if (path.size() == 0 || path[path.size() - 1] != '/') {
|
||||
path += '/';
|
||||
}
|
||||
path += suffix;
|
||||
return path;
|
||||
}
|
||||
|
@ -128,7 +174,7 @@ void CUnixPlatform::setDaemonLogger(const char* name)
|
|||
CLog::setOutputter(&CUnixPlatform::deamonLogger);
|
||||
}
|
||||
|
||||
void CUnixPlatform::deamonLogger(
|
||||
bool CUnixPlatform::deamonLogger(
|
||||
int priority, const char* msg)
|
||||
{
|
||||
// convert priority
|
||||
|
@ -157,4 +203,5 @@ void CUnixPlatform::deamonLogger(
|
|||
|
||||
// log it
|
||||
syslog(priority, "%s", msg);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,13 @@ public:
|
|||
virtual ~CUnixPlatform();
|
||||
|
||||
// IPlatform overrides
|
||||
virtual bool installDaemon(/* FIXME */);
|
||||
virtual bool uninstallDaemon(/* FIXME */);
|
||||
virtual bool daemonize(const char* name);
|
||||
virtual bool installDaemon(const char* name,
|
||||
const char* description,
|
||||
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 CString getUserDirectory() const;
|
||||
virtual CString getSystemDirectory() const;
|
||||
|
@ -23,7 +27,7 @@ protected:
|
|||
virtual void setDaemonLogger(const char* name);
|
||||
|
||||
private:
|
||||
static void deamonLogger(int, const char*);
|
||||
static bool deamonLogger(int, const char*);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
#include "CWin32Platform.h"
|
||||
#include "CLock.h"
|
||||
#include "CThread.h"
|
||||
#include "CLog.h"
|
||||
#include "stdvector.h"
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <tchar.h>
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// CWin32Platform
|
||||
//
|
||||
|
||||
HANDLE CWin32Platform::s_eventLog = NULL;
|
||||
CWin32Platform* CWin32Platform::s_daemonPlatform = NULL;
|
||||
|
||||
CWin32Platform::CWin32Platform()
|
||||
{
|
||||
// do nothing
|
||||
|
@ -17,22 +25,282 @@ CWin32Platform::~CWin32Platform()
|
|||
// do nothing
|
||||
}
|
||||
|
||||
bool CWin32Platform::installDaemon(/* FIXME */)
|
||||
bool CWin32Platform::isWindows95Family()
|
||||
{
|
||||
// FIXME
|
||||
return false;
|
||||
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);
|
||||
}
|
||||
|
||||
bool CWin32Platform::uninstallDaemon(/* FIXME */)
|
||||
void CWin32Platform::setStatus(
|
||||
SERVICE_STATUS_HANDLE handle,
|
||||
DWORD state)
|
||||
{
|
||||
// FIXME
|
||||
return false;
|
||||
setStatus(handle, state, 0, 0);
|
||||
}
|
||||
|
||||
bool CWin32Platform::daemonize(const char* name)
|
||||
void CWin32Platform::setStatus(
|
||||
SERVICE_STATUS_HANDLE handle,
|
||||
DWORD state, DWORD step, DWORD waitHint)
|
||||
{
|
||||
// FIXME
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
// construct entry
|
||||
CString value;
|
||||
value += "\"";
|
||||
value += pathname;
|
||||
value += "\" ";
|
||||
value += commandLine;
|
||||
|
||||
// install entry
|
||||
setValue(key, name, value);
|
||||
|
||||
// clean up
|
||||
closeKey(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// windows NT family services
|
||||
else {
|
||||
// open service manager
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
|
||||
if (mgr == NULL) {
|
||||
log((CLOG_ERR "OpenSCManager failed with %d", GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the servie
|
||||
SC_HANDLE service = CreateService(mgr,
|
||||
name,
|
||||
name,
|
||||
0,
|
||||
SERVICE_WIN32_OWN_PROCESS |
|
||||
SERVICE_INTERACTIVE_PROCESS,
|
||||
SERVICE_AUTO_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
pathname,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
// done with service and manager
|
||||
if (service != NULL) {
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(mgr);
|
||||
}
|
||||
else {
|
||||
log((CLOG_ERR "CreateService failed with %d", GetLastError()));
|
||||
CloseServiceHandle(mgr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// open the registry key for this service
|
||||
HKEY key = openNTServicesKey();
|
||||
key = openKey(key, name);
|
||||
if (key == NULL) {
|
||||
// can't open key
|
||||
uninstallDaemon(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the description
|
||||
setValue(key, "Description", description);
|
||||
|
||||
// set command line
|
||||
key = openKey(key, "Parameters");
|
||||
if (key == NULL) {
|
||||
// can't open key
|
||||
uninstallDaemon(name);
|
||||
return false;
|
||||
}
|
||||
setValue(key, "CommandLine", commandLine);
|
||||
|
||||
// done with registry
|
||||
closeKey(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CWin32Platform::uninstallDaemon(const char* name)
|
||||
{
|
||||
// windows 95 family services
|
||||
if (isWindows95Family()) {
|
||||
// open registry
|
||||
HKEY key = open95ServicesKey();
|
||||
if (key == NULL) {
|
||||
log((CLOG_ERR "cannot open RunServices registry key", GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove entry
|
||||
deleteValue(key, name);
|
||||
|
||||
// clean up
|
||||
closeKey(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// windows NT family services
|
||||
else {
|
||||
// remove parameters for this service
|
||||
HKEY key = openNTServicesKey();
|
||||
key = openKey(key, name);
|
||||
if (key != NULL) {
|
||||
deleteKey(key, "Parameters");
|
||||
closeKey(key);
|
||||
}
|
||||
|
||||
// open service manager
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
|
||||
if (mgr == NULL) {
|
||||
log((CLOG_ERR "OpenSCManager failed with %d", GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// open the service. oddly, you must open a service to delete it.
|
||||
bool success;
|
||||
SC_HANDLE service = OpenService(mgr, name, DELETE);
|
||||
if (service == NULL) {
|
||||
log((CLOG_ERR "OpenService failed with %d", GetLastError()));
|
||||
success = false;
|
||||
}
|
||||
|
||||
else {
|
||||
success = (DeleteService(service) != 0);
|
||||
CloseServiceHandle(service);
|
||||
}
|
||||
|
||||
// close the manager
|
||||
CloseServiceHandle(mgr);
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
int CWin32Platform::daemonize(
|
||||
const char* name,
|
||||
DaemonFunc func)
|
||||
{
|
||||
assert(name != NULL);
|
||||
assert(func != NULL);
|
||||
|
||||
// windows 95 family services
|
||||
if (isWindows95Family()) {
|
||||
typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD);
|
||||
|
||||
// mark this process as a service so it's not killed when the
|
||||
// user logs off.
|
||||
HINSTANCE kernel = LoadLibrary("kernel32.dll");
|
||||
if (kernel == NULL) {
|
||||
log((CLOG_ERR "LoadLibrary failed with %d", GetLastError()));
|
||||
return -1;
|
||||
}
|
||||
RegisterServiceProcessT RegisterServiceProcess =
|
||||
reinterpret_cast<RegisterServiceProcessT>(
|
||||
GetProcAddress(kernel,
|
||||
_T("RegisterServiceProcess")));
|
||||
if (RegisterServiceProcess == NULL) {
|
||||
log((CLOG_ERR "can't lookup RegisterServiceProcess: %d", GetLastError()));
|
||||
FreeLibrary(kernel);
|
||||
return -1;
|
||||
}
|
||||
if (RegisterServiceProcess(NULL, 1) == 0) {
|
||||
log((CLOG_ERR "RegisterServiceProcess failed with %d", GetLastError()));
|
||||
FreeLibrary(kernel);
|
||||
return -1;
|
||||
}
|
||||
FreeLibrary(kernel);
|
||||
|
||||
// now simply call the daemon function
|
||||
return func(this, 1, &name);
|
||||
}
|
||||
|
||||
// windows NT family services
|
||||
else {
|
||||
// save daemon function
|
||||
m_daemonFunc = func;
|
||||
|
||||
// construct the service entry
|
||||
SERVICE_TABLE_ENTRY entry[2];
|
||||
entry[0].lpServiceName = const_cast<char*>(name);
|
||||
entry[0].lpServiceProc = &CWin32Platform::serviceMainEntry;
|
||||
entry[1].lpServiceName = NULL;
|
||||
entry[1].lpServiceProc = NULL;
|
||||
|
||||
// hook us up to the service control manager. this won't return
|
||||
// (if successful) until the processes have terminated.
|
||||
s_daemonPlatform = this;
|
||||
if (StartServiceCtrlDispatcher(entry)) {
|
||||
s_daemonPlatform = NULL;
|
||||
return m_daemonResult;
|
||||
}
|
||||
log((CLOG_ERR "StartServiceCtrlDispatcher failed with %d", GetLastError()));
|
||||
s_daemonPlatform = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int CWin32Platform::restart(
|
||||
RestartFunc func, int /*minErrorCode*/)
|
||||
{
|
||||
// FIXME -- start in separate process or thread. note that this
|
||||
// isn't too critical as win32 doesn't force us to terminate for
|
||||
// any reason so we should never have to restart.
|
||||
return func();
|
||||
}
|
||||
|
||||
const char* CWin32Platform::getBasename(const char* pathname) const
|
||||
|
@ -61,14 +329,58 @@ const char* CWin32Platform::getBasename(const char* pathname) const
|
|||
|
||||
CString CWin32Platform::getUserDirectory() const
|
||||
{
|
||||
// FIXME
|
||||
return CString();
|
||||
// try %HOMEPATH%
|
||||
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
|
||||
{
|
||||
// FIXME
|
||||
return "";
|
||||
// get windows directory
|
||||
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(
|
||||
|
@ -78,13 +390,453 @@ CString CWin32Platform::addPathComponent(
|
|||
CString path;
|
||||
path.reserve(prefix.size() + 1 + suffix.size());
|
||||
path += prefix;
|
||||
path += '\\';
|
||||
if (path.size() == 0 ||
|
||||
(path[path.size() - 1] != '\\' &&
|
||||
path[path.size() - 1] != '/')) {
|
||||
path += '\\';
|
||||
}
|
||||
path += suffix;
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
|
||||
#include "IPlatform.h"
|
||||
#include "CCondVar.h"
|
||||
#include "CMutex.h"
|
||||
#include <windows.h>
|
||||
|
||||
class CWin32Platform : public IPlatform {
|
||||
public:
|
||||
typedef int (*RunFunc)(CMutex*);
|
||||
typedef void (*StopFunc)(void);
|
||||
|
||||
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
|
||||
virtual bool installDaemon(/* FIXME */);
|
||||
virtual bool uninstallDaemon(/* FIXME */);
|
||||
virtual bool daemonize(const char* name);
|
||||
virtual bool installDaemon(const char* name,
|
||||
const char* description,
|
||||
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 CString getUserDirectory() const;
|
||||
virtual CString getSystemDirectory() const;
|
||||
|
@ -20,7 +56,40 @@ public:
|
|||
const CString& suffix) const;
|
||||
|
||||
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
|
||||
|
|
|
@ -7,19 +7,48 @@
|
|||
|
||||
class IPlatform : public IInterface {
|
||||
public:
|
||||
typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv);
|
||||
typedef int (*RestartFunc)();
|
||||
|
||||
// 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.
|
||||
virtual bool installDaemon(/* FIXME */) = 0;
|
||||
virtual bool uninstallDaemon(/* FIXME */) = 0;
|
||||
virtual bool installDaemon(const char* name,
|
||||
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
|
||||
// messages to a system message logger since messages can no
|
||||
// longer go to the console. returns true iff successful.
|
||||
// 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
|
||||
|
||||
|
|
|
@ -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 "CMSWindowsClipboard.h"
|
||||
#include "CServer.h"
|
||||
#include "CSynergyHook.h"
|
||||
#include "CPlatform.h"
|
||||
#include "XScreen.h"
|
||||
#include "XSynergy.h"
|
||||
#include "CThread.h"
|
||||
#include "CLog.h"
|
||||
#include "CThread.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -14,38 +15,76 @@
|
|||
|
||||
CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() :
|
||||
m_server(NULL),
|
||||
m_active(false),
|
||||
m_threadID(0),
|
||||
m_desk(NULL),
|
||||
m_deskName(),
|
||||
m_window(NULL),
|
||||
m_nextClipboardWindow(NULL),
|
||||
m_clipboardOwner(NULL),
|
||||
m_hookLibrary(NULL),
|
||||
m_active(false),
|
||||
m_mark(0),
|
||||
m_markReceived(0)
|
||||
m_markReceived(0),
|
||||
m_nextClipboardWindow(NULL),
|
||||
m_clipboardOwner(NULL)
|
||||
{
|
||||
// detect operating system
|
||||
OSVERSIONINFO version;
|
||||
version.dwOSVersionInfoSize = sizeof(version);
|
||||
if (GetVersionEx(&version) == 0) {
|
||||
log((CLOG_WARN "cannot determine OS: %d", GetLastError()));
|
||||
// load the hook library
|
||||
m_hookLibrary = LoadLibrary("synrgyhk");
|
||||
if (m_hookLibrary == NULL) {
|
||||
log((CLOG_ERR "failed to load hook library"));
|
||||
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()
|
||||
{
|
||||
assert(m_hookLibrary != NULL);
|
||||
assert(m_window == NULL);
|
||||
assert(m_hookLibrary == NULL);
|
||||
|
||||
// done with hook library
|
||||
FreeLibrary(m_hookLibrary);
|
||||
}
|
||||
|
||||
void CMSWindowsPrimaryScreen::run()
|
||||
{
|
||||
// must call run() from same thread as open()
|
||||
assert(m_threadID == GetCurrentThreadId());
|
||||
|
||||
// change our priority
|
||||
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
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
doRun();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
|
||||
// remove timer
|
||||
if (!m_is95Family) {
|
||||
KillTimer(NULL, timer);
|
||||
}
|
||||
}
|
||||
|
||||
void CMSWindowsPrimaryScreen::stop()
|
||||
|
@ -64,16 +103,25 @@ void CMSWindowsPrimaryScreen::open(CServer* server)
|
|||
// open the display
|
||||
openDisplay();
|
||||
|
||||
// get keyboard state
|
||||
updateKeys();
|
||||
// initialize marks
|
||||
m_mark = 0;
|
||||
m_markReceived = 0;
|
||||
nextMark();
|
||||
|
||||
// send screen info
|
||||
SInt32 w, h;
|
||||
getScreenSize(&w, &h);
|
||||
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
|
||||
doEnter();
|
||||
enterNoWarp();
|
||||
}
|
||||
|
||||
void CMSWindowsPrimaryScreen::close()
|
||||
|
@ -92,115 +140,48 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
|
|||
log((CLOG_INFO "entering primary at %d,%d", x, y));
|
||||
assert(m_active == true);
|
||||
|
||||
doEnter();
|
||||
// enter the screen
|
||||
enterNoWarp();
|
||||
|
||||
// warp to requested location
|
||||
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()
|
||||
{
|
||||
log((CLOG_INFO "leaving primary"));
|
||||
assert(m_active == false);
|
||||
|
||||
// do non-warp enter stuff
|
||||
// get state of keys as we leave
|
||||
updateKeys();
|
||||
|
||||
// all messages prior to now are invalid
|
||||
nextMark();
|
||||
|
||||
// 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);
|
||||
}
|
||||
// save active window, show ours, and grab mouse/keyboard
|
||||
if (!onLeave()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// show our window
|
||||
ShowWindow(m_window, SW_SHOW);
|
||||
|
||||
// relay all mouse and keyboard events
|
||||
SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress(
|
||||
m_hookLibrary, "setRelay");
|
||||
setRelay();
|
||||
m_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) {
|
||||
// disable ctrl+alt+del, alt+tab, ctrl+esc
|
||||
DWORD 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
|
||||
nextMark();
|
||||
|
@ -238,12 +219,11 @@ log((CLOG_INFO "failed to get hot key: %d", GetLastError()));
|
|||
|
||||
void CMSWindowsPrimaryScreen::onConfigure()
|
||||
{
|
||||
if (!m_active) {
|
||||
if ((m_is95Family || m_desk != NULL) && !m_active) {
|
||||
SInt32 w, h;
|
||||
getScreenSize(&w, &h);
|
||||
SetZoneFunc setZone = (SetZoneFunc)GetProcAddress(
|
||||
m_hookLibrary, "setZone");
|
||||
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
|
||||
m_setZone(m_server->getActivePrimarySides(),
|
||||
w, h, getJumpZoneSize());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +234,7 @@ void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
|||
}
|
||||
|
||||
void CMSWindowsPrimaryScreen::setClipboard(
|
||||
ClipboardID id, const IClipboard* src)
|
||||
ClipboardID /*id*/, const IClipboard* src)
|
||||
{
|
||||
assert(m_window != NULL);
|
||||
|
||||
|
@ -262,7 +242,8 @@ void CMSWindowsPrimaryScreen::setClipboard(
|
|||
CClipboard::copy(&dst, src);
|
||||
}
|
||||
|
||||
void CMSWindowsPrimaryScreen::grabClipboard(ClipboardID id)
|
||||
void CMSWindowsPrimaryScreen::grabClipboard(
|
||||
ClipboardID /*id*/)
|
||||
{
|
||||
assert(m_window != NULL);
|
||||
|
||||
|
@ -284,7 +265,7 @@ SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const
|
|||
}
|
||||
|
||||
void CMSWindowsPrimaryScreen::getClipboard(
|
||||
ClipboardID id, IClipboard* dst) const
|
||||
ClipboardID /*id*/, IClipboard* dst) const
|
||||
{
|
||||
assert(m_window != NULL);
|
||||
|
||||
|
@ -332,80 +313,37 @@ void CMSWindowsPrimaryScreen::onOpenDisplay()
|
|||
assert(m_window == NULL);
|
||||
assert(m_server != NULL);
|
||||
|
||||
// initialize clipboard owner to current owner. we don't want
|
||||
// to take ownership of the clipboard just by starting up.
|
||||
m_clipboardOwner = GetClipboardOwner();
|
||||
// save thread id. we'll need to pass this to the hook library.
|
||||
m_threadID = GetCurrentThreadId();
|
||||
|
||||
// get screen size
|
||||
// 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 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);
|
||||
// get the input desktop and switch to it
|
||||
if (m_is95Family) {
|
||||
if (!openDesktop()) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
if (!hooked) {
|
||||
log((CLOG_ERR "failed to install hooks"));
|
||||
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
||||
m_nextClipboardWindow = NULL;
|
||||
DestroyWindow(m_window);
|
||||
m_window = NULL;
|
||||
// FIXME -- throw
|
||||
else {
|
||||
if (!switchDesktop(openInputDesktop())) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
|
||||
// initialize marks
|
||||
m_mark = 0;
|
||||
m_markReceived = 0;
|
||||
nextMark();
|
||||
}
|
||||
|
||||
void CMSWindowsPrimaryScreen::onCloseDisplay()
|
||||
{
|
||||
assert(m_window != NULL);
|
||||
|
||||
// uninstall input hooks
|
||||
UninstallFunc uninstall = (UninstallFunc)GetProcAddress(
|
||||
m_hookLibrary, "uninstall");
|
||||
if (uninstall != NULL) {
|
||||
uninstall();
|
||||
// disconnect from desktop
|
||||
if (m_is95Family) {
|
||||
closeDesktop();
|
||||
}
|
||||
else {
|
||||
switchDesktop(NULL);
|
||||
}
|
||||
|
||||
// done with hook library
|
||||
FreeLibrary(m_hookLibrary);
|
||||
m_hookLibrary = NULL;
|
||||
// clear thread id
|
||||
m_threadID = 0;
|
||||
|
||||
// remove clipboard snooper
|
||||
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
||||
m_nextClipboardWindow = NULL;
|
||||
|
||||
// destroy window
|
||||
DestroyWindow(m_window);
|
||||
m_window = NULL;
|
||||
assert(m_window == NULL);
|
||||
assert(m_desk == NULL);
|
||||
}
|
||||
|
||||
bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
||||
|
@ -417,8 +355,8 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
|||
return true;
|
||||
|
||||
case SYNERGY_MSG_KEY:
|
||||
// ignore if not at current mark
|
||||
if (m_mark == m_markReceived) {
|
||||
// ignore message if posted prior to last mark change
|
||||
if (m_markReceived == m_mark) {
|
||||
KeyModifierMask mask;
|
||||
const KeyID key = mapKey(msg->wParam, msg->lParam, &mask);
|
||||
if (key != kKeyNone) {
|
||||
|
@ -453,8 +391,8 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
|||
return true;
|
||||
|
||||
case SYNERGY_MSG_MOUSE_BUTTON:
|
||||
// ignore if not at current mark
|
||||
if (m_mark == m_markReceived) {
|
||||
// ignore message if posted prior to last mark change
|
||||
if (m_markReceived == m_mark) {
|
||||
const ButtonID button = mapButton(msg->wParam);
|
||||
switch (msg->wParam) {
|
||||
case WM_LBUTTONDOWN:
|
||||
|
@ -479,20 +417,19 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
|||
return true;
|
||||
|
||||
case SYNERGY_MSG_MOUSE_WHEEL:
|
||||
// ignore if not at current mark
|
||||
if (m_mark == m_markReceived) {
|
||||
// ignore message if posted prior to last mark change
|
||||
if (m_markReceived == m_mark) {
|
||||
log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam));
|
||||
m_server->onMouseWheel(msg->wParam);
|
||||
}
|
||||
return true;
|
||||
|
||||
case SYNERGY_MSG_MOUSE_MOVE:
|
||||
// ignore if not at current mark
|
||||
if (m_mark == m_markReceived) {
|
||||
SInt32 x = (SInt32)msg->wParam;
|
||||
SInt32 y = (SInt32)msg->lParam;
|
||||
// ignore message if posted prior to last mark change
|
||||
if (m_markReceived == m_mark) {
|
||||
SInt32 x = static_cast<SInt32>(msg->wParam);
|
||||
SInt32 y = static_cast<SInt32>(msg->lParam);
|
||||
if (!m_active) {
|
||||
log((CLOG_DEBUG2 "event: inactive move %d,%d", x, y));
|
||||
m_server->onMouseMovePrimary(x, y);
|
||||
}
|
||||
else {
|
||||
|
@ -500,15 +437,35 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
|||
x -= m_xCenter;
|
||||
y -= m_yCenter;
|
||||
|
||||
// ignore if the mouse didn't move
|
||||
if (x != 0 && y != 0) {
|
||||
log((CLOG_DEBUG2 "event: active move %d,%d", x, y));
|
||||
// ignore if the mouse didn't move or we're ignoring
|
||||
// motion.
|
||||
if (m_mouseMoveIgnore == 0) {
|
||||
if (x != 0 && y != 0) {
|
||||
// warp mouse back to center
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
|
||||
// warp mouse back to center
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
// send motion
|
||||
m_server->onMouseMoveSecondary(x, y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// ignored one more motion event
|
||||
--m_mouseMoveIgnore;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
// send motion
|
||||
m_server->onMouseMoveSecondary(x, y);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -523,11 +480,20 @@ LRESULT CMSWindowsPrimaryScreen::onEvent(
|
|||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg) {
|
||||
/*
|
||||
case WM_HOTKEY:
|
||||
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 0;
|
||||
*/
|
||||
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:
|
||||
ValidateRect(hwnd, NULL);
|
||||
|
@ -537,7 +503,9 @@ return 0;
|
|||
log((CLOG_DEBUG "clipboard was taken"));
|
||||
|
||||
// first pass it on
|
||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||
if (m_nextClipboardWindow != NULL) {
|
||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
// now notify server that somebody changed the clipboard.
|
||||
// skip that if we're the new owner.
|
||||
|
@ -555,51 +523,302 @@ return 0;
|
|||
return 0;
|
||||
|
||||
case WM_CHANGECBCHAIN:
|
||||
if (m_nextClipboardWindow == (HWND)wParam)
|
||||
if (m_nextClipboardWindow == (HWND)wParam) {
|
||||
m_nextClipboardWindow = (HWND)lParam;
|
||||
else
|
||||
}
|
||||
else if (m_nextClipboardWindow != NULL) {
|
||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_DISPLAYCHANGE: {
|
||||
// screen resolution has changed
|
||||
SInt32 w, h;
|
||||
updateScreenSize();
|
||||
getScreenSize(&w, &h);
|
||||
case WM_DISPLAYCHANGE:
|
||||
{
|
||||
// screen resolution may have changed
|
||||
SInt32 wOld, hOld;
|
||||
getScreenSize(&wOld, &hOld);
|
||||
SInt32 w, h;
|
||||
updateScreenSize();
|
||||
getScreenSize(&w, &h);
|
||||
|
||||
// recompute center pixel of screen
|
||||
m_xCenter = w >> 1;
|
||||
m_yCenter = h >> 1;
|
||||
// do nothing if resolution hasn't changed
|
||||
if (w != wOld || h != hOld) {
|
||||
// recompute center pixel of screen
|
||||
m_xCenter = w >> 1;
|
||||
m_yCenter = h >> 1;
|
||||
|
||||
// warp mouse to center if active
|
||||
if (m_active) {
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
// warp mouse to center if active
|
||||
if (m_active) {
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
}
|
||||
|
||||
// tell hook about resize if not active
|
||||
else {
|
||||
onConfigure();
|
||||
}
|
||||
|
||||
// send new screen info
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// tell hook about resize if not active
|
||||
else {
|
||||
SetZoneFunc setZone = (SetZoneFunc)GetProcAddress(
|
||||
m_hookLibrary, "setZone");
|
||||
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
|
||||
}
|
||||
|
||||
// send new screen info
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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[] =
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#ifndef CMSWINDOWSPRIMARYSCREEN_H
|
||||
#define CMSWINDOWSPRIMARYSCREEN_H
|
||||
|
||||
#include "KeyTypes.h"
|
||||
#include "MouseTypes.h"
|
||||
#include "CMSWindowsScreen.h"
|
||||
#include "IPrimaryScreen.h"
|
||||
#include "MouseTypes.h"
|
||||
#include "CString.h"
|
||||
#include "CSynergyHook.h"
|
||||
|
||||
class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen {
|
||||
public:
|
||||
|
@ -36,12 +37,24 @@ protected:
|
|||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
|
||||
virtual void onOpenDisplay();
|
||||
virtual void onCloseDisplay();
|
||||
virtual CString getCurrentDesktopName() const;
|
||||
|
||||
private:
|
||||
void doEnter();
|
||||
void enterNoWarp();
|
||||
void onEnter();
|
||||
bool onLeave();
|
||||
|
||||
// discard posted messages
|
||||
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,
|
||||
KeyModifierMask* maskOut);
|
||||
ButtonID mapButton(WPARAM button) const;
|
||||
|
@ -50,19 +63,51 @@ private:
|
|||
|
||||
private:
|
||||
CServer* m_server;
|
||||
|
||||
// true if windows 95/98/me
|
||||
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;
|
||||
|
||||
// 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_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_lastActiveWindow;
|
||||
DWORD m_lastActiveThread;
|
||||
HINSTANCE m_hookLibrary;
|
||||
UInt32 m_mark;
|
||||
UInt32 m_markReceived;
|
||||
BYTE m_keys[256];
|
||||
SInt32 m_xCenter, m_yCenter;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -298,7 +298,7 @@ void CServer::setInfoNoLock(const CString& screen,
|
|||
info->m_width = w;
|
||||
info->m_height = h;
|
||||
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)
|
||||
if (info->m_protocol != NULL) {
|
||||
|
@ -353,7 +353,7 @@ void CServer::grabClipboardNoLock(
|
|||
}
|
||||
|
||||
// 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_clipboardSeqNum = seqNum;
|
||||
|
||||
|
@ -404,7 +404,7 @@ void CServer::setClipboard(ClipboardID id,
|
|||
}
|
||||
|
||||
// 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_clipboardData = data;
|
||||
clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0);
|
||||
|
@ -603,17 +603,25 @@ void CServer::onMouseMoveSecondaryNoLock(
|
|||
if (!isLockedToScreenNoLock()) {
|
||||
// find direction of neighbor
|
||||
CConfig::EDirection dir;
|
||||
if (m_x < 0)
|
||||
if (m_x < 0) {
|
||||
dir = CConfig::kLeft;
|
||||
else if (m_x > m_active->m_width - 1)
|
||||
}
|
||||
else if (m_x > m_active->m_width - 1) {
|
||||
dir = CConfig::kRight;
|
||||
else if (m_y < 0)
|
||||
}
|
||||
else if (m_y < 0) {
|
||||
dir = CConfig::kTop;
|
||||
else if (m_y > m_active->m_height - 1)
|
||||
}
|
||||
else if (m_y > m_active->m_height - 1) {
|
||||
dir = CConfig::kBottom;
|
||||
else
|
||||
}
|
||||
else {
|
||||
newScreen = m_active;
|
||||
|
||||
// keep compiler quiet about unset variable
|
||||
dir = CConfig::kLeft;
|
||||
}
|
||||
|
||||
// get neighbor if we should switch
|
||||
if (newScreen == NULL) {
|
||||
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(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
|
||||
|
||||
// record new position
|
||||
|
@ -1485,7 +1493,7 @@ void CServer::removeConnection(const CString& name)
|
|||
m_y = m_primaryInfo->m_height >> 1;
|
||||
|
||||
// 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
|
||||
m_active = m_primaryInfo;
|
||||
|
|
|
@ -33,28 +33,26 @@ typedef struct tagMOUSEHOOKSTRUCTWin2000 {
|
|||
#pragma data_seg("shared")
|
||||
// all data in this shared section *must* be initialized
|
||||
|
||||
static HINSTANCE g_hinstance = NULL;
|
||||
static DWORD g_process = NULL;
|
||||
static EWheelSupport g_wheelSupport = kWheelNone;
|
||||
static UINT g_wmMouseWheel = 0;
|
||||
static HWND g_hwnd = NULL;
|
||||
static HHOOK g_keyboard = NULL;
|
||||
static HHOOK g_mouse = NULL;
|
||||
static HHOOK g_cbt = NULL;
|
||||
static HHOOK g_getMessage = NULL;
|
||||
static HANDLE g_keyHookThread = NULL;
|
||||
static HINSTANCE g_hinstance = NULL;
|
||||
static DWORD g_process = NULL;
|
||||
static EWheelSupport g_wheelSupport = kWheelNone;
|
||||
static UINT g_wmMouseWheel = 0;
|
||||
static DWORD g_threadID = 0;
|
||||
static HHOOK g_keyboard = NULL;
|
||||
static HHOOK g_mouse = NULL;
|
||||
static HHOOK g_cbt = NULL;
|
||||
static HHOOK g_getMessage = NULL;
|
||||
static HANDLE g_keyHookThread = NULL;
|
||||
static DWORD g_keyHookThreadID = 0;
|
||||
static HANDLE g_keyHookEvent = NULL;
|
||||
static HHOOK g_keyboardLL = NULL;
|
||||
static bool g_relay = false;
|
||||
static SInt32 g_zoneSize = 0;
|
||||
static UInt32 g_zoneSides = 0;
|
||||
static SInt32 g_wScreen = 0;
|
||||
static SInt32 g_hScreen = 0;
|
||||
static HCURSOR g_cursor = NULL;
|
||||
static DWORD g_cursorThread = 0;
|
||||
|
||||
static int foo = 0;
|
||||
static HANDLE g_keyHookEvent = NULL;
|
||||
static HHOOK g_keyboardLL = NULL;
|
||||
static bool g_relay = false;
|
||||
static SInt32 g_zoneSize = 0;
|
||||
static UInt32 g_zoneSides = 0;
|
||||
static SInt32 g_wScreen = 0;
|
||||
static SInt32 g_hScreen = 0;
|
||||
static HCURSOR g_cursor = NULL;
|
||||
static DWORD g_cursorThread = 0;
|
||||
|
||||
#pragma data_seg()
|
||||
|
||||
|
@ -91,16 +89,7 @@ static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam)
|
|||
if (code >= 0) {
|
||||
if (g_relay) {
|
||||
// forward message to our window
|
||||
PostMessage(g_hwnd, 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);
|
||||
}
|
||||
*/
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam);
|
||||
|
||||
// let certain keys pass through
|
||||
switch (wParam) {
|
||||
|
@ -133,59 +122,65 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam)
|
|||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0);
|
||||
PostThreadMessage(g_threadID,
|
||||
SYNERGY_MSG_MOUSE_BUTTON, wParam, 0);
|
||||
return 1;
|
||||
|
||||
case WM_MOUSEWHEEL: {
|
||||
// win2k and other systems supporting WM_MOUSEWHEEL in
|
||||
// the mouse hook are gratuitously different (and poorly
|
||||
// documented).
|
||||
switch (g_wheelSupport) {
|
||||
case kWheelModern: {
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
// win2k and other systems supporting WM_MOUSEWHEEL in
|
||||
// the mouse hook are gratuitously different (and poorly
|
||||
// documented).
|
||||
switch (g_wheelSupport) {
|
||||
case kWheelModern: {
|
||||
const MOUSEHOOKSTRUCT* info =
|
||||
(const MOUSEHOOKSTRUCT*)lParam;
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL,
|
||||
static_cast<short>(
|
||||
LOWORD(info->dwExtraInfo)), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case kWheelWin2000: {
|
||||
const MOUSEHOOKSTRUCTWin2000* info =
|
||||
(const MOUSEHOOKSTRUCTWin2000*)lParam;
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL,
|
||||
static_cast<short>(
|
||||
HIWORD(info->mouseData)), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
const MOUSEHOOKSTRUCT* info =
|
||||
(const MOUSEHOOKSTRUCT*)lParam;
|
||||
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL,
|
||||
static_cast<short>(LOWORD(info->dwExtraInfo)), 0);
|
||||
break;
|
||||
}
|
||||
SInt32 x = (SInt32)info->pt.x;
|
||||
SInt32 y = (SInt32)info->pt.y;
|
||||
|
||||
case kWheelWin2000: {
|
||||
const MOUSEHOOKSTRUCTWin2000* info =
|
||||
(const MOUSEHOOKSTRUCTWin2000*)lParam;
|
||||
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL,
|
||||
static_cast<short>(HIWORD(info->mouseData)), 0);
|
||||
break;
|
||||
}
|
||||
// we want the cursor to be hidden at all times so we
|
||||
// hide the cursor on whatever window has it. but then
|
||||
// we have to show the cursor whenever we leave that
|
||||
// window (or at some later time before we stop relaying).
|
||||
// so check the window with the cursor. if it's not the
|
||||
// same window that had it before then show the cursor
|
||||
// in the last window and hide it in this window.
|
||||
DWORD thread = GetWindowThreadProcessId(info->hwnd, NULL);
|
||||
if (thread != g_cursorThread) {
|
||||
restoreCursor();
|
||||
hideCursor(thread);
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
// relay the motion
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
case WM_MOUSEMOVE: {
|
||||
const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam;
|
||||
SInt32 x = (SInt32)info->pt.x;
|
||||
SInt32 y = (SInt32)info->pt.y;
|
||||
|
||||
// we want the cursor to be hidden at all times so we
|
||||
// hide the cursor on whatever window has it. but then
|
||||
// we have to show the cursor whenever we leave that
|
||||
// window (or at some later time before we stop relaying).
|
||||
// so check the window with the cursor. if it's not the
|
||||
// same window that had it before then show the cursor
|
||||
// in the last window and hide it in this window.
|
||||
DWORD thread = GetWindowThreadProcessId(info->hwnd, NULL);
|
||||
if (thread != g_cursorThread) {
|
||||
restoreCursor();
|
||||
hideCursor(thread);
|
||||
}
|
||||
|
||||
// relay the motion
|
||||
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// check for mouse inside jump zone
|
||||
|
@ -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) {
|
||||
restoreCursor();
|
||||
PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y);
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -222,15 +217,7 @@ static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam)
|
|||
{
|
||||
if (code >= 0) {
|
||||
if (g_relay) {
|
||||
switch (code) {
|
||||
case HCBT_ACTIVATE:
|
||||
case HCBT_SETFOCUS:
|
||||
// discard unless activating our window
|
||||
if (reinterpret_cast<HWND>(wParam) != g_hwnd) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// do nothing for now. may add something later.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,7 +231,8 @@ static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam)
|
|||
MSG* msg = reinterpret_cast<MSG*>(lParam);
|
||||
if (msg->message == g_wmMouseWheel) {
|
||||
// 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)
|
||||
// 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
|
||||
|
||||
// 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
|
||||
return 1;
|
||||
|
@ -436,16 +425,18 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
|
|||
|
||||
extern "C" {
|
||||
|
||||
int install(HWND hwnd)
|
||||
int install(DWORD threadID)
|
||||
{
|
||||
assert(g_threadID == 0);
|
||||
assert(g_hinstance != NULL);
|
||||
assert(g_keyboard == NULL);
|
||||
assert(g_mouse == NULL);
|
||||
assert(g_cbt == NULL);
|
||||
assert(g_wheelSupport != kWheelOld || g_getMessage == NULL);
|
||||
|
||||
// save window
|
||||
g_hwnd = hwnd;
|
||||
// save thread id. we'll post messages to this thread's
|
||||
// message queue.
|
||||
g_threadID = threadID;
|
||||
|
||||
// set defaults
|
||||
g_relay = false;
|
||||
|
@ -465,7 +456,7 @@ int install(HWND hwnd)
|
|||
g_hinstance,
|
||||
0);
|
||||
if (g_keyboard == NULL) {
|
||||
g_hwnd = NULL;
|
||||
g_threadID = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -478,7 +469,7 @@ int install(HWND hwnd)
|
|||
// uninstall keyboard hook before failing
|
||||
UnhookWindowsHookEx(g_keyboard);
|
||||
g_keyboard = NULL;
|
||||
g_hwnd = NULL;
|
||||
g_threadID = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -493,7 +484,7 @@ int install(HWND hwnd)
|
|||
UnhookWindowsHookEx(g_mouse);
|
||||
g_keyboard = NULL;
|
||||
g_mouse = NULL;
|
||||
g_hwnd = NULL;
|
||||
g_threadID = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -563,7 +554,7 @@ int uninstall(void)
|
|||
g_mouse = NULL;
|
||||
g_cbt = NULL;
|
||||
g_getMessage = NULL;
|
||||
g_hwnd = NULL;
|
||||
g_threadID = 0;
|
||||
|
||||
// show the cursor
|
||||
restoreCursor();
|
||||
|
|
|
@ -20,14 +20,14 @@
|
|||
#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y
|
||||
#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 void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32);
|
||||
typedef void (*SetRelayFunc)(void);
|
||||
|
||||
extern "C" {
|
||||
|
||||
CSYNERGYHOOK_API int install(HWND);
|
||||
CSYNERGYHOOK_API int install(DWORD);
|
||||
CSYNERGYHOOK_API int uninstall(void);
|
||||
CSYNERGYHOOK_API void setZone(UInt32 sides,
|
||||
SInt32 w, SInt32 h, SInt32 jumpZoneSize);
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=makehook - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
MTL=midl.exe
|
||||
|
||||
!IF "$(CFG)" == "makehook - Win32 Release"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "CServer.h"
|
||||
#include "CConfig.h"
|
||||
#include "CLog.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CNetwork.h"
|
||||
#include "CPlatform.h"
|
||||
|
@ -11,15 +12,20 @@
|
|||
#include "stdfstream.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
|
||||
#if defined(CONFIG_PLATFORM_WIN32)
|
||||
#define CONFIG_NAME "synergy.sgc"
|
||||
#define CONFIG_USER_DIR "%HOME%/"
|
||||
#define CONFIG_SYS_DIR ""
|
||||
#elif defined(CONFIG_PLATFORM_UNIX)
|
||||
#define CONFIG_NAME "synergy.conf"
|
||||
#define CONFIG_USER_DIR "~/"
|
||||
#define CONFIG_SYS_DIR "/etc/"
|
||||
#endif
|
||||
|
||||
//
|
||||
|
@ -29,6 +35,8 @@
|
|||
static const char* pname = NULL;
|
||||
static bool s_restartable = 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_logFilter = NULL;
|
||||
static CConfig s_config;
|
||||
|
@ -54,47 +62,97 @@ static void logLock(bool lock)
|
|||
|
||||
|
||||
//
|
||||
// main
|
||||
// platform independent main
|
||||
//
|
||||
|
||||
void realMain()
|
||||
static CServer* s_server = NULL;
|
||||
|
||||
static int realMain(CMutex* mutex)
|
||||
{
|
||||
// initialize threading library
|
||||
CThread::init();
|
||||
// s_serverLock should have mutex locked on entry
|
||||
|
||||
// make logging thread safe
|
||||
CMutex logMutex;
|
||||
s_logMutex = &logMutex;
|
||||
CLog::setLock(&logLock);
|
||||
|
||||
CServer* server = NULL;
|
||||
try {
|
||||
// initialize network library
|
||||
CNetwork::init();
|
||||
// initialize threading library
|
||||
CThread::init();
|
||||
|
||||
// if configuration has no screens then add this system
|
||||
// as the default
|
||||
if (s_config.begin() == s_config.end()) {
|
||||
s_config.addScreen("primary");
|
||||
// make logging thread safe
|
||||
CMutex logMutex;
|
||||
s_logMutex = &logMutex;
|
||||
CLog::setLock(&logLock);
|
||||
|
||||
bool locked = true;
|
||||
try {
|
||||
// initialize network library
|
||||
CNetwork::init();
|
||||
|
||||
// if configuration has no screens then add this system
|
||||
// as the default
|
||||
if (s_config.begin() == s_config.end()) {
|
||||
s_config.addScreen("primary");
|
||||
}
|
||||
|
||||
// create server
|
||||
s_server = new CServer();
|
||||
|
||||
// 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
|
||||
delete s_server;
|
||||
s_server = NULL;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
}
|
||||
catch (...) {
|
||||
// clean up
|
||||
if (!locked && mutex != NULL) {
|
||||
mutex->lock();
|
||||
}
|
||||
delete s_server;
|
||||
s_server = NULL;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
throw;
|
||||
}
|
||||
|
||||
// run server
|
||||
server = new CServer();
|
||||
server->setConfig(s_config);
|
||||
server->run();
|
||||
|
||||
// clean up
|
||||
delete server;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
}
|
||||
catch (...) {
|
||||
delete server;
|
||||
CNetwork::cleanup();
|
||||
CLog::setLock(NULL);
|
||||
s_logMutex = NULL;
|
||||
throw;
|
||||
catch (XBase& e) {
|
||||
log((CLOG_CRIT "failed: %s", e.what()));
|
||||
return 16;
|
||||
}
|
||||
catch (XThread&) {
|
||||
// terminated
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int restartMain()
|
||||
{
|
||||
return realMain(NULL);
|
||||
}
|
||||
|
||||
// invoke realMain and wait for it. if s_restartable then keep
|
||||
// restarting realMain until it returns a terminate code.
|
||||
static int restartableMain()
|
||||
{
|
||||
if (s_restartable) {
|
||||
CPlatform platform;
|
||||
return platform.restart(restartMain, 16);
|
||||
}
|
||||
else {
|
||||
return realMain(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,11 +161,9 @@ void realMain()
|
|||
// command line parsing
|
||||
//
|
||||
|
||||
static void bye()
|
||||
{
|
||||
log((CLOG_PRINT "Try `%s --help' for more information.", pname));
|
||||
exit(1);
|
||||
}
|
||||
#define BYE "\nTry `%s --help' for more information."
|
||||
|
||||
static void (*bye)(int) = &exit;
|
||||
|
||||
static void version()
|
||||
{
|
||||
|
@ -125,12 +181,18 @@ static void version()
|
|||
|
||||
static void help()
|
||||
{
|
||||
CPlatform platform;
|
||||
|
||||
log((CLOG_PRINT
|
||||
"Usage: %s"
|
||||
" [--config <pathname>]"
|
||||
" [--debug <level>]"
|
||||
" [--daemon|--no-daemon]"
|
||||
" [--"DAEMON"|--no-"DAEMON"]"
|
||||
" [--restart|--no-restart]\n"
|
||||
"or\n"
|
||||
" --install\n"
|
||||
" --uninstall\n"
|
||||
"\n"
|
||||
"Start the synergy mouse/keyboard sharing server.\n"
|
||||
"\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"
|
||||
" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n"
|
||||
" DEBUG, DEBUG1, DEBUG2.\n"
|
||||
" -f, --no-daemon run the server in the foreground.\n"
|
||||
" --daemon run the server as a daemon.\n"
|
||||
" -f, --no-"DAEMON" run the server in the foreground.\n"
|
||||
"* --"DAEMON" run the server as a "DAEMON".\n"
|
||||
" -1, --no-restart do not try to restart the server if it fails for\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"
|
||||
" --version display version information and exit.\n"
|
||||
"\n"
|
||||
"By default, the server is a restartable daemon. If no configuration file\n"
|
||||
"pathname is provided then the first of the following to load sets the\n"
|
||||
"configuration:\n"
|
||||
" " CONFIG_USER_DIR CONFIG_NAME "\n"
|
||||
" " CONFIG_SYS_DIR CONFIG_NAME "\n"
|
||||
"* marks defaults.\n"
|
||||
"\n"
|
||||
"If no configuration file pathname is provided then the first of the\n"
|
||||
"following to load sets the configuration:\n"
|
||||
" %s\n"
|
||||
" %s\n"
|
||||
"If no configuration file can be loaded then the configuration uses its\n"
|
||||
"defaults with just the server screen.\n"
|
||||
"\n"
|
||||
"Where log messages go depends on the platform and whether or not the\n"
|
||||
"server is running as a daemon.",
|
||||
pname));
|
||||
|
||||
}
|
||||
|
||||
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& 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;
|
||||
"server is running as a "DAEMON".",
|
||||
pname,
|
||||
platform.addPathComponent(
|
||||
platform.getUserDirectory(),
|
||||
CONFIG_NAME).c_str(),
|
||||
platform.addPathComponent(
|
||||
platform.getSystemDirectory(),
|
||||
CONFIG_NAME).c_str()));
|
||||
}
|
||||
|
||||
static bool isArg(int argi,
|
||||
int argc, char** argv,
|
||||
int argc, const char** argv,
|
||||
const char* name1,
|
||||
const char* name2,
|
||||
int minRequiredParameters = 0)
|
||||
|
@ -198,9 +240,9 @@ static bool isArg(int argi,
|
|||
(name2 != NULL && strcmp(argv[argi], name2) == 0)) {
|
||||
// match. check args left.
|
||||
if (argi + minRequiredParameters >= argc) {
|
||||
log((CLOG_PRINT "%s: missing arguments for `%s'",
|
||||
pname, argv[argi]));
|
||||
bye();
|
||||
log((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
|
||||
pname, argv[argi], pname));
|
||||
bye(2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -209,7 +251,7 @@ static bool isArg(int argi,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void parse(int argc, char** argv)
|
||||
static void parse(int argc, const char** argv)
|
||||
{
|
||||
assert(pname != NULL);
|
||||
assert(argv != NULL);
|
||||
|
@ -228,12 +270,12 @@ static void parse(int argc, char** argv)
|
|||
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
|
||||
s_daemon = false;
|
||||
}
|
||||
|
||||
else if (isArg(i, argc, argv, NULL, "--daemon")) {
|
||||
else if (isArg(i, argc, argv, NULL, "--"DAEMON)) {
|
||||
// daemonize
|
||||
s_daemon = true;
|
||||
}
|
||||
|
@ -250,12 +292,42 @@ static void parse(int argc, char** argv)
|
|||
|
||||
else if (isArg(i, argc, argv, "-h", "--help")) {
|
||||
help();
|
||||
exit(1);
|
||||
bye(0);
|
||||
}
|
||||
|
||||
else if (isArg(i, argc, argv, NULL, "--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)) {
|
||||
|
@ -265,8 +337,9 @@ static void parse(int argc, char** argv)
|
|||
}
|
||||
|
||||
else if (argv[i][0] == '-') {
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i]));
|
||||
bye();
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
|
||||
pname, argv[i], pname));
|
||||
bye(2);
|
||||
}
|
||||
|
||||
else {
|
||||
|
@ -277,101 +350,76 @@ static void parse(int argc, char** argv)
|
|||
|
||||
// no non-option arguments are allowed
|
||||
if (i != argc) {
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i]));
|
||||
bye();
|
||||
log((CLOG_PRINT "%s: unrecognized option `%s'" 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
|
||||
if (!CLog::setFilter(s_logFilter)) {
|
||||
log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter));
|
||||
bye();
|
||||
log((CLOG_PRINT "%s: unrecognized log level `%s'" 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) {
|
||||
// require the user specified file to load correctly
|
||||
loadConfig(s_configFile, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 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) {
|
||||
// load the default configuration if no explicit file given
|
||||
else {
|
||||
// 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.
|
||||
CPlatform platform;
|
||||
bool loaded = false;
|
||||
CString path = platform.getUserDirectory();
|
||||
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
|
||||
int result;
|
||||
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"));
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
// run the server. if running as a daemon then run it in a child
|
||||
// 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
|
||||
// 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 {
|
||||
result = restartableMain();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=server - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
@ -40,6 +40,7 @@ RSC=rc.exe
|
|||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "../Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# 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
|
||||
|
@ -53,7 +54,7 @@ BSC32=bscmake.exe
|
|||
# ADD BSC32 /nologo
|
||||
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 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"
|
||||
|
||||
|
@ -66,6 +67,7 @@ LINK32=link.exe
|
|||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "../Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# 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 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
|
||||
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 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
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=synrgyhk - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "IInterface.h"
|
||||
#include "BasicTypes.h"
|
||||
#include "KeyTypes.h"
|
||||
#include "ClipboardTypes.h"
|
||||
|
||||
class CServer;
|
||||
|
|
|
@ -23,8 +23,8 @@ CFG=synergy - Win32 Debug
|
|||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "millpond"
|
||||
# PROP Scc_LocalPath "."
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
|
|
Loading…
Reference in New Issue