Added switch delay and double-tap options to win32 and added a

tray icon to the client and server that gives status feedback to
the user and allows the user to kill the app.
This commit is contained in:
crs 2003-03-12 22:34:07 +00:00
parent f411df65fb
commit 1d17f865ea
82 changed files with 4420 additions and 396 deletions

View File

@ -0,0 +1,183 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CConfig.h"
#include "ProtocolTypes.h"
#include "CStringUtil.h"
#include "CArch.h"
#include "CAdvancedOptions.h"
#include "LaunchUtil.h"
#include "resource.h"
//
// CAdvancedOptions
//
CAdvancedOptions* CAdvancedOptions::s_singleton = NULL;
CAdvancedOptions::CAdvancedOptions(HWND parent, CConfig* config) :
m_parent(parent),
m_config(config),
m_isClient(false),
m_screenName(ARCH->getHostName()),
m_port(kDefaultPort)
{
assert(s_singleton == NULL);
s_singleton = this;
}
CAdvancedOptions::~CAdvancedOptions()
{
s_singleton = NULL;
}
void
CAdvancedOptions::doModal(bool isClient)
{
// save state
m_isClient = isClient;
// do dialog
DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADVANCED_OPTIONS),
m_parent, dlgProc, (LPARAM)this);
}
CString
CAdvancedOptions::getScreenName() const
{
return m_screenName;
}
int
CAdvancedOptions::getPort() const
{
return m_port;
}
CString
CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const
{
CString cmdLine;
// screen name
if (!m_screenName.empty()) {
cmdLine += " --name ";
cmdLine += m_screenName;
}
// port
char portString[20];
sprintf(portString, "%d", m_port);
if (isClient) {
cmdLine += " ";
cmdLine += serverName;
cmdLine += ":";
cmdLine += portString;
}
else {
cmdLine += " --address :";
cmdLine += portString;
}
return cmdLine;
}
void
CAdvancedOptions::init(HWND hwnd)
{
HWND child;
char buffer[20];
sprintf(buffer, "%d", m_port);
child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT);
SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer);
child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT);
SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_screenName.c_str());
}
bool
CAdvancedOptions::save(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT);
CString name = getWindowText(child);
if (!m_config->isValidScreenName(name)) {
showError(hwnd, CStringUtil::format(
getString(IDS_INVALID_SCREEN_NAME).c_str(),
name.c_str()));
SetFocus(child);
return false;
}
if (!m_isClient && !m_config->isScreen(name)) {
showError(hwnd, CStringUtil::format(
getString(IDS_UNKNOWN_SCREEN_NAME).c_str(),
name.c_str()));
SetFocus(child);
return false;
}
// get and verify port
child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT);
CString portString = getWindowText(child);
int port = atoi(portString.c_str());
if (port < 1 || port > 65535) {
CString defaultPortString = CStringUtil::print("%d", kDefaultPort);
showError(hwnd, CStringUtil::format(
getString(IDS_INVALID_PORT).c_str(),
portString.c_str(),
defaultPortString.c_str()));
SetFocus(child);
return false;
}
// save state
m_screenName = name;
m_port = port;
return true;
}
BOOL
CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
{
switch (message) {
case WM_INITDIALOG:
init(hwnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
if (save(hwnd)) {
EndDialog(hwnd, 0);
}
return TRUE;
case IDCANCEL:
EndDialog(hwnd, 0);
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
BOOL CALLBACK
CAdvancedOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
}

View File

@ -0,0 +1,74 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CADVANCEDOPTIONS_H
#define CADVANCEDOPTIONS_H
#include "CString.h"
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
class CConfig;
//! Advanced options dialog for Microsoft Windows launcher
class CAdvancedOptions {
public:
CAdvancedOptions(HWND parent, CConfig*);
~CAdvancedOptions();
//! @name manipulators
//@{
//! Run dialog
/*!
Display and handle the dialog until closed by the user.
*/
void doModal(bool isClient);
//@}
//! @name accessors
//@{
//! Get the screen name
CString getScreenName() const;
//! Get the port
int getPort() const;
//! Convert options to command line string
CString getCommandLine(bool isClient,
const CString& serverName) const;
//@}
private:
void init(HWND hwnd);
bool save(HWND hwnd);
// message handling
BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
private:
static CAdvancedOptions* s_singleton;
HWND m_parent;
CConfig* m_config;
bool m_isClient;
CString m_screenName;
int m_port;
};
#endif

View File

@ -0,0 +1,211 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CConfig.h"
#include "ProtocolTypes.h"
#include "CStringUtil.h"
#include "CArch.h"
#include "CGlobalOptions.h"
#include "LaunchUtil.h"
#include "resource.h"
static const int s_defaultDelay = 250;
//
// CGlobalOptions
//
CGlobalOptions* CGlobalOptions::s_singleton = NULL;
CGlobalOptions::CGlobalOptions(HWND parent, CConfig* config) :
m_parent(parent),
m_config(config),
m_delayTime(s_defaultDelay),
m_twoTapTime(s_defaultDelay)
{
assert(s_singleton == NULL);
s_singleton = this;
}
CGlobalOptions::~CGlobalOptions()
{
s_singleton = NULL;
}
void
CGlobalOptions::doModal()
{
// do dialog
DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_GLOBAL_OPTIONS),
m_parent, dlgProc, (LPARAM)this);
}
void
CGlobalOptions::init(HWND hwnd)
{
HWND child;
char buffer[30];
// reset options
sprintf(buffer, "%d", m_delayTime);
child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
setItemChecked(child, false);
child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
setWindowText(child, buffer);
sprintf(buffer, "%d", m_twoTapTime);
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
setItemChecked(child, false);
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
setWindowText(child, buffer);
// get the global options
const CConfig::CScreenOptions* options = m_config->getOptions("");
if (options != NULL) {
for (CConfig::CScreenOptions::const_iterator index = options->begin();
index != options->end(); ++index) {
const OptionID id = index->first;
const OptionValue value = index->second;
if (id == kOptionScreenSwitchDelay) {
if (value > 0) {
sprintf(buffer, "%d", value);
child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
setItemChecked(child, true);
child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
setWindowText(child, buffer);
}
}
else if (id == kOptionScreenSwitchTwoTap) {
if (value > 0) {
sprintf(buffer, "%d", value);
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
setItemChecked(child, true);
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
setWindowText(child, buffer);
}
}
}
}
}
bool
CGlobalOptions::save(HWND hwnd)
{
HWND child;
int newDelayTime = 0;
int newTwoTapTime = 0;
// get requested options
child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
if (isItemChecked(child)) {
child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
newDelayTime = getTime(hwnd, child, true);
if (newDelayTime == 0) {
return false;
}
}
else {
child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME);
newDelayTime = getTime(hwnd, child, false);
if (newDelayTime == 0) {
newDelayTime = s_defaultDelay;
}
}
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
if (isItemChecked(child)) {
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
newTwoTapTime = getTime(hwnd, child, true);
if (newTwoTapTime == 0) {
return false;
}
}
else {
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME);
newTwoTapTime = getTime(hwnd, child, false);
if (newTwoTapTime == 0) {
newTwoTapTime = s_defaultDelay;
}
}
// remove existing config options
m_config->removeOption("", kOptionScreenSwitchDelay);
m_config->removeOption("", kOptionScreenSwitchTwoTap);
// add requested options
child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK);
if (isItemChecked(child)) {
m_config->addOption("", kOptionScreenSwitchDelay, newDelayTime);
}
child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK);
if (isItemChecked(child)) {
m_config->addOption("", kOptionScreenSwitchTwoTap, newTwoTapTime);
}
// save last values
m_delayTime = newDelayTime;
m_twoTapTime = newTwoTapTime;
return true;
}
int
CGlobalOptions::getTime(HWND hwnd, HWND child, bool reportError)
{
CString valueString = getWindowText(child);
int value = atoi(valueString.c_str());
if (value < 1) {
if (reportError) {
showError(hwnd, CStringUtil::format(
getString(IDS_INVALID_TIME).c_str(),
valueString.c_str()));
SetFocus(child);
}
return 0;
}
return value;
}
BOOL
CGlobalOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
{
switch (message) {
case WM_INITDIALOG:
init(hwnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
if (save(hwnd)) {
EndDialog(hwnd, 0);
}
return TRUE;
case IDCANCEL:
EndDialog(hwnd, 0);
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
BOOL CALLBACK
CGlobalOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
}

View File

@ -0,0 +1,66 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CGLOBALOPTIONS_H
#define CGLOBALOPTIONS_H
#include "CString.h"
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
class CConfig;
//! Global options dialog for Microsoft Windows launcher
class CGlobalOptions {
public:
CGlobalOptions(HWND parent, CConfig*);
~CGlobalOptions();
//! @name manipulators
//@{
//! Run dialog
/*!
Display and handle the dialog until closed by the user.
*/
void doModal();
//@}
//! @name accessors
//@{
//@}
private:
void init(HWND hwnd);
bool save(HWND hwnd);
int getTime(HWND hwnd, HWND child, bool reportError);
// message handling
BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM);
static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM);
private:
static CGlobalOptions* s_singleton;
HWND m_parent;
CConfig* m_config;
int m_delayTime;
int m_twoTapTime;
};
#endif

View File

@ -103,6 +103,18 @@ enableItem(HWND hwnd, int id, bool enabled)
EnableWindow(GetDlgItem(hwnd, id), enabled); EnableWindow(GetDlgItem(hwnd, id), enabled);
} }
void
setItemChecked(HWND hwnd, bool checked)
{
SendMessage(hwnd, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
}
bool
isItemChecked(HWND hwnd)
{
return (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
}
CString CString
getAppPath(const CString& appName) getAppPath(const CString& appName)
{ {

View File

@ -42,6 +42,9 @@ CString getWindowText(HWND hwnd);
HWND getItem(HWND hwnd, int id); HWND getItem(HWND hwnd, int id);
void enableItem(HWND hwnd, int id, bool enabled); void enableItem(HWND hwnd, int id, bool enabled);
void setItemChecked(HWND, bool);
bool isItemChecked(HWND);
CString getAppPath(const CString& appName); CString getAppPath(const CString& appName);
bool loadConfig(CConfig& config); bool loadConfig(CConfig& config);

View File

@ -16,8 +16,12 @@ DEPTH = ../..
VDEPTH = ./$(VPATH)/$(DEPTH) VDEPTH = ./$(VPATH)/$(DEPTH)
EXTRA_DIST = \ EXTRA_DIST = \
CAdvancedOptions.cpp \
CAdvancedOptions.h \
CAutoStart.cpp \ CAutoStart.cpp \
CAutoStart.h \ CAutoStart.h \
CGlobalOptions.cpp \
CGlobalOptions.h \
LaunchUtil.cpp \ LaunchUtil.cpp \
LaunchUtil.h \ LaunchUtil.h \
launcher.cpp \ launcher.cpp \

View File

@ -13,6 +13,8 @@
*/ */
#include "CConfig.h" #include "CConfig.h"
#include "KeyTypes.h"
#include "OptionTypes.h"
#include "ProtocolTypes.h" #include "ProtocolTypes.h"
#include "CLog.h" #include "CLog.h"
#include "CStringUtil.h" #include "CStringUtil.h"
@ -24,6 +26,8 @@
// these must come after the above because it includes windows.h // these must come after the above because it includes windows.h
#include "LaunchUtil.h" #include "LaunchUtil.h"
#include "CAutoStart.h" #include "CAutoStart.h"
#include "CGlobalOptions.h"
#include "CAdvancedOptions.h"
#define CONFIG_NAME "synergy.sgc" #define CONFIG_NAME "synergy.sgc"
#define CLIENT_APP "synergyc.exe" #define CLIENT_APP "synergyc.exe"
@ -47,10 +51,28 @@ public:
HANDLE m_stop; HANDLE m_stop;
}; };
HINSTANCE s_instance = NULL; struct CModifierInfo {
public:
int m_ctrlID;
const char* m_name;
KeyModifierID m_modifierID;
OptionID m_optionID;
};
static const TCHAR* s_mainClass = TEXT("GoSynergy"); static const CModifierInfo s_modifiers[] = {
static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); { IDC_ADD_MOD_SHIFT, "Shift",
kKeyModifierIDShift, kOptionModifierMapForShift },
{ IDC_ADD_MOD_CTRL, "Ctrl",
kKeyModifierIDControl, kOptionModifierMapForControl },
{ IDC_ADD_MOD_ALT, "Alt",
kKeyModifierIDAlt, kOptionModifierMapForAlt },
{ IDC_ADD_MOD_META, "Meta",
kKeyModifierIDMeta, kOptionModifierMapForMeta },
{ IDC_ADD_MOD_SUPER, "Super",
kKeyModifierIDSuper, kOptionModifierMapForSuper }
};
static const KeyModifierID baseModifier = kKeyModifierIDShift;
static const char* s_debugName[][2] = { static const char* s_debugName[][2] = {
{ TEXT("Error"), "ERROR" }, { TEXT("Error"), "ERROR" },
@ -63,6 +85,14 @@ static const char* s_debugName[][2] = {
}; };
static const int s_defaultDebug = 3; // INFO static const int s_defaultDebug = 3; // INFO
HINSTANCE s_instance = NULL;
static CGlobalOptions* s_globalOptions = NULL;
static CAdvancedOptions* s_advancedOptions = NULL;
static const TCHAR* s_mainClass = TEXT("GoSynergy");
static const TCHAR* s_layoutClass = TEXT("SynergyLayout");
// //
// program arguments // program arguments
// //
@ -127,7 +157,7 @@ bool
isClientChecked(HWND hwnd) isClientChecked(HWND hwnd)
{ {
HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
return (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED); return isItemChecked(child);
} }
static static
@ -476,58 +506,20 @@ static
CString CString
getCommandLine(HWND hwnd, bool testing) getCommandLine(HWND hwnd, bool testing)
{ {
// decide if client or server
const bool isClient = isClientChecked(hwnd);
// get and verify screen name
HWND child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT);
CString name = getWindowText(child);
if (!ARG->m_config.isValidScreenName(name)) {
showError(hwnd, CStringUtil::format(
getString(IDS_INVALID_SCREEN_NAME).c_str(),
name.c_str()));
SetFocus(child);
return CString();
}
if (!isClient && !ARG->m_config.isScreen(name)) {
showError(hwnd, CStringUtil::format(
getString(IDS_UNKNOWN_SCREEN_NAME).c_str(),
name.c_str()));
SetFocus(child);
return CString();
}
// get and verify port
child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT);
CString portString = getWindowText(child);
UInt32 port = (UInt32)atoi(portString.c_str());
if (port < 1 || port > 65535) {
CString defaultPortString = CStringUtil::print("%d", kDefaultPort);
showError(hwnd, CStringUtil::format(
getString(IDS_INVALID_PORT).c_str(),
portString.c_str(),
defaultPortString.c_str()));
SetFocus(child);
return CString();
}
// prepare command line
CString cmdLine; CString cmdLine;
if (testing) {
// constant testing args
cmdLine += " -z --no-restart --no-daemon";
// debug level testing arg // add constant testing args
child = getItem(hwnd, IDC_MAIN_DEBUG); if (testing) {
cmdLine += " --debug "; cmdLine += " -z --no-restart --no-daemon";
cmdLine += s_debugName[SendMessage(child, CB_GETCURSEL, 0, 0)][1];
} }
cmdLine += " --name ";
cmdLine += name; // get the server name
CString server;
bool isClient = isClientChecked(hwnd);
if (isClient) { if (isClient) {
// check server name // check server name
child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
CString server = getWindowText(child); server = getWindowText(child);
if (!ARG->m_config.isValidScreenName(server)) { if (!ARG->m_config.isValidScreenName(server)) {
showError(hwnd, CStringUtil::format( showError(hwnd, CStringUtil::format(
getString(IDS_INVALID_SERVER_NAME).c_str(), getString(IDS_INVALID_SERVER_NAME).c_str(),
@ -551,16 +543,19 @@ getCommandLine(HWND hwnd, bool testing)
if (testing) { if (testing) {
cmdLine += " --no-camp"; cmdLine += " --no-camp";
} }
cmdLine += " ";
cmdLine += server;
cmdLine += ":";
cmdLine += portString;
} }
else {
cmdLine += " --address :"; // debug level
cmdLine += portString; if (testing) {
HWND child = getItem(hwnd, IDC_MAIN_DEBUG);
DWORD debug = SendMessage(child, CB_GETCURSEL, 0, 0);
cmdLine += " --debug ";
cmdLine += s_debugName[debug][1];
} }
// add advanced options
cmdLine += s_advancedOptions->getCommandLine(isClient, server);
return cmdLine; return cmdLine;
} }
@ -711,11 +706,9 @@ initMainWindow(HWND hwnd)
// choose client/server radio buttons // choose client/server radio buttons
HWND child; HWND child;
child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
SendMessage(child, BM_SETCHECK, !configLoaded ? setItemChecked(child, !configLoaded);
BST_CHECKED : BST_UNCHECKED, 0);
child = getItem(hwnd, IDC_MAIN_SERVER_RADIO); child = getItem(hwnd, IDC_MAIN_SERVER_RADIO);
SendMessage(child, BM_SETCHECK, configLoaded ? setItemChecked(child, configLoaded);
BST_CHECKED : BST_UNCHECKED, 0);
// if config is loaded then initialize server controls // if config is loaded then initialize server controls
if (configLoaded) { if (configLoaded) {
@ -729,16 +722,7 @@ initMainWindow(HWND hwnd)
} }
} }
// initialize other controls // debug level
char buffer[256];
sprintf(buffer, "%d", kDefaultPort);
child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT);
SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer);
CString hostname = ARCH->getHostName();
child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT);
SendMessage(child, WM_SETTEXT, 0, (LPARAM)hostname.c_str());
child = getItem(hwnd, IDC_MAIN_DEBUG); child = getItem(hwnd, IDC_MAIN_DEBUG);
for (unsigned int i = 0; i < sizeof(s_debugName) / for (unsigned int i = 0; i < sizeof(s_debugName) /
sizeof(s_debugName[0]); ++i) { sizeof(s_debugName[0]); ++i) {
@ -794,19 +778,32 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
CConfig::CScreenOptions::const_iterator index; CConfig::CScreenOptions::const_iterator index;
child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK);
index = info->m_options.find(kOptionHalfDuplexCapsLock); index = info->m_options.find(kOptionHalfDuplexCapsLock);
if (index != info->m_options.end() && index->second != 0) { setItemChecked(child, (index != info->m_options.end() &&
SendMessage(child, BM_SETCHECK, BST_CHECKED, 0); index->second != 0));
}
else {
SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0);
}
child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK);
index = info->m_options.find(kOptionHalfDuplexNumLock); index = info->m_options.find(kOptionHalfDuplexNumLock);
if (index != info->m_options.end() && index->second != 0) { setItemChecked(child, (index != info->m_options.end() &&
SendMessage(child, BM_SETCHECK, BST_CHECKED, 0); index->second != 0));
// modifier options
for (UInt32 i = 0; i < sizeof(s_modifiers) /
sizeof(s_modifiers[0]); ++i) {
child = getItem(hwnd, s_modifiers[i].m_ctrlID);
// fill in options
for (UInt32 j = 0; j < sizeof(s_modifiers) /
sizeof(s_modifiers[0]); ++j) {
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)s_modifiers[j].m_name);
} }
else {
SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0); // choose current value
index = info->m_options.find(s_modifiers[i].m_optionID);
KeyModifierID id = s_modifiers[i].m_modifierID;
if (index != info->m_options.end()) {
id = index->second;
}
SendMessage(child, CB_SETCURSEL, id - baseModifier, 0);
} }
return TRUE; return TRUE;
@ -883,20 +880,36 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
// save options // save options
child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK);
if (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED) { if (isItemChecked(child)) {
info->m_options[kOptionHalfDuplexCapsLock] = 1; info->m_options[kOptionHalfDuplexCapsLock] = 1;
} }
else { else {
info->m_options.erase(kOptionHalfDuplexCapsLock); info->m_options.erase(kOptionHalfDuplexCapsLock);
} }
child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK);
if (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED) { if (isItemChecked(child)) {
info->m_options[kOptionHalfDuplexNumLock] = 1; info->m_options[kOptionHalfDuplexNumLock] = 1;
} }
else { else {
info->m_options.erase(kOptionHalfDuplexNumLock); info->m_options.erase(kOptionHalfDuplexNumLock);
} }
// save modifier options
child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK);
for (UInt32 i = 0; i < sizeof(s_modifiers) /
sizeof(s_modifiers[0]); ++i) {
child = getItem(hwnd, s_modifiers[i].m_ctrlID);
KeyModifierID id = static_cast<KeyModifierID>(
SendMessage(child, CB_GETCURSEL, 0, 0) +
baseModifier);
if (id != s_modifiers[i].m_modifierID) {
info->m_options[s_modifiers[i].m_optionID] = id;
}
else {
info->m_options.erase(s_modifiers[i].m_optionID);
}
}
// success // success
EndDialog(hwnd, 1); EndDialog(hwnd, 1);
info = NULL; info = NULL;
@ -1065,6 +1078,16 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
return 0; return 0;
} }
break; break;
case IDC_MAIN_OPTIONS:
s_globalOptions->doModal();
enableSaveControls(hwnd);
break;
case IDC_MAIN_ADVANCED:
s_advancedOptions->doModal(isClientChecked(hwnd));
enableSaveControls(hwnd);
break;
} }
default: default:
@ -1076,7 +1099,7 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
int WINAPI int WINAPI
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow)
{ {
CArch arch; CArch arch(instance);
CLOG; CLOG;
CArgs args; CArgs args;
@ -1108,8 +1131,10 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow)
HWND m_mainWindow = CreateDialog(s_instance, HWND m_mainWindow = CreateDialog(s_instance,
MAKEINTRESOURCE(IDD_MAIN), 0, NULL); MAKEINTRESOURCE(IDD_MAIN), 0, NULL);
// prep window // prep windows
initMainWindow(m_mainWindow); initMainWindow(m_mainWindow);
s_globalOptions = new CGlobalOptions(m_mainWindow, &ARG->m_config);
s_advancedOptions = new CAdvancedOptions(m_mainWindow, &ARG->m_config);
// show window // show window
ShowWindow(m_mainWindow, nCmdShow); ShowWindow(m_mainWindow, nCmdShow);

View File

@ -95,10 +95,18 @@ LINK32=link.exe
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File # Begin Source File
SOURCE=.\CAdvancedOptions.cpp
# End Source File
# Begin Source File
SOURCE=.\CAutoStart.cpp SOURCE=.\CAutoStart.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CGlobalOptions.cpp
# End Source File
# Begin Source File
SOURCE=.\launcher.cpp SOURCE=.\launcher.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -115,10 +123,18 @@ SOURCE=.\LaunchUtil.cpp
# PROP Default_Filter "h;hpp;hxx;hm;inl" # PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File # Begin Source File
SOURCE=.\CAdvancedOptions.h
# End Source File
# Begin Source File
SOURCE=.\CAutoStart.h SOURCE=.\CAutoStart.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CGlobalOptions.h
# End Source File
# Begin Source File
SOURCE=.\LaunchUtil.h SOURCE=.\LaunchUtil.h
# End Source File # End Source File
# Begin Source File # Begin Source File

View File

@ -40,11 +40,14 @@
#define IDS_SERVER_IS_CLIENT 36 #define IDS_SERVER_IS_CLIENT 36
#define IDS_ADD_SCREEN 37 #define IDS_ADD_SCREEN 37
#define IDS_EDIT_SCREEN 38 #define IDS_EDIT_SCREEN 38
#define IDS_INVALID_TIME 39
#define IDD_MAIN 101 #define IDD_MAIN 101
#define IDD_ADD 102 #define IDD_ADD 102
#define IDD_WAIT 103 #define IDD_WAIT 103
#define IDI_SYNERGY 104 #define IDI_SYNERGY 104
#define IDD_AUTOSTART 105 #define IDD_AUTOSTART 105
#define IDD_ADVANCED_OPTIONS 106
#define IDD_GLOBAL_OPTIONS 107
#define IDC_MAIN_CLIENT_RADIO 1000 #define IDC_MAIN_CLIENT_RADIO 1000
#define IDC_MAIN_SERVER_RADIO 1001 #define IDC_MAIN_SERVER_RADIO 1001
#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 #define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002
@ -76,18 +79,31 @@
#define IDC_AUTOSTART_INSTALL_USER 1033 #define IDC_AUTOSTART_INSTALL_USER 1033
#define IDC_AUTOSTART_INSTALL_SYSTEM 1034 #define IDC_AUTOSTART_INSTALL_SYSTEM 1034
#define IDC_MAIN_AUTOSTART 1035 #define IDC_MAIN_AUTOSTART 1035
#define IDC_MAIN_DEBUG 1036 #define IDC_MAIN_OPTIONS 1036
#define IDC_ADD_HD_CAPS_CHECK 1037 #define IDC_ADD_HD_CAPS_CHECK 1037
#define IDC_MAIN_ADVANCED 1037
#define IDC_ADD_HD_NUM_CHECK 1038 #define IDC_ADD_HD_NUM_CHECK 1038
#define IDC_ADVANCED_NAME_EDIT 1038
#define IDC_ADVANCED_PORT_EDIT 1039
#define IDC_MAIN_DEBUG 1040
#define IDC_GLOBAL_DELAY_CHECK 1041
#define IDC_GLOBAL_DELAY_TIME 1042
#define IDC_GLOBAL_TWO_TAP_CHECK 1043
#define IDC_ADD_MOD_SHIFT 1043
#define IDC_GLOBAL_TWO_TAP_TIME 1044
#define IDC_ADD_MOD_CTRL 1044
#define IDC_ADD_MOD_ALT 1045
#define IDC_ADD_MOD_META 1046
#define IDC_ADD_MOD_SUPER 1047
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1 #define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_RESOURCE_VALUE 108
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1038 #define _APS_NEXT_CONTROL_VALUE 1044
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

View File

@ -0,0 +1,163 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CClientTaskBarReceiver.h"
#include "CClient.h"
#include "CLock.h"
#include "TMethodJob.h"
#include "CArch.h"
//
// CClientTaskBarReceiver
//
CClientTaskBarReceiver::CClientTaskBarReceiver() :
m_quit(NULL),
m_state(kNotRunning),
m_client(NULL)
{
// create a job for getting notification when the client's
// status changes.
m_job = new TMethodJob<CClientTaskBarReceiver>(this,
&CClientTaskBarReceiver::statusChanged, NULL);
}
CClientTaskBarReceiver::~CClientTaskBarReceiver()
{
if (m_client != NULL) {
m_client->removeStatusJob(m_job);
}
delete m_job;
delete m_quit;
}
void
CClientTaskBarReceiver::setClient(CClient* client)
{
{
CLock lock(&m_mutex);
if (m_client != client) {
if (m_client != NULL) {
m_client->removeStatusJob(m_job);
}
m_client = client;
if (m_client != NULL) {
m_client->addStatusJob(m_job);
}
}
}
ARCH->updateReceiver(this);
}
void
CClientTaskBarReceiver::setState(EState state)
{
{
CLock lock(&m_mutex);
m_state = state;
}
ARCH->updateReceiver(this);
}
void
CClientTaskBarReceiver::setQuitJob(IJob* job)
{
CLock lock(&m_mutex);
delete m_quit;
m_quit = job;
}
CClientTaskBarReceiver::EState
CClientTaskBarReceiver::getState() const
{
return m_state;
}
CClient*
CClientTaskBarReceiver::getClient() const
{
return m_client;
}
void
CClientTaskBarReceiver::lock() const
{
m_mutex.lock();
}
void
CClientTaskBarReceiver::unlock() const
{
m_mutex.unlock();
}
std::string
CClientTaskBarReceiver::getToolTip() const
{
switch (m_state) {
case kNotRunning:
return "Synergy: Not running";
case kNotWorking:
return CString("Synergy: ") + m_errorMessage;
case kNotConnected:
return "Synergy: Waiting for clients";
case kConnected:
return "Synergy: Connected";
default:
return "";
}
}
void
CClientTaskBarReceiver::quit()
{
if (m_quit != NULL) {
m_quit->run();
}
}
void
CClientTaskBarReceiver::onStatusChanged()
{
// do nothing
}
void
CClientTaskBarReceiver::statusChanged(void*)
{
// update our status
switch (m_client->getStatus(&m_errorMessage)) {
case CClient::kNotRunning:
setState(kNotRunning);
break;
case CClient::kRunning:
setState(kConnected);
break;
case CClient::kError:
setState(kNotWorking);
break;
default:
break;
}
// let subclasses have a go
onStatusChanged();
}

View File

@ -0,0 +1,110 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CCLIENTTASKBARRECEIVER_H
#define CCLIENTTASKBARRECEIVER_H
#include "CMutex.h"
#include "CString.h"
#include "IArchTaskBarReceiver.h"
class CClient;
class IJob;
//! Implementation of IArchTaskBarReceiver for the synergy server
class CClientTaskBarReceiver : public IArchTaskBarReceiver {
public:
enum EState {
kNotRunning,
kNotWorking,
kNotConnected,
kConnected,
kMaxState
};
CClientTaskBarReceiver();
virtual ~CClientTaskBarReceiver();
//! @name manipulators
//@{
//! Set server
/*!
Sets the server. The receiver will query state from this server.
*/
void setClient(CClient*);
//! Set state
/*!
Sets the current server state.
*/
void setState(EState);
//! Set the quit job that causes the server to quit
/*!
Set the job that causes the server to quit.
*/
void setQuitJob(IJob* adopted);
//@}
//! @name accessors
//@{
//! Get state
/*!
Returns the current server state. The receiver is not locked
by this call; the caller must do the locking.
*/
EState getState() const;
//! Get server
/*!
Returns the server set by \c setClient().
*/
CClient* getClient() const;
//@}
// IArchTaskBarReceiver overrides
virtual void showStatus() = 0;
virtual void runMenu(int x, int y) = 0;
virtual void primaryAction() = 0;
virtual void lock() const;
virtual void unlock() const;
virtual const Icon getIcon() const = 0;
virtual std::string getToolTip() const;
protected:
void quit();
//! Status change notification
/*!
Called when status changes. The default implementation does
nothing.
*/
virtual void onStatusChanged();
private:
void statusChanged(void*);
private:
CMutex m_mutex;
IJob* m_quit;
EState m_state;
CClient* m_client;
IJob* m_job;
CString m_errorMessage;
};
#endif

View File

@ -0,0 +1,280 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CMSWindowsClientTaskBarReceiver.h"
#include "CClient.h"
#include "BasicTypes.h"
#include "CArch.h"
#include "CArchTaskBarWindows.h"
#include "resource.h"
static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] =
{
IDI_TASKBAR_NOT_RUNNING,
IDI_TASKBAR_NOT_WORKING,
IDI_TASKBAR_NOT_CONNECTED,
IDI_TASKBAR_CONNECTED
};
//
// CMSWindowsClientTaskBarReceiver
//
CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver(
HINSTANCE appInstance) :
CClientTaskBarReceiver(),
m_appInstance(appInstance),
m_window(NULL)
{
for (UInt32 i = 0; i < kMaxState; ++i) {
m_icon[i] = loadIcon(g_stateToIconID[i]);
}
m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
// don't create the window yet. we'll create it on demand. this
// has the side benefit of being created in the thread used for
// the task bar. that's good because it means the existence of
// the window won't prevent changing the main thread's desktop.
// add ourself to the task bar
ARCH->addReceiver(this);
}
CMSWindowsClientTaskBarReceiver::~CMSWindowsClientTaskBarReceiver()
{
ARCH->removeReceiver(this);
for (UInt32 i = 0; i < kMaxState; ++i) {
deleteIcon(m_icon[i]);
}
DestroyMenu(m_menu);
destroyWindow();
}
void
CMSWindowsClientTaskBarReceiver::showStatus()
{
// create the window
createWindow();
// lock self while getting status
lock();
// get the current status
std::string status = getToolTip();
// done getting status
unlock();
// update dialog
HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS);
SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
if (!IsWindowVisible(m_window)) {
// position it by the mouse
POINT cursorPos;
GetCursorPos(&cursorPos);
RECT windowRect;
GetWindowRect(m_window, &windowRect);
int x = cursorPos.x;
int y = cursorPos.y;
int fw = GetSystemMetrics(SM_CXDLGFRAME);
int fh = GetSystemMetrics(SM_CYDLGFRAME);
int ww = windowRect.right - windowRect.left;
int wh = windowRect.bottom - windowRect.top;
int sw = GetSystemMetrics(SM_CXFULLSCREEN);
int sh = GetSystemMetrics(SM_CYFULLSCREEN);
if (fw < 1) {
fw = 1;
}
if (fh < 1) {
fh = 1;
}
if (x + ww - fw > sw) {
x -= ww - fw;
}
else {
x -= fw;
}
if (x < 0) {
x = 0;
}
if (y + wh - fh > sh) {
y -= wh - fh;
}
else {
y -= fh;
}
if (y < 0) {
y = 0;
}
SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh,
SWP_SHOWWINDOW);
}
}
void
CMSWindowsClientTaskBarReceiver::runMenu(int x, int y)
{
// do popup menu. we need a window to pass to TrackPopupMenu().
// the SetForegroundWindow() and SendMessage() calls around
// TrackPopupMenu() are to get the menu to be dismissed when
// another window gets activated and are just one of those
// win32 weirdnesses.
createWindow();
SetForegroundWindow(m_window);
HMENU menu = GetSubMenu(m_menu, 0);
SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE);
int n = TrackPopupMenu(menu,
TPM_NONOTIFY |
TPM_RETURNCMD |
TPM_LEFTBUTTON |
TPM_RIGHTBUTTON,
x, y, 0, m_window, NULL);
SendMessage(m_window, WM_NULL, 0, 0);
// perform the requested operation
switch (n) {
case IDC_TASKBAR_STATUS:
showStatus();
break;
case IDC_TASKBAR_QUIT:
quit();
break;
}
}
void
CMSWindowsClientTaskBarReceiver::primaryAction()
{
showStatus();
}
const IArchTaskBarReceiver::Icon
CMSWindowsClientTaskBarReceiver::getIcon() const
{
return reinterpret_cast<Icon>(m_icon[getState()]);
}
void
CMSWindowsClientTaskBarReceiver::onStatusChanged()
{
if (IsWindowVisible(m_window)) {
showStatus();
}
}
HICON
CMSWindowsClientTaskBarReceiver::loadIcon(UINT id)
{
HANDLE icon = LoadImage(m_appInstance,
MAKEINTRESOURCE(id),
IMAGE_ICON,
0, 0,
LR_DEFAULTCOLOR);
return reinterpret_cast<HICON>(icon);
}
void
CMSWindowsClientTaskBarReceiver::deleteIcon(HICON icon)
{
if (icon != NULL) {
DestroyIcon(icon);
}
}
void
CMSWindowsClientTaskBarReceiver::createWindow()
{
// ignore if already created
if (m_window != NULL) {
return;
}
// get the status dialog
m_window = CreateDialogParam(m_appInstance,
MAKEINTRESOURCE(IDD_TASKBAR_STATUS),
NULL,
&CMSWindowsClientTaskBarReceiver::staticDlgProc,
reinterpret_cast<LPARAM>(
reinterpret_cast<void*>(this)));
// window should appear on top of everything, including (especially)
// the task bar.
DWORD style = GetWindowLong(m_window, GWL_EXSTYLE);
style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
SetWindowLong(m_window, GWL_EXSTYLE, style);
// tell the task bar about this dialog
CArchTaskBarWindows::addDialog(m_window);
}
void
CMSWindowsClientTaskBarReceiver::destroyWindow()
{
if (m_window != NULL) {
CArchTaskBarWindows::removeDialog(m_window);
DestroyWindow(m_window);
m_window = NULL;
}
}
BOOL
CMSWindowsClientTaskBarReceiver::dlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM)
{
switch (msg) {
case WM_INITDIALOG:
// use default focus
return TRUE;
case WM_ACTIVATE:
// hide when another window is activated
if (LOWORD(wParam) == WA_INACTIVE) {
ShowWindow(hwnd, SW_HIDE);
}
break;
}
return FALSE;
}
BOOL CALLBACK
CMSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam)
{
// if msg is WM_INITDIALOG, extract the CMSWindowsClientTaskBarReceiver*
// and put it in the extra window data then forward the call.
CMSWindowsClientTaskBarReceiver* self = NULL;
if (msg == WM_INITDIALOG) {
self = reinterpret_cast<CMSWindowsClientTaskBarReceiver*>(
reinterpret_cast<void*>(lParam));
SetWindowLong(hwnd, GWL_USERDATA, lParam);
}
else {
// get the extra window data and forward the call
LONG data = GetWindowLong(hwnd, GWL_USERDATA);
if (data != 0) {
self = reinterpret_cast<CMSWindowsClientTaskBarReceiver*>(
reinterpret_cast<void*>(data));
}
}
// forward the message
if (self != NULL) {
return self->dlgProc(hwnd, msg, wParam, lParam);
}
else {
return (msg == WM_INITDIALOG) ? TRUE : FALSE;
}
}

View File

@ -0,0 +1,58 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CMSWINDOWSCLIENTTASKBARRECEIVER_H
#define CMSWINDOWSCLIENTTASKBARRECEIVER_H
#define WIN32_LEAN_AND_MEAN
#include "CClientTaskBarReceiver.h"
#include <windows.h>
//! Implementation of CClientTaskBarReceiver for Microsoft Windows
class CMSWindowsClientTaskBarReceiver : public CClientTaskBarReceiver {
public:
CMSWindowsClientTaskBarReceiver(HINSTANCE);
virtual ~CMSWindowsClientTaskBarReceiver();
// IArchTaskBarReceiver overrides
virtual void showStatus();
virtual void runMenu(int x, int y);
virtual void primaryAction();
virtual const Icon getIcon() const;
protected:
// CClientTaskBarReceiver overrides
virtual void onStatusChanged();
private:
HICON loadIcon(UINT);
void deleteIcon(HICON);
void createWindow();
void destroyWindow();
BOOL dlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK
staticDlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam);
private:
HINSTANCE m_appInstance;
HWND m_window;
HMENU m_menu;
HICON m_icon[kMaxState];
};
#endif

View File

@ -0,0 +1,61 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CXWindowsClientTaskBarReceiver.h"
#include "CArch.h"
//
// CXWindowsClientTaskBarReceiver
//
CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver()
{
// add ourself to the task bar
ARCH->addReceiver(this);
}
CXWindowsClientTaskBarReceiver::~CXWindowsClientTaskBarReceiver()
{
ARCH->removeReceiver(this);
}
void
CXWindowsClientTaskBarReceiver::showStatus()
{
// do nothing
}
void
CXWindowsClientTaskBarReceiver::runMenu(int, int)
{
// do nothing
}
void
CXWindowsClientTaskBarReceiver::primaryAction()
{
// do nothing
}
const IArchTaskBarReceiver::Icon
CXWindowsClientTaskBarReceiver::getIcon() const
{
return NULL;
}
void
CXWindowsClientTaskBarReceiver::onStatusChanged()
{
// do nothing
}

View File

@ -0,0 +1,37 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CXWINDOWSCLIENTTASKBARRECEIVER_H
#define CXWINDOWSCLIENTTASKBARRECEIVER_H
#include "CClientTaskBarReceiver.h"
//! Implementation of CClientTaskBarReceiver for X Windows
class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver {
public:
CXWindowsClientTaskBarReceiver();
virtual ~CXWindowsClientTaskBarReceiver();
// IArchTaskBarReceiver overrides
virtual void showStatus();
virtual void runMenu(int x, int y);
virtual void primaryAction();
virtual const Icon getIcon() const;
protected:
// CClientTaskBarReceiver overrides
virtual void onStatusChanged();
};
#endif

View File

@ -16,10 +16,16 @@ DEPTH = ../..
VDEPTH = ./$(VPATH)/$(DEPTH) VDEPTH = ./$(VPATH)/$(DEPTH)
EXTRA_DIST = \ EXTRA_DIST = \
CMSWindowsClientTaskBarReceiver.cpp \
CMSWindowsClientTaskBarReceiver.h \
resource.h \ resource.h \
synergyc.dsp \ synergyc.dsp \
synergyc.ico \ synergyc.ico \
synergyc.rc \ synergyc.rc \
tb_error.ico \
tb_idle.ico \
tb_run.ico \
tb_wait.ico \
$(NULL) $(NULL)
MAINTAINERCLEANFILES = \ MAINTAINERCLEANFILES = \
@ -28,6 +34,10 @@ MAINTAINERCLEANFILES = \
bin_PROGRAMS = synergyc bin_PROGRAMS = synergyc
synergyc_SOURCES = \ synergyc_SOURCES = \
CClientTaskBarReceiver.cpp \
CClientTaskBarReceiver.h \
CXWindowsClientTaskBarReceiver.cpp \
CXWindowsClientTaskBarReceiver.h \
synergyc.cpp \ synergyc.cpp \
$(NULL) $(NULL)
synergyc_LDADD = \ synergyc_LDADD = \

View File

@ -4,6 +4,15 @@
// //
#define IDS_FAILED 1 #define IDS_FAILED 1
#define IDI_SYNERGY 101 #define IDI_SYNERGY 101
#define IDI_TASKBAR_NOT_RUNNING 102
#define IDI_TASKBAR_NOT_WORKING 103
#define IDI_TASKBAR_NOT_CONNECTED 104
#define IDI_TASKBAR_CONNECTED 105
#define IDR_TASKBAR 107
#define IDD_TASKBAR_STATUS 108
#define IDC_TASKBAR_STATUS_STATUS 1000
#define IDC_TASKBAR_QUIT 40003
#define IDC_TASKBAR_STATUS 40004
// Next default values for new objects // Next default values for new objects
// //

View File

@ -25,6 +25,7 @@
#include "CMutex.h" #include "CMutex.h"
#include "CThread.h" #include "CThread.h"
#include "XThread.h" #include "XThread.h"
#include "CFunctionJob.h"
#include "CLog.h" #include "CLog.h"
#include "LogOutputters.h" #include "LogOutputters.h"
#include "CString.h" #include "CString.h"
@ -34,12 +35,15 @@
#define DAEMON_RUNNING(running_) #define DAEMON_RUNNING(running_)
#if WINDOWS_LIKE #if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
#include "CMSWindowsSecondaryScreen.h" #include "CMSWindowsSecondaryScreen.h"
#include "CMSWindowsClientTaskBarReceiver.h"
#include "resource.h" #include "resource.h"
#undef DAEMON_RUNNING #undef DAEMON_RUNNING
#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
#elif UNIX_LIKE #elif UNIX_LIKE
#include "CXWindowsSecondaryScreen.h" #include "CXWindowsSecondaryScreen.h"
#include "CXWindowsClientTaskBarReceiver.h"
#endif #endif
// platform dependent name of a daemon // platform dependent name of a daemon
@ -112,11 +116,46 @@ CSecondaryScreenFactory::create(IScreenReceiver* receiver)
} }
//! CQuitJob
/*!
A job that cancels a given thread.
*/
class CQuitJob : public IJob {
public:
CQuitJob(const CThread& thread);
~CQuitJob();
// IJob overrides
virtual void run();
private:
CThread m_thread;
};
CQuitJob::CQuitJob(const CThread& thread) :
m_thread(thread)
{
// do nothing
}
CQuitJob::~CQuitJob()
{
// do nothing
}
void
CQuitJob::run()
{
m_thread.cancel();
}
// //
// platform independent main // platform independent main
// //
static CClient* s_client = NULL; static CClient* s_client = NULL;
static CClientTaskBarReceiver* s_taskBarReceiver = NULL;
static static
int int
@ -137,6 +176,7 @@ realMain(void)
// open client // open client
try { try {
s_taskBarReceiver->setClient(s_client);
s_client->open(); s_client->open();
opened = true; opened = true;
@ -160,11 +200,10 @@ realMain(void)
DAEMON_RUNNING(false); \ DAEMON_RUNNING(false); \
locked = true; \ locked = true; \
} \ } \
if (s_client != NULL) { \
if (opened) { \ if (opened) { \
s_client->close(); \ s_client->close(); \
} \ } \
} \ s_taskBarReceiver->setClient(NULL); \
delete s_client; \ delete s_client; \
s_client = NULL; \ s_client = NULL; \
} while (false) } while (false)
@ -205,6 +244,43 @@ realMain(void)
return result; return result;
} }
static
void
realMainEntry(void*)
{
CThread::exit(reinterpret_cast<void*>(realMain()));
}
static
int
runMainInThread(void)
{
CThread appThread(new CFunctionJob(&realMainEntry));
try {
#if WINDOWS_LIKE
MSG msg;
while (appThread.waitForEvent(-1.0) == CThread::kEvent) {
// check for a quit event
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
CThread::getCurrentThread().cancel();
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#else
appThread.wait(-1.0);
#endif
return reinterpret_cast<int>(appThread.getResult());
}
catch (XThread&) {
appThread.cancel();
appThread.wait(-1.0);
throw;
}
}
// //
// command line parsing // command line parsing
@ -273,7 +349,7 @@ help()
static static
bool bool
isArg(int argi, int argc, const char** argv, isArg(int argi, int argc, const char* const* argv,
const char* name1, const char* name2, const char* name1, const char* name2,
int minRequiredParameters = 0) int minRequiredParameters = 0)
{ {
@ -294,7 +370,7 @@ isArg(int argi, int argc, const char** argv,
static static
void void
parse(int argc, const char** argv) parse(int argc, const char* const* argv)
{ {
assert(ARG->m_pname != NULL); assert(ARG->m_pname != NULL);
assert(argv != NULL); assert(argv != NULL);
@ -424,16 +500,6 @@ parse(int argc, const char** argv)
} }
} }
static
void
useSystemLog()
{
// redirect log messages
ILogOutputter* logger = new CSystemLogOutputter;
logger->open(DAEMON_NAME);
CLOG->insert(new CStopLogOutputter);
CLOG->insert(logger);
}
// //
// platform dependent entry points // platform dependent entry points
@ -441,8 +507,6 @@ useSystemLog()
#if WINDOWS_LIKE #if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
static bool s_hasImportantLogMessages = false; static bool s_hasImportantLogMessages = false;
// //
@ -494,7 +558,10 @@ static
int int
daemonStartup(int argc, const char** argv) daemonStartup(int argc, const char** argv)
{ {
useSystemLog(); CSystemLogger sysLogger(DAEMON_NAME);
// have to cancel this thread to quit
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// catch errors that would normally exit // catch errors that would normally exit
bye = &byeThrow; bye = &byeThrow;
@ -513,14 +580,62 @@ static
int int
daemonStartup95(int, const char**) daemonStartup95(int, const char**)
{ {
useSystemLog(); CSystemLogger sysLogger(DAEMON_NAME);
return realMain(); return runMainInThread();
}
static
int
run(int argc, char** argv)
{
// windows NT family starts services using no command line options.
// since i'm not sure how to tell the difference between that and
// a user providing no options we'll assume that if there are no
// arguments and we're on NT then we're being invoked as a service.
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
// parse command line
parse(argc, argv);
// daemonize if requested
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup95);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
else {
// cannot start a service from the command line so just
// run normally (except with log messages redirected).
CSystemLogger sysLogger(DAEMON_NAME);
return runMainInThread();
}
}
else {
// run
return runMainInThread();
}
} }
int WINAPI int WINAPI
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{ {
CArch arch; CArch arch(instance);
CLOG; CLOG;
CArgs args; CArgs args;
@ -533,51 +648,26 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
// send PRINT and FATAL output to a message box // send PRINT and FATAL output to a message box
CLOG->insert(new CMessageBoxOutputter); CLOG->insert(new CMessageBoxOutputter);
// windows NT family starts services using no command line options. // make the task bar receiver. the user can control this app
// since i'm not sure how to tell the difference between that and // through the task bar.
// a user providing no options we'll assume that if there are no s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance);
// arguments and we're on NT then we're being invoked as a service. s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
int result = kExitFailed;
try {
result = ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
delete CLOG;
return result;
}
// parse command line
parse(__argc, const_cast<const char**>(__argv));
// daemonize if requested
int result; int result;
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try { try {
result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); // run in foreground or as a daemon
result = run(__argc, __argv);
} }
catch (XArchDaemon& e) { catch (...) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); // note that we don't rethrow thread cancellation. we'll
// be exiting soon so it doesn't matter. what we'd like
// is for everything after this try/catch to be in a
// finally block.
result = kExitFailed; result = kExitFailed;
} }
}
else { // done with task bar receiver
// cannot start a service from the command line so just delete s_taskBarReceiver;
// run normally (except with log messages redirected).
useSystemLog();
result = realMain();
}
}
else {
// run
result = realMain();
}
// let user examine any messages if we're running as a backend // let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting. // by putting up a dialog box before exiting.
@ -598,7 +688,7 @@ static
int int
daemonStartup(int, const char**) daemonStartup(int, const char**)
{ {
useSystemLog(); CSystemLogger sysLogger(DAEMON_NAME);
return realMain(); return realMain();
} }
@ -612,8 +702,13 @@ main(int argc, char** argv)
// get program name // get program name
ARG->m_pname = ARCH->getBasename(argv[0]); ARG->m_pname = ARCH->getBasename(argv[0]);
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = new CXWindowsClientTaskBarReceiver;
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// parse command line // parse command line
parse(argc, const_cast<const char**>(argv)); parse(argc, argv);
// daemonize if requested // daemonize if requested
int result; int result;
@ -630,6 +725,9 @@ main(int argc, char** argv)
result = realMain(); result = realMain();
} }
// done with task bar receiver
delete s_taskBarReceiver;
return result; return result;
} }

View File

@ -94,6 +94,14 @@ LINK32=link.exe
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File # Begin Source File
SOURCE=.\CClientTaskBarReceiver.cpp
# End Source File
# Begin Source File
SOURCE=.\CMSWindowsClientTaskBarReceiver.cpp
# End Source File
# Begin Source File
SOURCE=.\synergyc.cpp SOURCE=.\synergyc.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -106,6 +114,14 @@ SOURCE=.\synergyc.rc
# PROP Default_Filter "h;hpp;hxx;hm;inl" # PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File # Begin Source File
SOURCE=.\CClientTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\CMSWindowsClientTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\resource.h SOURCE=.\resource.h
# End Source File # End Source File
# End Group # End Group
@ -116,6 +132,22 @@ SOURCE=.\resource.h
SOURCE=.\synergyc.ico SOURCE=.\synergyc.ico
# End Source File # End Source File
# Begin Source File
SOURCE=.\tb_error.ico
# End Source File
# Begin Source File
SOURCE=.\tb_idle.ico
# End Source File
# Begin Source File
SOURCE=.\tb_run.ico
# End Source File
# Begin Source File
SOURCE=.\tb_wait.ico
# End Source File
# End Group # End Group
# End Target # End Target
# End Project # End Project

BIN
cmd/synergyc/tb_error.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
cmd/synergyc/tb_idle.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
cmd/synergyc/tb_run.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
cmd/synergyc/tb_wait.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

View File

@ -0,0 +1,300 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CMSWindowsServerTaskBarReceiver.h"
#include "CServer.h"
#include "BasicTypes.h"
#include "CArch.h"
#include "CArchTaskBarWindows.h"
#include "resource.h"
static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] =
{
IDI_TASKBAR_NOT_RUNNING,
IDI_TASKBAR_NOT_WORKING,
IDI_TASKBAR_NOT_CONNECTED,
IDI_TASKBAR_CONNECTED
};
//
// CMSWindowsServerTaskBarReceiver
//
CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver(
HINSTANCE appInstance) :
CServerTaskBarReceiver(),
m_appInstance(appInstance),
m_window(NULL)
{
for (UInt32 i = 0; i < kMaxState; ++i) {
m_icon[i] = loadIcon(g_stateToIconID[i]);
}
m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
// don't create the window yet. we'll create it on demand. this
// has the side benefit of being created in the thread used for
// the task bar. that's good because it means the existence of
// the window won't prevent changing the main thread's desktop.
// add ourself to the task bar
ARCH->addReceiver(this);
}
CMSWindowsServerTaskBarReceiver::~CMSWindowsServerTaskBarReceiver()
{
ARCH->removeReceiver(this);
for (UInt32 i = 0; i < kMaxState; ++i) {
deleteIcon(m_icon[i]);
}
DestroyMenu(m_menu);
destroyWindow();
}
void
CMSWindowsServerTaskBarReceiver::showStatus()
{
// create the window
createWindow();
// lock self while getting status
lock();
// get the current status
std::string status = getToolTip();
// get the connect clients, if any
typedef std::vector<CString> CClientList;
CClientList clients;
CServer* server = getServer();
if (server != NULL) {
server->getClients(clients);
}
// done getting status
unlock();
// update dialog
HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS);
SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS);
SendMessage(child, LB_RESETCONTENT, 0, 0);
for (CClientList::const_iterator index = clients.begin();
index != clients.end(); ) {
const char* client = index->c_str();
if (++index == clients.end()) {
SendMessage(child, LB_ADDSTRING, 0, (LPARAM)client);
}
else {
SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)client);
}
}
if (!IsWindowVisible(m_window)) {
// position it by the mouse
POINT cursorPos;
GetCursorPos(&cursorPos);
RECT windowRect;
GetWindowRect(m_window, &windowRect);
int x = cursorPos.x;
int y = cursorPos.y;
int fw = GetSystemMetrics(SM_CXDLGFRAME);
int fh = GetSystemMetrics(SM_CYDLGFRAME);
int ww = windowRect.right - windowRect.left;
int wh = windowRect.bottom - windowRect.top;
int sw = GetSystemMetrics(SM_CXFULLSCREEN);
int sh = GetSystemMetrics(SM_CYFULLSCREEN);
if (fw < 1) {
fw = 1;
}
if (fh < 1) {
fh = 1;
}
if (x + ww - fw > sw) {
x -= ww - fw;
}
else {
x -= fw;
}
if (x < 0) {
x = 0;
}
if (y + wh - fh > sh) {
y -= wh - fh;
}
else {
y -= fh;
}
if (y < 0) {
y = 0;
}
SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh,
SWP_SHOWWINDOW);
}
}
void
CMSWindowsServerTaskBarReceiver::runMenu(int x, int y)
{
// do popup menu. we need a window to pass to TrackPopupMenu().
// the SetForegroundWindow() and SendMessage() calls around
// TrackPopupMenu() are to get the menu to be dismissed when
// another window gets activated and are just one of those
// win32 weirdnesses.
createWindow();
SetForegroundWindow(m_window);
HMENU menu = GetSubMenu(m_menu, 0);
SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE);
int n = TrackPopupMenu(menu,
TPM_NONOTIFY |
TPM_RETURNCMD |
TPM_LEFTBUTTON |
TPM_RIGHTBUTTON,
x, y, 0, m_window, NULL);
SendMessage(m_window, WM_NULL, 0, 0);
// perform the requested operation
switch (n) {
case IDC_TASKBAR_STATUS:
showStatus();
break;
case IDC_TASKBAR_QUIT:
quit();
break;
}
}
void
CMSWindowsServerTaskBarReceiver::primaryAction()
{
showStatus();
}
const IArchTaskBarReceiver::Icon
CMSWindowsServerTaskBarReceiver::getIcon() const
{
return reinterpret_cast<Icon>(m_icon[getState()]);
}
void
CMSWindowsServerTaskBarReceiver::onStatusChanged()
{
if (IsWindowVisible(m_window)) {
showStatus();
}
}
HICON
CMSWindowsServerTaskBarReceiver::loadIcon(UINT id)
{
HANDLE icon = LoadImage(m_appInstance,
MAKEINTRESOURCE(id),
IMAGE_ICON,
0, 0,
LR_DEFAULTCOLOR);
return reinterpret_cast<HICON>(icon);
}
void
CMSWindowsServerTaskBarReceiver::deleteIcon(HICON icon)
{
if (icon != NULL) {
DestroyIcon(icon);
}
}
void
CMSWindowsServerTaskBarReceiver::createWindow()
{
// ignore if already created
if (m_window != NULL) {
return;
}
// get the status dialog
m_window = CreateDialogParam(m_appInstance,
MAKEINTRESOURCE(IDD_TASKBAR_STATUS),
NULL,
&CMSWindowsServerTaskBarReceiver::staticDlgProc,
reinterpret_cast<LPARAM>(
reinterpret_cast<void*>(this)));
// window should appear on top of everything, including (especially)
// the task bar.
DWORD style = GetWindowLong(m_window, GWL_EXSTYLE);
style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
SetWindowLong(m_window, GWL_EXSTYLE, style);
// tell the task bar about this dialog
CArchTaskBarWindows::addDialog(m_window);
}
void
CMSWindowsServerTaskBarReceiver::destroyWindow()
{
if (m_window != NULL) {
CArchTaskBarWindows::removeDialog(m_window);
DestroyWindow(m_window);
m_window = NULL;
}
}
BOOL
CMSWindowsServerTaskBarReceiver::dlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM)
{
switch (msg) {
case WM_INITDIALOG:
// use default focus
return TRUE;
case WM_ACTIVATE:
// hide when another window is activated
if (LOWORD(wParam) == WA_INACTIVE) {
ShowWindow(hwnd, SW_HIDE);
}
break;
}
return FALSE;
}
BOOL CALLBACK
CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam)
{
// if msg is WM_INITDIALOG, extract the CMSWindowsServerTaskBarReceiver*
// and put it in the extra window data then forward the call.
CMSWindowsServerTaskBarReceiver* self = NULL;
if (msg == WM_INITDIALOG) {
self = reinterpret_cast<CMSWindowsServerTaskBarReceiver*>(
reinterpret_cast<void*>(lParam));
SetWindowLong(hwnd, GWL_USERDATA, lParam);
}
else {
// get the extra window data and forward the call
LONG data = GetWindowLong(hwnd, GWL_USERDATA);
if (data != 0) {
self = reinterpret_cast<CMSWindowsServerTaskBarReceiver*>(
reinterpret_cast<void*>(data));
}
}
// forward the message
if (self != NULL) {
return self->dlgProc(hwnd, msg, wParam, lParam);
}
else {
return (msg == WM_INITDIALOG) ? TRUE : FALSE;
}
}

View File

@ -0,0 +1,58 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CMSWINDOWSSERVERTASKBARRECEIVER_H
#define CMSWINDOWSSERVERTASKBARRECEIVER_H
#define WIN32_LEAN_AND_MEAN
#include "CServerTaskBarReceiver.h"
#include <windows.h>
//! Implementation of CServerTaskBarReceiver for Microsoft Windows
class CMSWindowsServerTaskBarReceiver : public CServerTaskBarReceiver {
public:
CMSWindowsServerTaskBarReceiver(HINSTANCE);
virtual ~CMSWindowsServerTaskBarReceiver();
// IArchTaskBarReceiver overrides
virtual void showStatus();
virtual void runMenu(int x, int y);
virtual void primaryAction();
virtual const Icon getIcon() const;
protected:
// CServerTaskBarReceiver overrides
virtual void onStatusChanged();
private:
HICON loadIcon(UINT);
void deleteIcon(HICON);
void createWindow();
void destroyWindow();
BOOL dlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK
staticDlgProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam);
private:
HINSTANCE m_appInstance;
HWND m_window;
HMENU m_menu;
HICON m_icon[kMaxState];
};
#endif

View File

@ -0,0 +1,171 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CServerTaskBarReceiver.h"
#include "CServer.h"
#include "CLock.h"
#include "TMethodJob.h"
#include "CArch.h"
//
// CServerTaskBarReceiver
//
CServerTaskBarReceiver::CServerTaskBarReceiver() :
m_quit(NULL),
m_state(kNotRunning),
m_server(NULL)
{
// create a job for getting notification when the server's
// status changes.
m_job = new TMethodJob<CServerTaskBarReceiver>(this,
&CServerTaskBarReceiver::statusChanged, NULL);
}
CServerTaskBarReceiver::~CServerTaskBarReceiver()
{
if (m_server != NULL) {
m_server->removeStatusJob(m_job);
}
delete m_job;
delete m_quit;
}
void
CServerTaskBarReceiver::setServer(CServer* server)
{
{
CLock lock(&m_mutex);
if (m_server != server) {
if (m_server != NULL) {
m_server->removeStatusJob(m_job);
}
m_server = server;
if (m_server != NULL) {
m_server->addStatusJob(m_job);
}
}
}
ARCH->updateReceiver(this);
}
void
CServerTaskBarReceiver::setState(EState state)
{
{
CLock lock(&m_mutex);
m_state = state;
}
ARCH->updateReceiver(this);
}
void
CServerTaskBarReceiver::setQuitJob(IJob* job)
{
CLock lock(&m_mutex);
delete m_quit;
m_quit = job;
}
CServerTaskBarReceiver::EState
CServerTaskBarReceiver::getState() const
{
return m_state;
}
CServer*
CServerTaskBarReceiver::getServer() const
{
return m_server;
}
void
CServerTaskBarReceiver::lock() const
{
m_mutex.lock();
}
void
CServerTaskBarReceiver::unlock() const
{
m_mutex.unlock();
}
std::string
CServerTaskBarReceiver::getToolTip() const
{
switch (m_state) {
case kNotRunning:
return "Synergy: Not running";
case kNotWorking:
return CString("Synergy: ") + m_errorMessage;
case kNotConnected:
return "Synergy: Waiting for clients";
case kConnected:
return "Synergy: Connected";
default:
return "";
}
}
void
CServerTaskBarReceiver::quit()
{
if (m_quit != NULL) {
m_quit->run();
}
}
void
CServerTaskBarReceiver::onStatusChanged()
{
// do nothing
}
void
CServerTaskBarReceiver::statusChanged(void*)
{
// update our status
switch (m_server->getStatus(&m_errorMessage)) {
case CServer::kNotRunning:
setState(kNotRunning);
break;
case CServer::kRunning:
if (m_server->getNumClients() > 1)
setState(kConnected);
else
setState(kNotConnected);
break;
case CServer::kServerNameUnknown:
m_errorMessage = "Server name is not in configuration";
setState(kNotWorking);
break;
case CServer::kError:
setState(kNotWorking);
break;
default:
break;
}
// let subclasses have a go
onStatusChanged();
}

View File

@ -0,0 +1,110 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CSERVERTASKBARRECEIVER_H
#define CSERVERTASKBARRECEIVER_H
#include "CMutex.h"
#include "CString.h"
#include "IArchTaskBarReceiver.h"
class CServer;
class IJob;
//! Implementation of IArchTaskBarReceiver for the synergy server
class CServerTaskBarReceiver : public IArchTaskBarReceiver {
public:
enum EState {
kNotRunning,
kNotWorking,
kNotConnected,
kConnected,
kMaxState
};
CServerTaskBarReceiver();
virtual ~CServerTaskBarReceiver();
//! @name manipulators
//@{
//! Set server
/*!
Sets the server. The receiver will query state from this server.
*/
void setServer(CServer*);
//! Set state
/*!
Sets the current server state.
*/
void setState(EState);
//! Set the quit job that causes the server to quit
/*!
Set the job that causes the server to quit.
*/
void setQuitJob(IJob* adopted);
//@}
//! @name accessors
//@{
//! Get state
/*!
Returns the current server state. The receiver is not locked
by this call; the caller must do the locking.
*/
EState getState() const;
//! Get server
/*!
Returns the server set by \c setServer().
*/
CServer* getServer() const;
//@}
// IArchTaskBarReceiver overrides
virtual void showStatus() = 0;
virtual void runMenu(int x, int y) = 0;
virtual void primaryAction() = 0;
virtual void lock() const;
virtual void unlock() const;
virtual const Icon getIcon() const = 0;
virtual std::string getToolTip() const;
protected:
void quit();
//! Status change notification
/*!
Called when status changes. The default implementation does
nothing.
*/
virtual void onStatusChanged();
private:
void statusChanged(void*);
private:
CMutex m_mutex;
IJob* m_quit;
EState m_state;
CServer* m_server;
IJob* m_job;
CString m_errorMessage;
};
#endif

View File

@ -0,0 +1,61 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CXWindowsServerTaskBarReceiver.h"
#include "CArch.h"
//
// CXWindowsServerTaskBarReceiver
//
CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver()
{
// add ourself to the task bar
ARCH->addReceiver(this);
}
CXWindowsServerTaskBarReceiver::~CXWindowsServerTaskBarReceiver()
{
ARCH->removeReceiver(this);
}
void
CXWindowsServerTaskBarReceiver::showStatus()
{
// do nothing
}
void
CXWindowsServerTaskBarReceiver::runMenu(int, int)
{
// do nothing
}
void
CXWindowsServerTaskBarReceiver::primaryAction()
{
// do nothing
}
const IArchTaskBarReceiver::Icon
CXWindowsServerTaskBarReceiver::getIcon() const
{
return NULL;
}
void
CXWindowsServerTaskBarReceiver::onStatusChanged()
{
// do nothing
}

View File

@ -0,0 +1,37 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CXWINDOWSSERVERTASKBARRECEIVER_H
#define CXWINDOWSSERVERTASKBARRECEIVER_H
#include "CServerTaskBarReceiver.h"
//! Implementation of CServerTaskBarReceiver for X Windows
class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver {
public:
CXWindowsServerTaskBarReceiver();
virtual ~CXWindowsServerTaskBarReceiver();
// IArchTaskBarReceiver overrides
virtual void showStatus();
virtual void runMenu(int x, int y);
virtual void primaryAction();
virtual const Icon getIcon() const;
protected:
// CServerTaskBarReceiver overrides
virtual void onStatusChanged();
};
#endif

View File

@ -16,10 +16,16 @@ DEPTH = ../..
VDEPTH = ./$(VPATH)/$(DEPTH) VDEPTH = ./$(VPATH)/$(DEPTH)
EXTRA_DIST = \ EXTRA_DIST = \
CMSWindowsServerTaskBarReceiver.cpp \
CMSWindowsServerTaskBarReceiver.h \
resource.h \ resource.h \
synergys.ico \ synergys.ico \
synergys.dsp \ synergys.dsp \
synergys.rc \ synergys.rc \
tb_error.ico \
tb_idle.ico \
tb_run.ico \
tb_wait.ico \
$(NULL) $(NULL)
MAINTAINERCLEANFILES = \ MAINTAINERCLEANFILES = \
@ -28,6 +34,10 @@ MAINTAINERCLEANFILES = \
bin_PROGRAMS = synergys bin_PROGRAMS = synergys
synergys_SOURCES = \ synergys_SOURCES = \
CServerTaskBarReceiver.cpp \
CServerTaskBarReceiver.h \
CXWindowsServerTaskBarReceiver.cpp \
CXWindowsServerTaskBarReceiver.h \
synergys.cpp \ synergys.cpp \
$(NULL) $(NULL)
synergys_LDADD = \ synergys_LDADD = \

View File

@ -4,14 +4,24 @@
// //
#define IDS_FAILED 1 #define IDS_FAILED 1
#define IDI_SYNERGY 101 #define IDI_SYNERGY 101
#define IDI_TASKBAR_NOT_RUNNING 102
#define IDI_TASKBAR_NOT_WORKING 103
#define IDI_TASKBAR_NOT_CONNECTED 104
#define IDI_TASKBAR_CONNECTED 105
#define IDR_TASKBAR 107
#define IDD_TASKBAR_STATUS 108
#define IDC_TASKBAR_STATUS_STATUS 1000
#define IDC_TASKBAR_STATUS_CLIENTS 1001
#define IDC_TASKBAR_QUIT 40003
#define IDC_TASKBAR_STATUS 40004
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_RESOURCE_VALUE 109
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40005
#define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

View File

@ -24,6 +24,7 @@
#include "CMutex.h" #include "CMutex.h"
#include "CThread.h" #include "CThread.h"
#include "XThread.h" #include "XThread.h"
#include "CFunctionJob.h"
#include "CLog.h" #include "CLog.h"
#include "LogOutputters.h" #include "LogOutputters.h"
#include "CArch.h" #include "CArch.h"
@ -33,12 +34,15 @@
#define DAEMON_RUNNING(running_) #define DAEMON_RUNNING(running_)
#if WINDOWS_LIKE #if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
#include "CMSWindowsPrimaryScreen.h" #include "CMSWindowsPrimaryScreen.h"
#include "CMSWindowsServerTaskBarReceiver.h"
#include "resource.h" #include "resource.h"
#undef DAEMON_RUNNING #undef DAEMON_RUNNING
#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
#elif UNIX_LIKE #elif UNIX_LIKE
#include "CXWindowsPrimaryScreen.h" #include "CXWindowsPrimaryScreen.h"
#include "CXWindowsServerTaskBarReceiver.h"
#endif #endif
// platform dependent name of a daemon // platform dependent name of a daemon
@ -123,11 +127,46 @@ CPrimaryScreenFactory::create(IScreenReceiver* receiver,
} }
//! CQuitJob
/*!
A job that cancels a given thread.
*/
class CQuitJob : public IJob {
public:
CQuitJob(const CThread& thread);
~CQuitJob();
// IJob overrides
virtual void run();
private:
CThread m_thread;
};
CQuitJob::CQuitJob(const CThread& thread) :
m_thread(thread)
{
// do nothing
}
CQuitJob::~CQuitJob()
{
// do nothing
}
void
CQuitJob::run()
{
m_thread.cancel();
}
// //
// platform independent main // platform independent main
// //
static CServer* s_server = NULL; static CServer* s_server = NULL;
static CServerTaskBarReceiver* s_taskBarReceiver = NULL;
static static
int int
@ -168,6 +207,7 @@ realMain(void)
// open server // open server
try { try {
s_taskBarReceiver->setServer(s_server);
s_server->open(); s_server->open();
opened = true; opened = true;
@ -182,11 +222,10 @@ realMain(void)
DAEMON_RUNNING(false); \ DAEMON_RUNNING(false); \
locked = true; \ locked = true; \
} \ } \
if (s_server != NULL) { \
if (opened) { \ if (opened) { \
s_server->close(); \ s_server->close(); \
} \ } \
} \ s_taskBarReceiver->setServer(NULL); \
delete s_server; \ delete s_server; \
s_server = NULL; \ s_server = NULL; \
} while (false) } while (false)
@ -227,6 +266,41 @@ realMain(void)
return result; return result;
} }
static
void
realMainEntry(void*)
{
CThread::exit(reinterpret_cast<void*>(realMain()));
}
static
int
runMainInThread(void)
{
CThread appThread(new CFunctionJob(&realMainEntry));
try {
#if WINDOWS_LIKE
MSG msg;
while (appThread.waitForEvent(-1.0) == CThread::kEvent) {
// check for a quit event
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
CThread::getCurrentThread().cancel();
}
}
}
#else
appThread.wait(-1.0);
#endif
return reinterpret_cast<int>(appThread.getResult());
}
catch (XThread&) {
appThread.cancel();
appThread.wait(-1.0);
throw;
}
}
// //
// command line parsing // command line parsing
@ -329,7 +403,7 @@ PLATFORM_EXTRA
static static
bool bool
isArg(int argi, int argc, const char** argv, isArg(int argi, int argc, const char* const* argv,
const char* name1, const char* name2, const char* name1, const char* name2,
int minRequiredParameters = 0) int minRequiredParameters = 0)
{ {
@ -350,7 +424,7 @@ isArg(int argi, int argc, const char** argv,
static static
void void
parse(int argc, const char** argv) parse(int argc, const char* const* argv)
{ {
assert(ARG->m_pname != NULL); assert(ARG->m_pname != NULL);
assert(argv != NULL); assert(argv != NULL);
@ -553,16 +627,6 @@ loadConfig()
} }
} }
static
void
useSystemLog()
{
// redirect log messages
ILogOutputter* logger = new CSystemLogOutputter;
logger->open(DAEMON_NAME);
CLOG->insert(new CStopLogOutputter);
CLOG->insert(logger);
}
// //
// platform dependent entry points // platform dependent entry points
@ -570,8 +634,6 @@ useSystemLog()
#if WINDOWS_LIKE #if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
static bool s_hasImportantLogMessages = false; static bool s_hasImportantLogMessages = false;
// //
@ -623,7 +685,10 @@ static
int int
daemonStartup(int argc, const char** argv) daemonStartup(int argc, const char** argv)
{ {
useSystemLog(); CSystemLogger sysLogger(DAEMON_NAME);
// have to cancel this thread to quit
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// catch errors that would normally exit // catch errors that would normally exit
bye = &byeThrow; bye = &byeThrow;
@ -645,14 +710,65 @@ static
int int
daemonStartup95(int, const char**) daemonStartup95(int, const char**)
{ {
useSystemLog(); CSystemLogger sysLogger(DAEMON_NAME);
return realMain(); return runMainInThread();
}
static
int
run(int argc, char** argv)
{
// windows NT family starts services using no command line options.
// since i'm not sure how to tell the difference between that and
// a user providing no options we'll assume that if there are no
// arguments and we're on NT then we're being invoked as a service.
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
// parse command line
parse(argc, argv);
// load configuration
loadConfig();
// daemonize if requested
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup95);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
else {
// cannot start a service from the command line so just
// run normally (except with log messages redirected).
CSystemLogger sysLogger(DAEMON_NAME);
return runMainInThread();
}
}
else {
// run
return runMainInThread();
}
} }
int WINAPI int WINAPI
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{ {
CArch arch; CArch arch(instance);
CLOG; CLOG;
CArgs args; CArgs args;
@ -665,54 +781,26 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
// send PRINT and FATAL output to a message box // send PRINT and FATAL output to a message box
CLOG->insert(new CMessageBoxOutputter); CLOG->insert(new CMessageBoxOutputter);
// windows NT family starts services using no command line options. // make the task bar receiver. the user can control this app
// since i'm not sure how to tell the difference between that and // through the task bar.
// a user providing no options we'll assume that if there are no s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance);
// arguments and we're on NT then we're being invoked as a service. s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
int result = kExitFailed;
try {
result = ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
delete CLOG;
return result;
}
// parse command line
parse(__argc, const_cast<const char**>(__argv));
// load configuration
loadConfig();
// daemonize if requested
int result; int result;
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try { try {
result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); // run in foreground or as a daemon
result = run(__argc, __argv);
} }
catch (XArchDaemon& e) { catch (...) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); // note that we don't rethrow thread cancellation. we'll
// be exiting soon so it doesn't matter. what we'd like
// is for everything after this try/catch to be in a
// finally block.
result = kExitFailed; result = kExitFailed;
} }
}
else { // done with task bar receiver
// cannot start a service from the command line so just delete s_taskBarReceiver;
// run normally (except with log messages redirected).
useSystemLog();
result = realMain();
}
}
else {
// run
result = realMain();
}
// let user examine any messages if we're running as a backend // let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting. // by putting up a dialog box before exiting.
@ -733,7 +821,7 @@ static
int int
daemonStartup(int, const char**) daemonStartup(int, const char**)
{ {
useSystemLog(); CSystemLogger sysLogger(DAEMON_NAME);
return realMain(); return realMain();
} }
@ -747,8 +835,13 @@ main(int argc, char** argv)
// get program name // get program name
ARG->m_pname = ARCH->getBasename(argv[0]); ARG->m_pname = ARCH->getBasename(argv[0]);
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = new CXWindowsServerTaskBarReceiver;
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// parse command line // parse command line
parse(argc, const_cast<const char**>(argv)); parse(argc, argv);
// load configuration // load configuration
loadConfig(); loadConfig();
@ -768,6 +861,9 @@ main(int argc, char** argv)
result = realMain(); result = realMain();
} }
// done with task bar receiver
delete s_taskBarReceiver;
return result; return result;
} }

View File

@ -94,6 +94,14 @@ LINK32=link.exe
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File # Begin Source File
SOURCE=.\CMSWindowsServerTaskBarReceiver.cpp
# End Source File
# Begin Source File
SOURCE=.\CServerTaskBarReceiver.cpp
# End Source File
# Begin Source File
SOURCE=.\synergys.cpp SOURCE=.\synergys.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -106,6 +114,14 @@ SOURCE=.\synergys.rc
# PROP Default_Filter "h;hpp;hxx;hm;inl" # PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File # Begin Source File
SOURCE=.\CMSWindowsServerTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\CServerTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\resource.h SOURCE=.\resource.h
# End Source File # End Source File
# End Group # End Group
@ -116,6 +132,22 @@ SOURCE=.\resource.h
SOURCE=.\synergys.ico SOURCE=.\synergys.ico
# End Source File # End Source File
# Begin Source File
SOURCE=.\tb_error.ico
# End Source File
# Begin Source File
SOURCE=.\tb_idle.ico
# End Source File
# Begin Source File
SOURCE=.\tb_run.ico
# End Source File
# Begin Source File
SOURCE=.\tb_wait.ico
# End Source File
# End Group # End Group
# End Target # End Target
# End Project # End Project

BIN
cmd/synergys/tb_error.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
cmd/synergys/tb_idle.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
cmd/synergys/tb_run.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
cmd/synergys/tb_wait.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

View File

@ -23,6 +23,7 @@
#undef ARCH_NETWORK #undef ARCH_NETWORK
#undef ARCH_SLEEP #undef ARCH_SLEEP
#undef ARCH_STRING #undef ARCH_STRING
#undef ARCH_TASKBAR
#undef ARCH_TIME #undef ARCH_TIME
// include appropriate architecture implementation // include appropriate architecture implementation
@ -35,6 +36,7 @@
# include "CArchNetworkWinsock.h" # include "CArchNetworkWinsock.h"
# include "CArchSleepWindows.h" # include "CArchSleepWindows.h"
# include "CArchStringWindows.h" # include "CArchStringWindows.h"
# include "CArchTaskBarWindows.h"
# include "CArchTimeWindows.h" # include "CArchTimeWindows.h"
#elif UNIX_LIKE #elif UNIX_LIKE
# include "CArchConsoleUnix.h" # include "CArchConsoleUnix.h"
@ -47,6 +49,7 @@
# include "CArchNetworkBSD.h" # include "CArchNetworkBSD.h"
# include "CArchSleepUnix.h" # include "CArchSleepUnix.h"
# include "CArchStringUnix.h" # include "CArchStringUnix.h"
# include "CArchTaskBarXWindows.h"
# include "CArchTimeUnix.h" # include "CArchTimeUnix.h"
#endif #endif
@ -82,6 +85,10 @@
# error unsupported platform for string # error unsupported platform for string
#endif #endif
#if !defined(ARCH_TASKBAR)
# error unsupported platform for taskbar
#endif
#if !defined(ARCH_TIME) #if !defined(ARCH_TIME)
# error unsupported platform for time # error unsupported platform for time
#endif #endif
@ -92,7 +99,7 @@
CArch* CArch::s_instance = NULL; CArch* CArch::s_instance = NULL;
CArch::CArch(ARCH_ARGS) CArch::CArch(ARCH_ARGS* args)
{ {
// only once instance of CArch // only once instance of CArch
assert(s_instance == NULL); assert(s_instance == NULL);
@ -108,11 +115,13 @@ CArch::CArch(ARCH_ARGS)
m_time = new ARCH_TIME; m_time = new ARCH_TIME;
m_console = new ARCH_CONSOLE; m_console = new ARCH_CONSOLE;
m_daemon = new ARCH_DAEMON; m_daemon = new ARCH_DAEMON;
m_taskbar = new ARCH_TASKBAR(args);
} }
CArch::~CArch() CArch::~CArch()
{ {
// clean up // clean up
delete m_taskbar;
delete m_daemon; delete m_daemon;
delete m_console; delete m_console;
delete m_time; delete m_time;
@ -337,10 +346,10 @@ CArch::wait(CArchThread thread, double timeout)
return m_mt->wait(thread, timeout); return m_mt->wait(thread, timeout);
} }
bool IArchMultithread::EWaitResult
CArch::waitForEvent(double timeout) CArch::waitForEvent(CArchThread thread, double timeout)
{ {
return m_mt->waitForEvent(timeout); return m_mt->waitForEvent(thread, timeout);
} }
bool bool
@ -577,6 +586,24 @@ CArch::getWideCharEncoding()
return m_string->getWideCharEncoding(); return m_string->getWideCharEncoding();
} }
void
CArch::addReceiver(IArchTaskBarReceiver* receiver)
{
m_taskbar->addReceiver(receiver);
}
void
CArch::removeReceiver(IArchTaskBarReceiver* receiver)
{
m_taskbar->removeReceiver(receiver);
}
void
CArch::updateReceiver(IArchTaskBarReceiver* receiver)
{
m_taskbar->updateReceiver(receiver);
}
double double
CArch::time() CArch::time()
{ {

View File

@ -23,6 +23,7 @@
#include "IArchNetwork.h" #include "IArchNetwork.h"
#include "IArchSleep.h" #include "IArchSleep.h"
#include "IArchString.h" #include "IArchString.h"
#include "IArchTaskBar.h"
#include "IArchTime.h" #include "IArchTime.h"
/*! /*!
@ -31,7 +32,7 @@ This macro evaluates to the singleton CArch object.
*/ */
#define ARCH (CArch::getInstance()) #define ARCH (CArch::getInstance())
#define ARCH_ARGS #define ARCH_ARGS void
//! Delegating mplementation of architecture dependent interfaces //! Delegating mplementation of architecture dependent interfaces
/*! /*!
@ -51,9 +52,10 @@ class CArch : public IArchConsole,
public IArchNetwork, public IArchNetwork,
public IArchSleep, public IArchSleep,
public IArchString, public IArchString,
public IArchTaskBar,
public IArchTime { public IArchTime {
public: public:
CArch(ARCH_ARGS); CArch(ARCH_ARGS* args = NULL);
~CArch(); ~CArch();
// //
@ -114,7 +116,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n); virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout); virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);
@ -164,6 +166,11 @@ public:
virtual EWideCharEncoding virtual EWideCharEncoding
getWideCharEncoding(); getWideCharEncoding();
// IArchTaskBar
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
// IArchTime overrides // IArchTime overrides
virtual double time(); virtual double time();
@ -178,6 +185,7 @@ private:
IArchNetwork* m_net; IArchNetwork* m_net;
IArchSleep* m_sleep; IArchSleep* m_sleep;
IArchString* m_string; IArchString* m_string;
IArchTaskBar* m_taskbar;
IArchTime* m_time; IArchTime* m_time;
}; };

View File

@ -13,6 +13,7 @@
*/ */
#include "CArchConsoleWindows.h" #include "CArchConsoleWindows.h"
#include "IArchMultithread.h"
#include "CArch.h" #include "CArch.h"
#include <cstdio> #include <cstdio>
@ -20,12 +21,12 @@
// CArchConsoleWindows // CArchConsoleWindows
// //
DWORD CArchConsoleWindows::s_thread = 0; CArchThread CArchConsoleWindows::s_thread = 0;
CArchConsoleWindows::CArchConsoleWindows() : CArchConsoleWindows::CArchConsoleWindows() :
m_output(NULL) m_output(NULL)
{ {
s_thread = GetCurrentThreadId(); s_thread = ARCH->newCurrentThread();
m_mutex = ARCH->newMutex(); m_mutex = ARCH->newMutex();
} }
@ -33,6 +34,7 @@ CArchConsoleWindows::CArchConsoleWindows() :
CArchConsoleWindows::~CArchConsoleWindows() CArchConsoleWindows::~CArchConsoleWindows()
{ {
ARCH->closeMutex(m_mutex); ARCH->closeMutex(m_mutex);
ARCH->closeThread(s_thread);
} }
void void
@ -101,7 +103,7 @@ CArchConsoleWindows::getNewlineForConsole()
BOOL WINAPI BOOL WINAPI
CArchConsoleWindows::signalHandler(DWORD) CArchConsoleWindows::signalHandler(DWORD)
{ {
// terminate cleanly and skip remaining handlers // terminate thread and skip remaining handlers
PostThreadMessage(s_thread, WM_QUIT, 0, 0); ARCH->cancelThread(s_thread);
return TRUE; return TRUE;
} }

View File

@ -39,7 +39,7 @@ private:
static BOOL WINAPI signalHandler(DWORD); static BOOL WINAPI signalHandler(DWORD);
private: private:
static DWORD s_thread; static CArchThread s_thread;
CArchMutex m_mutex; CArchMutex m_mutex;
HANDLE m_output; HANDLE m_output;

View File

@ -25,6 +25,7 @@
# include "CArchNetworkWinsock.cpp" # include "CArchNetworkWinsock.cpp"
# include "CArchSleepWindows.cpp" # include "CArchSleepWindows.cpp"
# include "CArchStringWindows.cpp" # include "CArchStringWindows.cpp"
# include "CArchTaskBarWindows.cpp"
# include "CArchTimeWindows.cpp" # include "CArchTimeWindows.cpp"
# include "XArchWindows.cpp" # include "XArchWindows.cpp"
#elif UNIX_LIKE #elif UNIX_LIKE
@ -38,6 +39,7 @@
# include "CArchNetworkBSD.cpp" # include "CArchNetworkBSD.cpp"
# include "CArchSleepUnix.cpp" # include "CArchSleepUnix.cpp"
# include "CArchStringUnix.cpp" # include "CArchStringUnix.cpp"
# include "CArchTaskBarXWindows.cpp"
# include "CArchTimeUnix.cpp" # include "CArchTimeUnix.cpp"
# include "XArchUnix.cpp" # include "XArchUnix.cpp"
#endif #endif

View File

@ -517,11 +517,11 @@ CArchMultithreadPosix::wait(CArchThread target, double timeout)
} }
} }
bool IArchMultithread::EWaitResult
CArchMultithreadPosix::waitForEvent(double /*timeout*/) CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/)
{ {
// not implemented // not implemented
return false; return kTimeout;
} }
bool bool

View File

@ -55,7 +55,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n); virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout); virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);

View File

@ -453,6 +453,89 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout)
} }
} }
IArchMultithread::EWaitResult
CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout)
{
// find current thread. ref the target so it can't go away while
// we're watching it.
lockMutex(m_threadMutex);
CArchThreadImpl* self = findNoRef(GetCurrentThreadId());
assert(self != NULL);
if (target != NULL) {
refThread(target);
}
unlockMutex(m_threadMutex);
// see if we've been cancelled before checking if any events
// are pending.
DWORD result = WaitForSingleObject(self->m_cancel, 0);
if (result == WAIT_OBJECT_0) {
if (target != NULL) {
closeThread(target);
}
testCancelThreadImpl(self);
}
// check if messages are available first. if we don't do this then
// MsgWaitForMultipleObjects() will block even if the queue isn't
// empty if the messages in the queue were there before the last
// call to GetMessage()/PeekMessage().
if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) {
return kEvent;
}
// convert timeout
DWORD t;
if (timeout < 0.0) {
t = INFINITE;
}
else {
t = (DWORD)(1000.0 * timeout);
}
// wait for this thread to be cancelled or for the target thread to
// terminate.
DWORD n = (target == NULL || target == self) ? 1 : 2;
HANDLE handles[2];
handles[0] = self->m_cancel;
handles[1] = (n == 2) ? target->m_exit : NULL;
result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT);
// cancel takes priority
if (result != WAIT_OBJECT_0 + 0 &&
WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) {
result = WAIT_OBJECT_0 + 0;
}
// release target
if (target != NULL) {
closeThread(target);
}
// handle result
switch (result) {
case WAIT_OBJECT_0 + 0:
// this thread was cancelled. does not return.
testCancelThreadImpl(self);
case WAIT_OBJECT_0 + 1:
// target thread terminated
if (n == 2) {
return kExit;
}
// fall through
case WAIT_OBJECT_0 + 2:
// message is available
return kEvent;
default:
// timeout or error
return kTimeout;
}
}
/*
bool bool
CArchMultithreadWindows::waitForEvent(double timeout) CArchMultithreadWindows::waitForEvent(double timeout)
{ {
@ -499,6 +582,7 @@ CArchMultithreadWindows::waitForEvent(double timeout)
return false; return false;
} }
} }
*/
bool bool
CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2)

View File

@ -69,7 +69,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n); virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout); virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);

View File

@ -0,0 +1,518 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CArchTaskBarWindows.h"
#include "IArchTaskBarReceiver.h"
#include "CArch.h"
#include "XArch.h"
#include <string.h>
#include <shellapi.h>
static const UINT kAddReceiver = WM_USER + 10;
static const UINT kRemoveReceiver = WM_USER + 11;
static const UINT kUpdateReceiver = WM_USER + 12;
static const UINT kNotifyReceiver = WM_USER + 13;
static const UINT kFirstReceiverID = WM_USER + 14;
//
// CArchTaskBarWindows
//
CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL;
HINSTANCE CArchTaskBarWindows::s_appInstance = NULL;
CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) :
m_nextID(kFirstReceiverID)
{
// save the singleton instance
s_instance = this;
// save app instance
s_appInstance = reinterpret_cast<HINSTANCE>(appInstance);
// we need a mutex
m_mutex = ARCH->newMutex();
// and a condition variable which uses the above mutex
m_ready = false;
m_condVar = ARCH->newCondVar();
// we're going to want to get a result from the thread we're
// about to create to know if it initialized successfully.
// so we lock the condition variable.
ARCH->lockMutex(m_mutex);
// open a window and run an event loop in a separate thread.
// this has to happen in a separate thread because if we
// create a window on the current desktop with the current
// thread then the current thread won't be able to switch
// desktops if it needs to.
m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this);
// wait for child thread
while (!m_ready) {
ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
}
// ready
ARCH->unlockMutex(m_mutex);
}
CArchTaskBarWindows::~CArchTaskBarWindows()
{
if (m_thread != NULL) {
ARCH->cancelThread(m_thread);
ARCH->wait(m_thread, -1.0);
ARCH->closeThread(m_thread);
}
ARCH->closeCondVar(m_condVar);
ARCH->closeMutex(m_mutex);
s_instance = NULL;
}
void
CArchTaskBarWindows::addDialog(HWND hwnd)
{
// add dialog to added dialogs list
ARCH->lockMutex(s_instance->m_mutex);
s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true));
ARCH->unlockMutex(s_instance->m_mutex);
}
void
CArchTaskBarWindows::removeDialog(HWND hwnd)
{
// mark dialog as removed
ARCH->lockMutex(s_instance->m_mutex);
CDialogs::iterator index = s_instance->m_dialogs.find(hwnd);
if (index != s_instance->m_dialogs.end()) {
index->second = false;
}
s_instance->m_addedDialogs.erase(hwnd);
ARCH->unlockMutex(s_instance->m_mutex);
}
void
CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
{
// ignore bogus receiver
if (receiver == NULL) {
return;
}
// add receiver if necessary
CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
// add it, creating a new message ID for it
CReceiverInfo info;
info.m_id = getNextID();
index = m_receivers.insert(std::make_pair(receiver, info)).first;
// add ID to receiver mapping
m_idTable.insert(std::make_pair(info.m_id, index));
}
// add receiver
PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0);
}
void
CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
{
// find receiver
CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
return;
}
// remove icon. wait for this to finish before returning.
SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0);
// recycle the ID
recycleID(index->second.m_id);
// discard
m_idTable.erase(index->second.m_id);
m_receivers.erase(index);
}
void
CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
{
// find receiver
CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
return;
}
// update icon and tool tip
PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0);
}
UINT
CArchTaskBarWindows::getNextID()
{
if (m_oldIDs.empty()) {
return m_nextID++;
}
UINT id = m_oldIDs.back();
m_oldIDs.pop_back();
return id;
}
void
CArchTaskBarWindows::recycleID(UINT id)
{
m_oldIDs.push_back(id);
}
void
CArchTaskBarWindows::addIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_ADD);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::removeIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
removeIconNoLock(id);
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::updateIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_MODIFY);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::addAllIcons()
{
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) {
modifyIconNoLock(index, NIM_ADD);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::removeAllIcons()
{
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) {
removeIconNoLock(index->second.m_id);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::modifyIconNoLock(
CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage)
{
// get receiver
UINT id = index->second.m_id;
IArchTaskBarReceiver* receiver = index->first;
// lock receiver so icon and tool tip are guaranteed to be consistent
receiver->lock();
// get icon data
HICON icon = reinterpret_cast<HICON>(
const_cast<IArchTaskBarReceiver::Icon>(receiver->getIcon()));
// get tool tip
std::string toolTip = receiver->getToolTip();
// done querying
receiver->unlock();
// prepare to add icon
NOTIFYICONDATA data;
data.cbSize = sizeof(NOTIFYICONDATA);
data.hWnd = m_hwnd;
data.uID = id;
data.uFlags = NIF_MESSAGE;
data.uCallbackMessage = kNotifyReceiver;
data.hIcon = icon;
if (icon != NULL) {
data.uFlags |= NIF_ICON;
}
if (!toolTip.empty()) {
strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip));
data.szTip[sizeof(data.szTip) - 1] = '\0';
data.uFlags |= NIF_TIP;
}
else {
data.szTip[0] = '\0';
}
// add icon
if (Shell_NotifyIcon(taskBarMessage, &data) == 0) {
// failed
}
}
void
CArchTaskBarWindows::removeIconNoLock(UINT id)
{
NOTIFYICONDATA data;
data.cbSize = sizeof(NOTIFYICONDATA);
data.hWnd = m_hwnd;
data.uID = id;
if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) {
// failed
}
}
void
CArchTaskBarWindows::handleIconMessage(
IArchTaskBarReceiver* receiver, LPARAM lParam)
{
// process message
switch (lParam) {
case WM_LBUTTONDOWN:
receiver->showStatus();
break;
case WM_LBUTTONDBLCLK:
receiver->primaryAction();
break;
case WM_RBUTTONUP: {
POINT p;
GetCursorPos(&p);
receiver->runMenu(p.x, p.y);
break;
}
case WM_MOUSEMOVE:
// currently unused
break;
default:
// unused
break;
}
}
bool
CArchTaskBarWindows::processDialogs(MSG* msg)
{
// only one thread can be in this method on any particular object
// at any given time. that's not a problem since only our event
// loop calls this method and there's just one of those.
ARCH->lockMutex(m_mutex);
// remove removed dialogs
m_dialogs.erase(false);
// merge added dialogs into the dialog list
for (CDialogs::const_iterator index = m_addedDialogs.begin();
index != m_addedDialogs.end(); ++index) {
m_dialogs.insert(std::make_pair(index->first, index->second));
}
m_addedDialogs.clear();
ARCH->unlockMutex(m_mutex);
// check message against all dialogs until one handles it.
// note that we don't hold a lock while checking because
// the message is processed and may make calls to this
// object. that's okay because addDialog() and
// removeDialog() don't change the map itself (just the
// values of some elements).
ARCH->lockMutex(m_mutex);
for (CDialogs::const_iterator index = m_dialogs.begin();
index != m_dialogs.end(); ++index) {
if (index->second) {
ARCH->unlockMutex(m_mutex);
if (IsDialogMessage(index->first, msg)) {
return true;
}
ARCH->lockMutex(m_mutex);
}
}
ARCH->unlockMutex(m_mutex);
return false;
}
LRESULT
CArchTaskBarWindows::wndProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case kNotifyReceiver: {
// lookup receiver
CIDToReceiverMap::const_iterator index = m_idTable.find(wParam);
if (index != m_idTable.end()) {
IArchTaskBarReceiver* receiver = index->second->first;
handleIconMessage(receiver, lParam);
return 0;
}
break;
}
case kAddReceiver:
addIcon(wParam);
break;
case kRemoveReceiver:
removeIcon(wParam);
break;
case kUpdateReceiver:
updateIcon(wParam);
break;
default:
if (msg == m_taskBarRestart) {
// task bar was recreated so re-add our icons
addAllIcons();
}
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK
CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
// if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put
// it in the extra window data then forward the call.
CArchTaskBarWindows* self = NULL;
if (msg == WM_NCCREATE) {
CREATESTRUCT* createInfo;
createInfo = reinterpret_cast<CREATESTRUCT*>(lParam);
self = reinterpret_cast<CArchTaskBarWindows*>(
createInfo->lpCreateParams);
SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(self));
}
else {
// get the extra window data and forward the call
LONG data = GetWindowLong(hwnd, 0);
if (data != 0) {
self = reinterpret_cast<CArchTaskBarWindows*>(
reinterpret_cast<void*>(data));
}
}
// forward the message
if (self != NULL) {
return self->wndProc(hwnd, msg, wParam, lParam);
}
else {
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
void
CArchTaskBarWindows::threadMainLoop()
{
// register the task bar restart message
m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
// register a window class
WNDCLASSEX classInfo;
classInfo.cbSize = sizeof(classInfo);
classInfo.style = CS_NOCLOSE;
classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc;
classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*);
classInfo.hInstance = s_appInstance;
classInfo.hIcon = NULL;
classInfo.hCursor = NULL;
classInfo.hbrBackground = NULL;
classInfo.lpszMenuName = NULL;
classInfo.lpszClassName = TEXT("SynergyTaskBar");
classInfo.hIconSm = NULL;
ATOM windowClass = RegisterClassEx(&classInfo);
// create window
m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
reinterpret_cast<LPCTSTR>(windowClass),
TEXT("Synergy Task Bar"),
WS_POPUP,
0, 0, 1, 1,
NULL,
NULL,
s_appInstance,
reinterpret_cast<void*>(this));
// signal ready
ARCH->lockMutex(m_mutex);
m_ready = true;
ARCH->broadcastCondVar(m_condVar);
ARCH->unlockMutex(m_mutex);
// handle failure
if (m_hwnd == NULL) {
UnregisterClass((LPCTSTR)windowClass, s_appInstance);
return;
}
try {
// main loop
MSG msg;
for (;;) {
// wait for message
if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) {
continue;
}
// peek for message and remove it. we don't GetMessage()
// because we should never block here, only in waitForEvent().
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
continue;
}
// check message against dialogs
if (!processDialogs(&msg)) {
// process message
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
catch (XThread&) {
// clean up
removeAllIcons();
DestroyWindow(m_hwnd);
UnregisterClass((LPCTSTR)windowClass, s_appInstance);
throw;
}
}
void*
CArchTaskBarWindows::threadEntry(void* self)
{
reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
return NULL;
}

View File

@ -0,0 +1,110 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CARCHTASKBARWINDOWS_H
#define CARCHTASKBARWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "IArchTaskBar.h"
#include "IArchMultithread.h"
#include "stdmap.h"
#include "stdvector.h"
#include <windows.h>
#define ARCH_TASKBAR CArchTaskBarWindows
//! Win32 implementation of IArchTaskBar
class CArchTaskBarWindows : public IArchTaskBar {
public:
CArchTaskBarWindows(void*);
virtual ~CArchTaskBarWindows();
//! Add a dialog window
/*!
Tell the task bar event loop about a dialog. Win32 annoyingly
requires messages destined for modeless dialog boxes to be
dispatched differently than other messages.
*/
static void addDialog(HWND);
//! Remove a dialog window
/*!
Remove a dialog window added via \c addDialog().
*/
static void removeDialog(HWND);
// IArchTaskBar overrides
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
private:
class CReceiverInfo {
public:
UINT m_id;
};
typedef std::map<IArchTaskBarReceiver*, CReceiverInfo> CReceiverToInfoMap;
typedef std::map<UINT, CReceiverToInfoMap::iterator> CIDToReceiverMap;
typedef std::vector<UINT> CIDStack;
typedef std::map<HWND, bool> CDialogs;
UINT getNextID();
void recycleID(UINT);
void addIcon(UINT);
void removeIcon(UINT);
void updateIcon(UINT);
void addAllIcons();
void removeAllIcons();
void modifyIconNoLock(CReceiverToInfoMap::const_iterator,
DWORD taskBarMessage);
void removeIconNoLock(UINT id);
void handleIconMessage(IArchTaskBarReceiver*, LPARAM);
bool processDialogs(MSG*);
LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK
staticWndProc(HWND, UINT, WPARAM, LPARAM);
void threadMainLoop();
static void* threadEntry(void*);
private:
static CArchTaskBarWindows* s_instance;
static HINSTANCE s_appInstance;
// multithread data
CArchMutex m_mutex;
CArchCond m_condVar;
bool m_ready;
int m_result;
CArchThread m_thread;
// child thread data
HWND m_hwnd;
UINT m_taskBarRestart;
// shared data
CReceiverToInfoMap m_receivers;
CIDToReceiverMap m_idTable;
CIDStack m_oldIDs;
UINT m_nextID;
// dialogs
CDialogs m_dialogs;
CDialogs m_addedDialogs;
};
#endif

View File

@ -0,0 +1,47 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CArchTaskBarXWindows.h"
//
// CArchTaskBarXWindows
//
CArchTaskBarXWindows::CArchTaskBarXWindows(void*)
{
// do nothing
}
CArchTaskBarXWindows::~CArchTaskBarXWindows()
{
// do nothing
}
void
CArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}
void
CArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}
void
CArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}

View File

@ -0,0 +1,34 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CARCHTASKBARXWINDOWS_H
#define CARCHTASKBARXWINDOWS_H
#include "IArchTaskBar.h"
#define ARCH_TASKBAR CArchTaskBarXWindows
//! X11 implementation of IArchTaskBar
class CArchTaskBarXWindows : public IArchTaskBar {
public:
CArchTaskBarXWindows(void*);
virtual ~CArchTaskBarXWindows();
// IArchTaskBar overrides
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
};
#endif

View File

@ -67,6 +67,13 @@ synergy. Each architecture must implement this interface.
*/ */
class IArchMultithread : public IInterface { class IArchMultithread : public IInterface {
public: public:
//! Result of waitForEvent()
enum EWaitResult {
kEvent, //!< An event is pending
kExit, //!< Thread exited
kTimeout //!< Wait timed out
};
//! Type of thread entry point //! Type of thread entry point
typedef void* (*ThreadFunc)(void*); typedef void* (*ThreadFunc)(void*);
//! Type of thread identifier //! Type of thread identifier
@ -102,10 +109,12 @@ public:
//! Wait on a condition variable //! Wait on a condition variable
/*! /*!
Waiting on a conditation variable for up to \c timeout seconds. Wait on a conditation variable for up to \c timeout seconds.
If \c timeout is < 0 then there is no timeout. The mutex must If \c timeout is < 0 then there is no timeout. The mutex must
be locked when this method is called. The mutex is unlocked be locked when this method is called. The mutex is unlocked
during the wait and locked again before returning. during the wait and locked again before returning. Returns
true if the condition variable was signalled and false on
timeout.
(Cancellation point) (Cancellation point)
*/ */
@ -206,14 +215,18 @@ public:
//! Wait for a user event //! Wait for a user event
/*! /*!
Waits for up to \c timeout seconds for a pending user event. Waits for up to \c timeout seconds for a pending user event or
Returns true if an event occurred, false otherwise. \c thread to exit (normally or by cancellation). Waits forever
if \c timeout < 0. Returns kEvent if an event occurred, kExit
if \c thread exited, or kTimeout if the timeout expired. If
\c thread is NULL then it doesn't wait for any thread to exit
and it will not return kExit.
This method is not required by all platforms. This method is not required by all platforms.
(Cancellation point) (Cancellation point)
*/ */
virtual bool waitForEvent(double timeout) = 0; virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
//! Compare threads //! Compare threads
/*! /*!

63
lib/arch/IArchTaskBar.h Normal file
View File

@ -0,0 +1,63 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef IARCHTASKBAR_H
#define IARCHTASKBAR_H
#include "IInterface.h"
class IArchTaskBarReceiver;
//! Interface for architecture dependent task bar control
/*!
This interface defines the task bar icon operations required
by synergy. Each architecture must implement this interface
though each operation can be a no-op.
*/
class IArchTaskBar : public IInterface {
public:
// Event data is architecture dependent
typedef void* Event;
//! @name manipulators
//@{
//! Add a receiver
/*!
Add a receiver object to be notified of user and application
events. This should be called before other methods. When
the receiver is added to the task bar, its icon appears on
the task bar.
*/
virtual void addReceiver(IArchTaskBarReceiver*) = 0;
//! Remove a receiver
/*!
Remove a receiver object from the task bar. This removes the
icon from the task bar.
*/
virtual void removeReceiver(IArchTaskBarReceiver*) = 0;
//! Update a receiver
/*!
Updates the display of the receiver on the task bar. This
should be called when the receiver appearance may have changed
(e.g. it's icon or tool tip has changed).
*/
virtual void updateReceiver(IArchTaskBarReceiver*) = 0;
//@}
};
#endif

View File

@ -0,0 +1,90 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef IARCHTASKBARRECEIVER_H
#define IARCHTASKBARRECEIVER_H
#include "IInterface.h"
#include "stdstring.h"
//! Interface for architecture dependent task bar event handling
/*!
This interface defines the task bar icon event handlers required
by synergy. Each architecture must implement this interface
though each operation can be a no-op.
*/
class IArchTaskBarReceiver : public IInterface {
public:
// Icon data is architecture dependent
typedef void* Icon;
//! @name manipulators
//@{
//! Show status window
/*!
Open a window displaying current status. This should return
immediately without waiting for the window to be closed.
*/
virtual void showStatus() = 0;
//! Popup menu
/*!
Popup a menu of operations at or around \c x,y and perform the
chosen operation.
*/
virtual void runMenu(int x, int y) = 0;
//! Perform primary action
/*!
Perform the primary (default) action.
*/
virtual void primaryAction() = 0;
//@}
//! @name accessors
//@{
//! Lock receiver
/*!
Locks the receiver from changing state. The receiver should be
locked when querying it's state to ensure consistent results.
Each call to \c lock() must have a matching \c unlock() and
locks cannot be nested.
*/
virtual void lock() const = 0;
//! Unlock receiver
virtual void unlock() const = 0;
//! Get icon
/*!
Returns the icon to display in the task bar. The interface
to set the icon is left to subclasses. Getting and setting
the icon must be thread safe.
*/
virtual const Icon getIcon() const = 0;
//! Get tooltip
/*!
Returns the tool tip to display in the task bar. The interface
to set the tooltip is left to sublclasses. Getting and setting
the icon must be thread safe.
*/
virtual std::string getToolTip() const = 0;
//@}
};
#endif

View File

@ -26,6 +26,7 @@ EXTRA_DIST = \
CArchNetworkWinsock.cpp \ CArchNetworkWinsock.cpp \
CArchSleepWindows.cpp \ CArchSleepWindows.cpp \
CArchStringWindows.cpp \ CArchStringWindows.cpp \
CArchTaskBarWindows.cpp \
CArchTimeWindows.cpp \ CArchTimeWindows.cpp \
XArchWindows.cpp \ XArchWindows.cpp \
CArchConsoleWindows.h \ CArchConsoleWindows.h \
@ -37,6 +38,7 @@ EXTRA_DIST = \
CArchNetworkWinsock.h \ CArchNetworkWinsock.h \
CArchSleepWindows.h \ CArchSleepWindows.h \
CArchStringWindows.h \ CArchStringWindows.h \
CArchTaskBarWindows.h \
CArchTimeWindows.h \ CArchTimeWindows.h \
XArchWindows.h \ XArchWindows.h \
$(NULL) $(NULL)
@ -59,6 +61,8 @@ libarch_a_SOURCES = \
IArchNetwork.h \ IArchNetwork.h \
IArchSleep.h \ IArchSleep.h \
IArchString.h \ IArchString.h \
IArchTaskBar.h \
IArchTaskBarReceiver.h \
IArchTime.h \ IArchTime.h \
XArch.h \ XArch.h \
$(NULL) $(NULL)
@ -72,6 +76,7 @@ EXTRA_libarch_a_SOURCES = \
CArchNetworkBSD.cpp \ CArchNetworkBSD.cpp \
CArchSleepUnix.cpp \ CArchSleepUnix.cpp \
CArchStringUnix.cpp \ CArchStringUnix.cpp \
CArchTaskBarXWindows.cpp \
CArchTimeUnix.cpp \ CArchTimeUnix.cpp \
CMultibyte.cpp \ CMultibyte.cpp \
CMultibyteOS.cpp \ CMultibyteOS.cpp \
@ -87,6 +92,7 @@ EXTRA_libarch_a_SOURCES = \
CArchNetworkBSD.h \ CArchNetworkBSD.h \
CArchSleepUnix.h \ CArchSleepUnix.h \
CArchStringUnix.h \ CArchStringUnix.h \
CArchTaskBarXWindows.h \
CArchTimeUnix.h \ CArchTimeUnix.h \
XArchUnix.h \ XArchUnix.h \
$(NULL) $(NULL)

View File

@ -115,6 +115,10 @@ SOURCE=.\CArchDaemonWindows.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CArchFileWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchImpl.h SOURCE=.\CArchImpl.h
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -143,6 +147,10 @@ SOURCE=.\CArchStringWindows.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CArchTaskBarWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchTimeWindows.h SOURCE=.\CArchTimeWindows.h
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -151,6 +159,14 @@ SOURCE=.\IArchConsole.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\IArchDaemon.h
# End Source File
# Begin Source File
SOURCE=.\IArchFile.h
# End Source File
# Begin Source File
SOURCE=.\IArchLog.h SOURCE=.\IArchLog.h
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -171,6 +187,14 @@ SOURCE=.\IArchString.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\IArchTaskBar.h
# End Source File
# Begin Source File
SOURCE=.\IArchTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\IArchTime.h SOURCE=.\IArchTime.h
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -232,6 +256,11 @@ SOURCE=.\CArchStringWindows.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CArchTaskBarWindows.cpp
# PROP Exclude_From_Build 1
# End Source File
# Begin Source File
SOURCE=.\CArchTimeWindows.cpp SOURCE=.\CArchTimeWindows.cpp
# PROP Exclude_From_Build 1 # PROP Exclude_From_Build 1
# End Source File # End Source File

113
lib/base/CJobList.cpp Normal file
View File

@ -0,0 +1,113 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CJobList.h"
#include "IJob.h"
#include "CArch.h"
//
// CJobList
//
CJobList::CJobList()
{
m_mutex = ARCH->newMutex();
}
CJobList::~CJobList()
{
ARCH->closeMutex(m_mutex);
}
void
CJobList::addJob(IJob* job)
{
// ignore bogus job
if (job == NULL) {
return;
}
// make a temporary list with the job. later we'll splice this
// list into our jobs list. since splice() never throws we
// don't have to try/catch to unlock the mutex.
CJobs tmpList;
tmpList.push_front(job);
// add job to list
ARCH->lockMutex(m_mutex);
m_jobs.splice(m_jobs.begin(), tmpList);
ARCH->unlockMutex(m_mutex);
}
void
CJobList::removeJob(IJob* job)
{
if (job != NULL) {
ARCH->lockMutex(m_mutex);
m_jobs.remove(job);
ARCH->unlockMutex(m_mutex);
}
}
void
CJobList::runJobs() const
{
// this is a little tricky. to allow any number of threads to
// traverse the list while jobs are added and removed while
// not holding the mutex when running a job (so other threads
// or the running job can add and remove jobs), we insert a
// new element into the list. this element has a NULL job and
// is a "safe place" while we traverse the list. the safe place
// is inserted at the start of the list (with the mutex locked)
// then, for each job, we lock the mutex and move the safe place
// past the next job, unlock the mutex, run the job and repeat
// until there are no more jobs. the safe place will not be
// removed by any other thread and is after every job that has
// been run and before every job that still needs to run. when
// all the jobs have been run we remove the safe place.
// add the safe place
CJobs tmpList;
tmpList.push_front(NULL);
ARCH->lockMutex(m_mutex);
m_jobs.splice(m_jobs.begin(), tmpList);
CJobs::iterator safePlace = m_jobs.begin();
// find the next non-NULL job (NULL jobs are safe places)
CJobs::iterator next = safePlace;
while (next != m_jobs.end() && *next == NULL)
++next;
while (next != m_jobs.end()) {
// found a job. run it without holding a lock. note the
// race condition here: we release the lock, allowing
// removeJob() to remove this job before we run the job.
// therefore the caller cannot safely destroy the job
// until all runJobs() complete.
IJob* job = *next;
++next;
m_jobs.splice(next, m_jobs, safePlace);
ARCH->unlockMutex(m_mutex);
job->run();
// next real job
ARCH->lockMutex(m_mutex);
next = safePlace;
while (next != m_jobs.end() && *next == NULL)
++next;
}
// remove the safe place
m_jobs.erase(safePlace);
ARCH->unlockMutex(m_mutex);
}

72
lib/base/CJobList.h Normal file
View File

@ -0,0 +1,72 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CJOBLIST_H
#define CJOBLIST_H
#include "IArchMultithread.h"
#include "stdlist.h"
class IJob;
class CJobList {
public:
CJobList();
~CJobList();
//! @name manipulators
//@{
//! Add a job
/*!
Add a job to the list. The client keeps ownership of the job.
Jobs can be safely added while \c runJobs() is executing.
*/
void addJob(IJob*);
//! Remove a job
/*!
Remove a job from the list. The client keeps ownership of the job.
Jobs can be safely removed while \c runJobs() is executing.
*/
void removeJob(IJob*);
//@}
//! @name accessors
//@{
//! Run all jobs
/*!
Run all jobs in the list. Any number of threads can call
\c runJobs() at once. Jobs can be added and removed while
\c runJobs() is executing in the same or another thread.
Any job added after \c runJobs() starts will not be run
by that call to runJobs(). Destroying a removed job
while \c runJobs() is executing is not safe unless the
removed completed before \c runJobs() started.
*/
void runJobs() const;
//@}
private:
typedef std::list<IJob*> CJobs;
typedef CJobs::iterator iterator;
CArchMutex m_mutex;
mutable CJobs m_jobs;
};
#endif

View File

@ -156,3 +156,26 @@ CSystemLogOutputter::getNewline() const
{ {
return ""; return "";
} }
//
// CSystemLogger
//
CSystemLogger::CSystemLogger(const char* title)
{
// redirect log messages
m_syslog = new CSystemLogOutputter;
m_stop = new CStopLogOutputter;
m_syslog->open(title);
CLOG->insert(m_stop);
CLOG->insert(m_syslog);
}
CSystemLogger::~CSystemLogger()
{
CLOG->remove(m_syslog);
CLOG->remove(m_stop);
delete m_stop;
delete m_syslog;
}

View File

@ -68,4 +68,22 @@ public:
virtual const char* getNewline() const; virtual const char* getNewline() const;
}; };
//! Write log to system log only
/*!
Creating an object of this type inserts a CStopLogOutputter followed
by a CSystemLogOutputter into CLog. The destructor removes those
outputters. Add one of these to any scope that needs to write to
the system log (only) and restore the old outputters when exiting
the scope.
*/
class CSystemLogger {
public:
CSystemLogger(const char* title);
~CSystemLogger();
private:
ILogOutputter* m_syslog;
ILogOutputter* m_stop;
};
#endif #endif

View File

@ -26,6 +26,7 @@ MAINTAINERCLEANFILES = \
noinst_LIBRARIES = libbase.a noinst_LIBRARIES = libbase.a
libbase_a_SOURCES = \ libbase_a_SOURCES = \
CFunctionJob.cpp \ CFunctionJob.cpp \
CJobList.cpp \
CLog.cpp \ CLog.cpp \
CStopwatch.cpp \ CStopwatch.cpp \
CStringUtil.cpp \ CStringUtil.cpp \
@ -33,6 +34,7 @@ libbase_a_SOURCES = \
LogOutputters.cpp \ LogOutputters.cpp \
XBase.cpp \ XBase.cpp \
CFunctionJob.h \ CFunctionJob.h \
CJobList.h \
CLog.h \ CLog.h \
CStopwatch.h \ CStopwatch.h \
CString.h \ CString.h \

View File

@ -91,6 +91,10 @@ SOURCE=.\CFunctionJob.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CJobList.cpp
# End Source File
# Begin Source File
SOURCE=.\CLog.cpp SOURCE=.\CLog.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -123,6 +127,10 @@ SOURCE=.\CFunctionJob.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CJobList.h
# End Source File
# Begin Source File
SOURCE=.\CLog.h SOURCE=.\CLog.h
# End Source File # End Source File
# Begin Source File # Begin Source File

View File

@ -52,7 +52,8 @@ CClient::CClient(const CString& clientName) :
m_streamFilterFactory(NULL), m_streamFilterFactory(NULL),
m_session(NULL), m_session(NULL),
m_active(false), m_active(false),
m_rejected(true) m_rejected(true),
m_status(kNotRunning)
{ {
// do nothing // do nothing
} }
@ -108,15 +109,61 @@ CClient::exitMainLoop()
m_screen->exitMainLoop(); m_screen->exitMainLoop();
} }
void
CClient::addStatusJob(IJob* job)
{
m_statusJobs.addJob(job);
}
void
CClient::removeStatusJob(IJob* job)
{
m_statusJobs.removeJob(job);
}
bool bool
CClient::wasRejected() const CClient::wasRejected() const
{ {
return m_rejected; return m_rejected;
} }
CClient::EStatus
CClient::getStatus(CString* msg) const
{
CLock lock(&m_mutex);
if (msg != NULL) {
*msg = m_statusMessage;
}
return m_status;
}
void
CClient::runStatusJobs() const
{
m_statusJobs.runJobs();
}
void
CClient::setStatus(EStatus status, const char* msg)
{
{
CLock lock(&m_mutex);
m_status = status;
if (m_status == kError) {
m_statusMessage = (msg == NULL) ? "Error" : msg;
}
else {
m_statusMessage = (msg == NULL) ? "" : msg;
}
}
runStatusJobs();
}
void void
CClient::onError() CClient::onError()
{ {
setStatus(kError);
// close down session but don't wait too long // close down session but don't wait too long
deleteSession(3.0); deleteSession(3.0);
} }
@ -172,9 +219,11 @@ CClient::open()
try { try {
LOG((CLOG_INFO "opening screen")); LOG((CLOG_INFO "opening screen"));
openSecondaryScreen(); openSecondaryScreen();
setStatus(kNotRunning);
} }
catch (XScreenOpenFailure&) { catch (XScreenOpenFailure& e) {
// can't open screen // can't open screen
setStatus(kError, e.what());
LOG((CLOG_INFO "failed to open screen")); LOG((CLOG_INFO "failed to open screen"));
throw; throw;
} }
@ -195,6 +244,7 @@ CClient::mainLoop()
} }
try { try {
setStatus(kNotRunning);
LOG((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); LOG((CLOG_NOTE "starting client \"%s\"", m_name.c_str()));
// start server interactions // start server interactions
@ -213,6 +263,7 @@ CClient::mainLoop()
} }
catch (XMT& e) { catch (XMT& e) {
LOG((CLOG_ERR "client error: %s", e.what())); LOG((CLOG_ERR "client error: %s", e.what()));
setStatus(kError, e.what());
// clean up // clean up
deleteSession(); deleteSession();
@ -221,6 +272,7 @@ CClient::mainLoop()
} }
catch (XBase& e) { catch (XBase& e) {
LOG((CLOG_ERR "client error: %s", e.what())); LOG((CLOG_ERR "client error: %s", e.what()));
setStatus(kError, e.what());
// clean up // clean up
deleteSession(); deleteSession();
@ -229,6 +281,8 @@ CClient::mainLoop()
m_rejected = false; m_rejected = false;
} }
catch (XThread&) { catch (XThread&) {
setStatus(kNotRunning);
// clean up // clean up
deleteSession(); deleteSession();
LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str()));
@ -236,6 +290,7 @@ CClient::mainLoop()
} }
catch (...) { catch (...) {
LOG((CLOG_DEBUG "unknown client error")); LOG((CLOG_DEBUG "unknown client error"));
setStatus(kError);
// clean up // clean up
deleteSession(); deleteSession();
@ -529,11 +584,12 @@ CClient::runServer()
{ {
IDataSocket* socket = NULL; IDataSocket* socket = NULL;
CServerProxy* proxy = NULL; CServerProxy* proxy = NULL;
bool timedOut;
try { try {
for (;;) { for (;;) {
try { try {
// allow connect this much time to succeed // allow connect this much time to succeed
CTimerThread timer(15.0); CTimerThread timer(15.0, &timedOut);
// create socket and attempt to connect to server // create socket and attempt to connect to server
LOG((CLOG_DEBUG1 "connecting to server")); LOG((CLOG_DEBUG1 "connecting to server"));
@ -546,6 +602,7 @@ CClient::runServer()
break; break;
} }
catch (XSocketConnect& e) { catch (XSocketConnect& e) {
setStatus(kError, e.what());
LOG((CLOG_DEBUG "failed to connect to server: %s", e.what())); LOG((CLOG_DEBUG "failed to connect to server: %s", e.what()));
// failed to connect. if not camping then rethrow. // failed to connect. if not camping then rethrow.
@ -565,31 +622,47 @@ CClient::runServer()
m_server = proxy; m_server = proxy;
} }
catch (XThread&) { catch (XThread&) {
if (timedOut) {
LOG((CLOG_ERR "connection timed out")); LOG((CLOG_ERR "connection timed out"));
setStatus(kError, "connection timed out");
}
else {
// cancelled by some thread other than the timer
}
delete socket; delete socket;
throw; throw;
} }
catch (XBase& e) { catch (XBase& e) {
LOG((CLOG_ERR "connection failed: %s", e.what())); LOG((CLOG_ERR "connection failed: %s", e.what()));
setStatus(kError, e.what());
LOG((CLOG_DEBUG "disconnecting from server")); LOG((CLOG_DEBUG "disconnecting from server"));
delete socket; delete socket;
return; return;
} }
catch (...) { catch (...) {
LOG((CLOG_ERR "connection failed: <unknown error>")); LOG((CLOG_ERR "connection failed: <unknown error>"));
setStatus(kError);
LOG((CLOG_DEBUG "disconnecting from server")); LOG((CLOG_DEBUG "disconnecting from server"));
delete socket; delete socket;
return; return;
} }
try { try {
// prepare for remote control
m_screen->remoteControl();
// process messages // process messages
bool rejected = true; bool rejected = true;
if (proxy != NULL) { if (proxy != NULL) {
LOG((CLOG_DEBUG1 "communicating with server")); LOG((CLOG_DEBUG1 "communicating with server"));
setStatus(kRunning);
rejected = !proxy->mainLoop(); rejected = !proxy->mainLoop();
setStatus(kNotRunning);
} }
// prepare for local control
m_screen->localControl();
// clean up // clean up
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_rejected = rejected; m_rejected = rejected;
@ -600,6 +673,8 @@ CClient::runServer()
delete socket; delete socket;
} }
catch (...) { catch (...) {
setStatus(kNotRunning);
m_screen->localControl();
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_rejected = false; m_rejected = false;
m_server = NULL; m_server = NULL;
@ -664,9 +739,11 @@ CClient::handshakeServer(IDataSocket* socket)
} }
catch (XIncompatibleClient& e) { catch (XIncompatibleClient& e) {
LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor()));
setStatus(kError, e.what());
} }
catch (XBase& e) { catch (XBase& e) {
LOG((CLOG_WARN "error communicating with server: %s", e.what())); LOG((CLOG_WARN "error communicating with server: %s", e.what()));
setStatus(kError, e.what());
} }
catch (...) { catch (...) {
// probably timed out // probably timed out

View File

@ -20,6 +20,7 @@
#include "IClipboard.h" #include "IClipboard.h"
#include "CNetworkAddress.h" #include "CNetworkAddress.h"
#include "CMutex.h" #include "CMutex.h"
#include "CJobList.h"
class CSecondaryScreen; class CSecondaryScreen;
class CServerProxy; class CServerProxy;
@ -36,6 +37,13 @@ This class implements the top-level client algorithms for synergy.
*/ */
class CClient : public IScreenReceiver, public IClient { class CClient : public IScreenReceiver, public IClient {
public: public:
enum EStatus {
kNotRunning,
kRunning,
kError,
kMaxStatus
};
/*! /*!
This client will attempt to connect the server using \c clientName This client will attempt to connect the server using \c clientName
as its name. as its name.
@ -93,6 +101,22 @@ public:
*/ */
void exitMainLoop(); void exitMainLoop();
//! Add a job to notify of status changes
/*!
The added job is run whenever the server's status changes in
certain externally visible ways. The client keeps ownership
of the job.
*/
void addStatusJob(IJob*);
//! Remove a job to notify of status changes
/*!
Removes a previously added status notification job. A job can
remove itself when called but must not remove any other jobs.
The client keeps ownership of the job.
*/
void removeStatusJob(IJob*);
//@} //@}
//! @name accessors //! @name accessors
//@{ //@{
@ -103,6 +127,12 @@ public:
*/ */
bool wasRejected() const; bool wasRejected() const;
//! Get the status
/*!
Returns the current status and status message.
*/
EStatus getStatus(CString* = NULL) const;
//@} //@}
// IScreenReceiver overrides // IScreenReceiver overrides
@ -140,6 +170,12 @@ public:
virtual void getCursorCenter(SInt32& x, SInt32& y) const; virtual void getCursorCenter(SInt32& x, SInt32& y) const;
private: private:
// notify status jobs of a change
void runStatusJobs() const;
// set new status
void setStatus(EStatus, const char* msg = NULL);
// open/close the secondary screen // open/close the secondary screen
void openSecondaryScreen(); void openSecondaryScreen();
void closeSecondaryScreen(); void closeSecondaryScreen();
@ -169,6 +205,11 @@ private:
bool m_ownClipboard[kClipboardEnd]; bool m_ownClipboard[kClipboardEnd];
IClipboard::Time m_timeClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd];
CString m_dataClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd];
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
}; };
#endif #endif

View File

@ -420,6 +420,7 @@ CServerProxy::translateModifierMask(KeyModifierMask mask) const
if ((mask & KeyModifierSuper) != 0) { if ((mask & KeyModifierSuper) != 0) {
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]]; newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]];
} }
return newMask;
} }
void void

View File

@ -97,10 +97,16 @@ CThread::wait(double timeout) const
return ARCH->wait(m_thread, timeout); return ARCH->wait(m_thread, timeout);
} }
bool CThread::EWaitResult
CThread::waitForEvent(double timeout) CThread::waitForEvent(double timeout) const
{ {
return ARCH->waitForEvent(timeout); // IArchMultithread EWaitResults map directly to our EWaitResults
static const EWaitResult s_map[] = {
kEvent,
kExit,
kTimeout
};
return s_map[ARCH->waitForEvent(m_thread, timeout)];
} }
void* void*

View File

@ -39,6 +39,13 @@ documentation.
// note -- do not derive from this class // note -- do not derive from this class
class CThread { class CThread {
public: public:
//! Result of waitForEvent()
enum EWaitResult {
kEvent, //!< An event is pending
kExit, //!< Thread exited
kTimeout //!< Wait timed out
};
//! Run \c adoptedJob in a new thread //! Run \c adoptedJob in a new thread
/*! /*!
Create and start a new thread executing the \c adoptedJob. The Create and start a new thread executing the \c adoptedJob. The
@ -159,18 +166,19 @@ public:
//! Wait for an event (win32) //! Wait for an event (win32)
/*! /*!
Wait for the message queue to contain a message for up to \c timeout Wait for the message queue to contain a message or for the thread
seconds. This returns immediately if any message is available to exit for up to \c timeout seconds. This returns immediately if
(including messages that were already in the queue during the last any message is available (including messages that were already in
call to \c GetMessage() or \c PeekMessage() or waitForEvent(). the queue during the last call to \c GetMessage() or
Returns true iff a message is available. This will wait forever \c PeekMessage() or waitForEvent(). Returns kEvent if a message
if \c timeout < 0.0. is available, kExit if the thread exited, and kTimeout otherwise.
This will wait forever if \c timeout < 0.0.
This method is available under win32 only. This method is available under win32 only.
(cancellation point) (cancellation point)
*/ */
static bool waitForEvent(double timeout = -1.0); EWaitResult waitForEvent(double timeout = -1.0) const;
//! Get the exit result //! Get the exit result
/*! /*!

View File

@ -22,8 +22,13 @@
// CTimerThread // CTimerThread
// //
CTimerThread::CTimerThread(double timeout) : m_timeout(timeout) CTimerThread::CTimerThread(double timeout, bool* timedOut) :
m_timeout(timeout),
m_timedOut(timedOut)
{ {
if (m_timedOut != NULL) {
*m_timedOut = false;
}
if (m_timeout >= 0.0) { if (m_timeout >= 0.0) {
m_callingThread = new CThread(CThread::getCurrentThread()); m_callingThread = new CThread(CThread::getCurrentThread());
m_timingThread = new CThread(new TMethodJob<CTimerThread>( m_timingThread = new CThread(new TMethodJob<CTimerThread>(
@ -53,5 +58,8 @@ CTimerThread::timer(void*)
LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout));
ARCH->sleep(m_timeout); ARCH->sleep(m_timeout);
LOG((CLOG_DEBUG1 "timeout")); LOG((CLOG_DEBUG1 "timeout"));
if (m_timedOut != NULL) {
*m_timedOut = true;
}
m_callingThread->cancel(); m_callingThread->cancel();
} }

View File

@ -30,9 +30,11 @@ public:
/*! /*!
Cancels the calling thread after \c timeout seconds unless destroyed Cancels the calling thread after \c timeout seconds unless destroyed
before then. If \c timeout is less than zero then it never times before then. If \c timeout is less than zero then it never times
out and this is a no-op. out and this is a no-op. If \c timedOutFlag is not NULL then it's
set to false in the c'tor and to true if the timeout exipires before
it's cancelled.
*/ */
CTimerThread(double timeout); CTimerThread(double timeout, bool* timedOutFlag = NULL);
//! Cancel the timer thread //! Cancel the timer thread
~CTimerThread(); ~CTimerThread();
@ -45,6 +47,7 @@ private:
private: private:
double m_timeout; double m_timeout;
bool* m_timedOut;
CThread* m_callingThread; CThread* m_callingThread;
CThread* m_timingThread; CThread* m_timingThread;
}; };

View File

@ -112,6 +112,12 @@ CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/)
// no options // no options
} }
UInt32
CMSWindowsPrimaryScreen::addOneShotTimer(double timeout)
{
return m_screen->addOneShotTimer(timeout);
}
KeyModifierMask KeyModifierMask
CMSWindowsPrimaryScreen::getToggleMask() const CMSWindowsPrimaryScreen::getToggleMask() const
{ {
@ -376,6 +382,12 @@ CMSWindowsPrimaryScreen::onEvent(CEvent* event)
return false; return false;
} }
void
CMSWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id)
{
m_receiver->onOneShotTimerExpired(id);
}
SInt32 SInt32
CMSWindowsPrimaryScreen::getJumpZoneSize() const CMSWindowsPrimaryScreen::getJumpZoneSize() const
{ {

View File

@ -39,6 +39,7 @@ public:
virtual void warpCursor(SInt32 x, SInt32 y); virtual void warpCursor(SInt32 x, SInt32 y);
virtual void resetOptions(); virtual void resetOptions();
virtual void setOptions(const COptionsList& options); virtual void setOptions(const COptionsList& options);
virtual UInt32 addOneShotTimer(double timeout);
virtual KeyModifierMask getToggleMask() const; virtual KeyModifierMask getToggleMask() const;
virtual bool isLockedToScreen() const; virtual bool isLockedToScreen() const;
virtual IScreen* getScreen() const; virtual IScreen* getScreen() const;
@ -47,6 +48,7 @@ public:
virtual void onScreensaver(bool activated); virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event); virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event); virtual bool onEvent(CEvent* event);
virtual void onOneShotTimerExpired(UInt32 id);
virtual SInt32 getJumpZoneSize() const; virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND); virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND); virtual void preDestroyWindow(HWND);

View File

@ -122,6 +122,11 @@ CMSWindowsScreen::closeDesktop()
// remove timer // remove timer
if (m_timer != 0) { if (m_timer != 0) {
KillTimer(NULL, m_timer); KillTimer(NULL, m_timer);
m_timer = 0;
}
if (m_oneShotTimer != 0) {
KillTimer(NULL, m_oneShotTimer);
m_oneShotTimer = 0;
} }
// disconnect from desktop // disconnect from desktop
@ -134,6 +139,18 @@ CMSWindowsScreen::closeDesktop()
assert(m_desk == NULL); assert(m_desk == NULL);
} }
UInt32
CMSWindowsScreen::addOneShotTimer(double timeout)
{
// FIXME -- support multiple one-shot timers
if (m_oneShotTimer != 0) {
KillTimer(NULL, m_oneShotTimer);
}
m_oneShotTimer = SetTimer(NULL, 0,
static_cast<UINT>(1000.0 * timeout), NULL);
return 0;
}
bool bool
CMSWindowsScreen::isMultimon() const CMSWindowsScreen::isMultimon() const
{ {
@ -205,8 +222,12 @@ CMSWindowsScreen::mainLoop()
event.m_result = 0; event.m_result = 0;
for (;;) { for (;;) {
// wait for an event in a cancellable way // wait for an event in a cancellable way
CThread::waitForEvent(); if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) {
GetMessage(&event.m_msg, NULL, 0, 0); continue;
}
if (!PeekMessage(&event.m_msg, NULL, 0, 0, PM_REMOVE)) {
continue;
}
// handle quit message // handle quit message
if (event.m_msg.message == WM_QUIT) { if (event.m_msg.message == WM_QUIT) {
@ -467,6 +488,7 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event)
} }
case WM_TIMER: case WM_TIMER:
if (msg->wParam == m_timer) {
// if current desktop is not the input desktop then switch to it. // if current desktop is not the input desktop then switch to it.
// windows 95 doesn't support multiple desktops so don't bother // windows 95 doesn't support multiple desktops so don't bother
// to check under it. // to check under it.
@ -488,6 +510,13 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event)
} }
} }
} }
}
else if (msg->wParam == m_oneShotTimer) {
// one shot timer expired
KillTimer(NULL, m_oneShotTimer);
m_oneShotTimer = 0;
m_eventHandler->onOneShotTimerExpired(0);
}
return true; return true;
} }

View File

@ -64,6 +64,14 @@ public:
*/ */
void closeDesktop(); void closeDesktop();
//! Install a one-shot timer
/*!
Installs a one-shot timer for \c timeout seconds and returns the
id of the timer (which will be passed to the receiver's
\c onTimerExpired()).
*/
UInt32 addOneShotTimer(double timeout);
//@} //@}
//! @name accessors //! @name accessors
//@{ //@{
@ -169,6 +177,9 @@ private:
// the timer used to check for desktop switching // the timer used to check for desktop switching
UINT m_timer; UINT m_timer;
// the one shot timer
UINT m_oneShotTimer;
// the current desk and it's name // the current desk and it's name
HDESK m_desk; HDESK m_desk;
CString m_deskName; CString m_deskName;

View File

@ -262,6 +262,12 @@ CMSWindowsSecondaryScreen::onEvent(CEvent* event)
return false; return false;
} }
void
CMSWindowsSecondaryScreen::onOneShotTimerExpired(UInt32)
{
// ignore
}
SInt32 SInt32
CMSWindowsSecondaryScreen::getJumpZoneSize() const CMSWindowsSecondaryScreen::getJumpZoneSize() const
{ {
@ -272,6 +278,11 @@ void
CMSWindowsSecondaryScreen::postCreateWindow(HWND window) CMSWindowsSecondaryScreen::postCreateWindow(HWND window)
{ {
m_window = window; m_window = window;
// update key state
updateKeys();
// hide cursor if this screen isn't active
if (!isActive()) { if (!isActive()) {
showWindow(); showWindow();
} }

View File

@ -53,6 +53,7 @@ public:
virtual void onScreensaver(bool activated); virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event); virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event); virtual bool onEvent(CEvent* event);
virtual void onOneShotTimerExpired(UInt32 id);
virtual SInt32 getJumpZoneSize() const; virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND); virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND); virtual void preDestroyWindow(HWND);
@ -69,6 +70,7 @@ protected:
virtual void hideWindow(); virtual void hideWindow();
virtual void warpCursor(SInt32 x, SInt32 y); virtual void warpCursor(SInt32 x, SInt32 y);
virtual void updateKeys(); virtual void updateKeys();
virtual void releaseKeys();
virtual void setToggleState(KeyModifierMask); virtual void setToggleState(KeyModifierMask);
virtual KeyModifierMask getToggleState() const; virtual KeyModifierMask getToggleState() const;
@ -98,7 +100,6 @@ private:
KeyModifierMask, EKeyAction) const; KeyModifierMask, EKeyAction) const;
void doKeystrokes(const Keystrokes&, SInt32 count); void doKeystrokes(const Keystrokes&, SInt32 count);
void releaseKeys();
void toggleKey(UINT virtualKey, KeyModifierMask mask); void toggleKey(UINT virtualKey, KeyModifierMask mask);
UINT virtualKeyToScanCode(UINT& virtualKey) const; UINT virtualKeyToScanCode(UINT& virtualKey) const;
bool isExtendedKey(UINT virtualKey) const; bool isExtendedKey(UINT virtualKey) const;

View File

@ -260,6 +260,12 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam)
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return 1; return 1;
} }
else {
x += g_xScreen;
y += g_yScreen;
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return 0;
}
} }
} }

View File

@ -356,7 +356,7 @@ CXWindowsSecondaryScreen::destroyWindow()
CDisplayLock display(m_screen); CDisplayLock display(m_screen);
if (display != NULL) { if (display != NULL) {
// release keys that are still pressed // release keys that are still pressed
releaseKeys(display); doReleaseKeys(display);
// no longer impervious to server grabs // no longer impervious to server grabs
XTestGrabControl(display, False); XTestGrabControl(display, False);
@ -901,7 +901,7 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const
} }
void void
CXWindowsSecondaryScreen::releaseKeys(Display* display) CXWindowsSecondaryScreen::doReleaseKeys(Display* display)
{ {
assert(display != NULL); assert(display != NULL);
@ -969,6 +969,15 @@ CXWindowsSecondaryScreen::updateKeys()
updateModifiers(display); updateModifiers(display);
} }
void
CXWindowsSecondaryScreen::releaseKeys()
{
CDisplayLock display(m_screen);
if (display != NULL) {
doReleaseKeys(display);
}
}
void void
CXWindowsSecondaryScreen::updateModifiers(Display* display) CXWindowsSecondaryScreen::updateModifiers(Display* display)
{ {

View File

@ -67,6 +67,7 @@ protected:
virtual void hideWindow(); virtual void hideWindow();
virtual void warpCursor(SInt32 x, SInt32 y); virtual void warpCursor(SInt32 x, SInt32 y);
virtual void updateKeys(); virtual void updateKeys();
virtual void releaseKeys();
virtual void setToggleState(KeyModifierMask); virtual void setToggleState(KeyModifierMask);
virtual KeyModifierMask getToggleState() const; virtual KeyModifierMask getToggleState() const;
@ -97,14 +98,10 @@ private:
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
KeyModifierMask, EKeyAction) const; KeyModifierMask, EKeyAction) const;
/*
bool findKeyCode(KeyCode&, unsigned int&,
KeyID id, unsigned int) const;
*/
void doKeystrokes(const Keystrokes&, SInt32 count); void doKeystrokes(const Keystrokes&, SInt32 count);
unsigned int maskToX(KeyModifierMask) const; unsigned int maskToX(KeyModifierMask) const;
void releaseKeys(Display*); void doReleaseKeys(Display*);
void updateKeycodeMap(Display* display); void updateKeycodeMap(Display* display);
void updateModifiers(Display* display); void updateModifiers(Display* display);
void updateModifierMap(Display* display); void updateModifierMap(Display* display);

View File

@ -66,7 +66,8 @@ CServer::CServer(const CString& serverName) :
m_switchWaitEngaged(false), m_switchWaitEngaged(false),
m_switchTwoTapDelay(0.0), m_switchTwoTapDelay(0.0),
m_switchTwoTapEngaged(false), m_switchTwoTapEngaged(false),
m_switchTwoTapArmed(false) m_switchTwoTapArmed(false),
m_status(kNotRunning)
{ {
// do nothing // do nothing
} }
@ -85,14 +86,17 @@ CServer::open()
try { try {
LOG((CLOG_INFO "opening screen")); LOG((CLOG_INFO "opening screen"));
openPrimaryScreen(); openPrimaryScreen();
setStatus(kNotRunning);
} }
catch (XScreen&) { catch (XScreen& e) {
// can't open screen // can't open screen
setStatus(kError, e.what());
LOG((CLOG_INFO "failed to open screen")); LOG((CLOG_INFO "failed to open screen"));
throw; throw;
} }
catch (XUnknownClient& e) { catch (XUnknownClient& e) {
// can't open screen // can't open screen
setStatus(kServerNameUnknown);
LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str()));
throw; throw;
} }
@ -108,6 +112,7 @@ CServer::mainLoop()
} }
try { try {
setStatus(kNotRunning);
LOG((CLOG_NOTE "starting server")); LOG((CLOG_NOTE "starting server"));
// start listening for new clients // start listening for new clients
@ -138,11 +143,13 @@ CServer::mainLoop()
stopThreads(); \ stopThreads(); \
delete m_httpServer; \ delete m_httpServer; \
m_httpServer = NULL; \ m_httpServer = NULL; \
runStatusJobs(); \
} while (false) } while (false)
FINALLY; FINALLY;
} }
catch (XMT& e) { catch (XMT& e) {
LOG((CLOG_ERR "server error: %s", e.what())); LOG((CLOG_ERR "server error: %s", e.what()));
setStatus(kError, e.what());
// clean up // clean up
LOG((CLOG_NOTE "stopping server")); LOG((CLOG_NOTE "stopping server"));
@ -151,12 +158,15 @@ CServer::mainLoop()
} }
catch (XBase& e) { catch (XBase& e) {
LOG((CLOG_ERR "server error: %s", e.what())); LOG((CLOG_ERR "server error: %s", e.what()));
setStatus(kError, e.what());
// clean up // clean up
LOG((CLOG_NOTE "stopping server")); LOG((CLOG_NOTE "stopping server"));
FINALLY; FINALLY;
} }
catch (XThread&) { catch (XThread&) {
setStatus(kNotRunning);
// clean up // clean up
LOG((CLOG_NOTE "stopping server")); LOG((CLOG_NOTE "stopping server"));
FINALLY; FINALLY;
@ -164,6 +174,7 @@ CServer::mainLoop()
} }
catch (...) { catch (...) {
LOG((CLOG_DEBUG "unknown server error")); LOG((CLOG_DEBUG "unknown server error"));
setStatus(kError);
// clean up // clean up
LOG((CLOG_NOTE "stopping server")); LOG((CLOG_NOTE "stopping server"));
@ -260,6 +271,9 @@ CServer::setConfig(const CConfig& config)
sendOptions(client); sendOptions(client);
} }
// notify of status
runStatusJobs();
return true; return true;
} }
@ -287,12 +301,52 @@ CServer::setStreamFilterFactory(IStreamFilterFactory* adopted)
m_streamFilterFactory = adopted; m_streamFilterFactory = adopted;
} }
void
CServer::addStatusJob(IJob* job)
{
m_statusJobs.addJob(job);
}
void
CServer::removeStatusJob(IJob* job)
{
m_statusJobs.removeJob(job);
}
CString CString
CServer::getPrimaryScreenName() const CServer::getPrimaryScreenName() const
{ {
return m_name; return m_name;
} }
UInt32
CServer::getNumClients() const
{
CLock lock(&m_mutex);
return m_clients.size();
}
void
CServer::getClients(std::vector<CString>& list) const
{
CLock lock(&m_mutex);
list.clear();
for (CClientList::const_iterator index = m_clients.begin();
index != m_clients.end(); ++index) {
list.push_back(index->first);
}
}
CServer::EStatus
CServer::getStatus(CString* msg) const
{
CLock lock(&m_mutex);
if (msg != NULL) {
*msg = m_statusMessage;
}
return m_status;
}
void void
CServer::getConfig(CConfig* config) const CServer::getConfig(CConfig* config) const
{ {
@ -302,6 +356,28 @@ CServer::getConfig(CConfig* config) const
*config = m_config; *config = m_config;
} }
void
CServer::runStatusJobs() const
{
m_statusJobs.runJobs();
}
void
CServer::setStatus(EStatus status, const char* msg)
{
{
CLock lock(&m_mutex);
m_status = status;
if (m_status == kError) {
m_statusMessage = (msg == NULL) ? "Error" : msg;
}
else {
m_statusMessage = (msg == NULL) ? "" : msg;
}
}
runStatusJobs();
}
UInt32 UInt32
CServer::getActivePrimarySides() const CServer::getActivePrimarySides() const
{ {
@ -325,6 +401,8 @@ CServer::getActivePrimarySides() const
void void
CServer::onError() CServer::onError()
{ {
setStatus(kError);
// stop all running threads but don't wait too long since some // stop all running threads but don't wait too long since some
// threads may be unable to proceed until this thread returns. // threads may be unable to proceed until this thread returns.
stopThreads(3.0); stopThreads(3.0);
@ -743,6 +821,10 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
case kBottom: case kBottom:
clearWait = (m_y <= ay + ah - 1 + zoneSize); clearWait = (m_y <= ay + ah - 1 + zoneSize);
break; break;
default:
clearWait = false;
break;
} }
if (clearWait) { if (clearWait) {
onNoSwitch(); onNoSwitch();
@ -1174,7 +1256,7 @@ CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y)
// if waiting before a switch then prepare to switch later // if waiting before a switch then prepare to switch later
if (!allowSwitch && m_switchWaitDelay > 0.0) { if (!allowSwitch && m_switchWaitDelay > 0.0) {
if (isNewDirection) { if (isNewDirection || !m_switchWaitEngaged) {
m_switchWaitEngaged = true; m_switchWaitEngaged = true;
m_switchWaitX = x; m_switchWaitX = x;
m_switchWaitY = y; m_switchWaitY = y;
@ -1413,6 +1495,7 @@ CServer::acceptClients(void*)
break; break;
} }
catch (XSocketAddressInUse& e) { catch (XSocketAddressInUse& e) {
setStatus(kError, e.what());
LOG((CLOG_WARN "bind failed: %s", e.what())); LOG((CLOG_WARN "bind failed: %s", e.what()));
// give up if we've waited too long // give up if we've waited too long
@ -1427,6 +1510,7 @@ CServer::acceptClients(void*)
} }
// accept connections and begin processing them // accept connections and begin processing them
setStatus(kRunning);
LOG((CLOG_DEBUG1 "waiting for client connections")); LOG((CLOG_DEBUG1 "waiting for client connections"));
for (;;) { for (;;) {
// accept connection // accept connection
@ -1439,16 +1523,15 @@ CServer::acceptClients(void*)
startThread(new TMethodJob<CServer>( startThread(new TMethodJob<CServer>(
this, &CServer::runClient, socket)); this, &CServer::runClient, socket));
} }
// clean up
delete listen;
} }
catch (XBase& e) { catch (XBase& e) {
setStatus(kError, e.what());
LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); LOG((CLOG_ERR "cannot listen for clients: %s", e.what()));
delete listen; delete listen;
exitMainLoopWithError(); exitMainLoopWithError();
} }
catch (...) { catch (...) {
setStatus(kNotRunning);
delete listen; delete listen;
throw; throw;
} }
@ -1923,6 +2006,7 @@ CServer::addConnection(IClient* client)
LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str()));
{
CLock lock(&m_mutex); CLock lock(&m_mutex);
// name must be in our configuration // name must be in our configuration
@ -1938,12 +2022,16 @@ CServer::addConnection(IClient* client)
// save screen info // save screen info
m_clients.insert(std::make_pair(client->getName(), client)); m_clients.insert(std::make_pair(client->getName(), client));
LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str()));
}
runStatusJobs();
} }
void void
CServer::removeConnection(const CString& name) CServer::removeConnection(const CString& name)
{ {
LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str()));
bool updateStatus;
{
CLock lock(&m_mutex); CLock lock(&m_mutex);
// find client // find client
@ -1961,7 +2049,8 @@ CServer::removeConnection(const CString& name)
clearSwitchState(); clearSwitchState();
} }
// don't notify active screen since it probably already disconnected // don't notify active screen since it probably already
// disconnected.
LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y));
// cut over // cut over
@ -1988,6 +2077,13 @@ CServer::removeConnection(const CString& name)
// remove any thread for this client // remove any thread for this client
m_clientThreads.erase(name); m_clientThreads.erase(name);
updateStatus = (m_clients.size() <= 1);
}
if (updateStatus) {
runStatusJobs();
}
} }

View File

@ -22,9 +22,11 @@
#include "CCondVar.h" #include "CCondVar.h"
#include "CMutex.h" #include "CMutex.h"
#include "CThread.h" #include "CThread.h"
#include "CJobList.h"
#include "CStopwatch.h" #include "CStopwatch.h"
#include "stdlist.h" #include "stdlist.h"
#include "stdmap.h" #include "stdmap.h"
#include "stdvector.h"
class CClientProxy; class CClientProxy;
class CHTTPServer; class CHTTPServer;
@ -42,6 +44,14 @@ This class implements the top-level server algorithms for synergy.
*/ */
class CServer : public IServer, public IPrimaryScreenReceiver { class CServer : public IServer, public IPrimaryScreenReceiver {
public: public:
enum EStatus {
kNotRunning,
kRunning,
kServerNameUnknown,
kError,
kMaxStatus
};
/*! /*!
The server will look itself up in the configuration using \c serverName The server will look itself up in the configuration using \c serverName
as its name. as its name.
@ -116,6 +126,22 @@ public:
*/ */
void setStreamFilterFactory(IStreamFilterFactory*); void setStreamFilterFactory(IStreamFilterFactory*);
//! Add a job to notify of status changes
/*!
The added job is run whenever the server's status changes in
certain externally visible ways. The client keeps ownership
of the job.
*/
void addStatusJob(IJob*);
//! Remove a job to notify of status changes
/*!
Removes a previously added status notification job. A job can
remove itself when called but must not remove any other jobs.
The client keeps ownership of the job.
*/
void removeStatusJob(IJob*);
//@} //@}
//! @name accessors //! @name accessors
//@{ //@{
@ -132,6 +158,24 @@ public:
*/ */
CString getPrimaryScreenName() const; CString getPrimaryScreenName() const;
//! Get number of connected clients
/*!
Returns the number of connected clients, including the server itself.
*/
UInt32 getNumClients() const;
//! Get the list of connected clients
/*!
Set the \c list to the names of the currently connected clients.
*/
void getClients(std::vector<CString>& list) const;
//! Get the status
/*!
Returns the current status and status message.
*/
EStatus getStatus(CString* = NULL) const;
//@} //@}
// IServer overrides // IServer overrides
@ -170,6 +214,12 @@ protected:
private: private:
typedef std::list<CThread> CThreadList; typedef std::list<CThread> CThreadList;
// notify status jobs of a change
void runStatusJobs() const;
// set new status
void setStatus(EStatus, const char* msg = NULL);
// get the sides of the primary screen that have neighbors // get the sides of the primary screen that have neighbors
UInt32 getActivePrimarySides() const; UInt32 getActivePrimarySides() const;
@ -352,6 +402,11 @@ private:
CStopwatch m_switchTwoTapTimer; CStopwatch m_switchTwoTapTimer;
bool m_switchTwoTapEngaged; bool m_switchTwoTapEngaged;
bool m_switchTwoTapArmed; bool m_switchTwoTapArmed;
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
}; };
#endif #endif

View File

@ -74,6 +74,30 @@ CSecondaryScreen::open()
// create and prepare our window // create and prepare our window
createWindow(); createWindow();
// subclass hook
onPostOpen();
// reset options
resetOptions();
}
catch (...) {
close();
throw;
}
}
void
CSecondaryScreen::close()
{
onPreClose();
destroyWindow();
getScreen()->close();
onPostClose();
}
void
CSecondaryScreen::remoteControl()
{
// assume primary has all clipboards // assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
grabClipboard(id); grabClipboard(id);
@ -85,17 +109,6 @@ CSecondaryScreen::open()
// disable the screen saver // disable the screen saver
getScreen()->openScreensaver(false); getScreen()->openScreensaver(false);
// subclass hook
onPostOpen();
// reset options
resetOptions();
}
catch (...) {
close();
throw;
}
// hide the cursor // hide the cursor
{ {
CLock lock(&m_mutex); CLock lock(&m_mutex);
@ -105,13 +118,9 @@ CSecondaryScreen::open()
} }
void void
CSecondaryScreen::close() CSecondaryScreen::localControl()
{ {
onPreClose();
getScreen()->closeScreensaver(); getScreen()->closeScreensaver();
destroyWindow();
getScreen()->close();
onPostClose();
} }
void void

View File

@ -41,11 +41,10 @@ public:
//! Open screen //! Open screen
/*! /*!
Opens the screen. This includes initializing the screen, Opens the screen. It also causes events to the reported to an
hiding the cursor, and disabling the screen saver. It also causes IScreenReceiver (which is set through some other interface).
events to the reported to an IScreenReceiver (which is set through Calls close() before returning (rethrowing) if it fails for any
some other interface). Calls close() before returning (rethrowing) reason.
if it fails for any reason.
*/ */
void open(); void open();
@ -64,12 +63,26 @@ public:
*/ */
void exitMainLoop(); void exitMainLoop();
//! Prepare for remote control
/*!
Prepares the screen for remote control by the server. In
particular, it disables the screen saver.
*/
void remoteControl();
//! Release from remote control
/*!
Cleans up the screen from remote control by the server. In
particular, it enables the screen saver. It also synthesizes
key up events for any keys that are logically down; without
this the client will leave its keyboard in the wrong logical
state.
*/
void localControl();
//! Close screen //! Close screen
/*! /*!
Closes the screen. This restores the screen saver, shows the cursor Closes the screen.
and closes the screen. It also synthesizes key up events for any
keys that are logically down; without this the client will leave
its keyboard in the wrong logical state.
*/ */
void close(); void close();
@ -332,6 +345,13 @@ protected:
*/ */
virtual void updateKeys() = 0; virtual void updateKeys() = 0;
//! Release keys
/*!
Synthesizes key release event for any key that our key state
says is down.
*/
virtual void releaseKeys() = 0;
//! Synchronize toggle key state //! Synchronize toggle key state
/*! /*!
Toggle modifiers that don't match the given state so that they do. Toggle modifiers that don't match the given state so that they do.