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);
}
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
getAppPath(const CString& appName)
{

View File

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

View File

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

View File

@ -13,6 +13,8 @@
*/
#include "CConfig.h"
#include "KeyTypes.h"
#include "OptionTypes.h"
#include "ProtocolTypes.h"
#include "CLog.h"
#include "CStringUtil.h"
@ -24,6 +26,8 @@
// these must come after the above because it includes windows.h
#include "LaunchUtil.h"
#include "CAutoStart.h"
#include "CGlobalOptions.h"
#include "CAdvancedOptions.h"
#define CONFIG_NAME "synergy.sgc"
#define CLIENT_APP "synergyc.exe"
@ -47,10 +51,28 @@ public:
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 TCHAR* s_layoutClass = TEXT("SynergyLayout");
static const CModifierInfo s_modifiers[] = {
{ 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] = {
{ TEXT("Error"), "ERROR" },
@ -63,6 +85,14 @@ static const char* s_debugName[][2] = {
};
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
//
@ -127,7 +157,7 @@ bool
isClientChecked(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
return (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED);
return isItemChecked(child);
}
static
@ -476,58 +506,20 @@ static
CString
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;
if (testing) {
// constant testing args
cmdLine += " -z --no-restart --no-daemon";
// debug level testing arg
child = getItem(hwnd, IDC_MAIN_DEBUG);
cmdLine += " --debug ";
cmdLine += s_debugName[SendMessage(child, CB_GETCURSEL, 0, 0)][1];
// add constant testing args
if (testing) {
cmdLine += " -z --no-restart --no-daemon";
}
cmdLine += " --name ";
cmdLine += name;
// get the server name
CString server;
bool isClient = isClientChecked(hwnd);
if (isClient) {
// check server name
child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
CString server = getWindowText(child);
HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
server = getWindowText(child);
if (!ARG->m_config.isValidScreenName(server)) {
showError(hwnd, CStringUtil::format(
getString(IDS_INVALID_SERVER_NAME).c_str(),
@ -551,16 +543,19 @@ getCommandLine(HWND hwnd, bool testing)
if (testing) {
cmdLine += " --no-camp";
}
cmdLine += " ";
cmdLine += server;
cmdLine += ":";
cmdLine += portString;
}
else {
cmdLine += " --address :";
cmdLine += portString;
// debug level
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;
}
@ -711,11 +706,9 @@ initMainWindow(HWND hwnd)
// choose client/server radio buttons
HWND child;
child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
SendMessage(child, BM_SETCHECK, !configLoaded ?
BST_CHECKED : BST_UNCHECKED, 0);
setItemChecked(child, !configLoaded);
child = getItem(hwnd, IDC_MAIN_SERVER_RADIO);
SendMessage(child, BM_SETCHECK, configLoaded ?
BST_CHECKED : BST_UNCHECKED, 0);
setItemChecked(child, configLoaded);
// if config is loaded then initialize server controls
if (configLoaded) {
@ -729,16 +722,7 @@ initMainWindow(HWND hwnd)
}
}
// initialize other controls
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());
// debug level
child = getItem(hwnd, IDC_MAIN_DEBUG);
for (unsigned int i = 0; i < sizeof(s_debugName) /
sizeof(s_debugName[0]); ++i) {
@ -794,19 +778,32 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
CConfig::CScreenOptions::const_iterator index;
child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK);
index = info->m_options.find(kOptionHalfDuplexCapsLock);
if (index != info->m_options.end() && index->second != 0) {
SendMessage(child, BM_SETCHECK, BST_CHECKED, 0);
}
else {
SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0);
}
setItemChecked(child, (index != info->m_options.end() &&
index->second != 0));
child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK);
index = info->m_options.find(kOptionHalfDuplexNumLock);
if (index != info->m_options.end() && index->second != 0) {
SendMessage(child, BM_SETCHECK, BST_CHECKED, 0);
}
else {
SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0);
setItemChecked(child, (index != info->m_options.end() &&
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);
}
// 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;
@ -883,20 +880,36 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
// save options
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;
}
else {
info->m_options.erase(kOptionHalfDuplexCapsLock);
}
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;
}
else {
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
EndDialog(hwnd, 1);
info = NULL;
@ -1065,6 +1078,16 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
return 0;
}
break;
case IDC_MAIN_OPTIONS:
s_globalOptions->doModal();
enableSaveControls(hwnd);
break;
case IDC_MAIN_ADVANCED:
s_advancedOptions->doModal(isClientChecked(hwnd));
enableSaveControls(hwnd);
break;
}
default:
@ -1076,7 +1099,7 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
int WINAPI
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow)
{
CArch arch;
CArch arch(instance);
CLOG;
CArgs args;
@ -1108,8 +1131,10 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow)
HWND m_mainWindow = CreateDialog(s_instance,
MAKEINTRESOURCE(IDD_MAIN), 0, NULL);
// prep window
// prep windows
initMainWindow(m_mainWindow);
s_globalOptions = new CGlobalOptions(m_mainWindow, &ARG->m_config);
s_advancedOptions = new CAdvancedOptions(m_mainWindow, &ARG->m_config);
// show window
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"
# Begin Source File
SOURCE=.\CAdvancedOptions.cpp
# End Source File
# Begin Source File
SOURCE=.\CAutoStart.cpp
# End Source File
# Begin Source File
SOURCE=.\CGlobalOptions.cpp
# End Source File
# Begin Source File
SOURCE=.\launcher.cpp
# End Source File
# Begin Source File
@ -115,10 +123,18 @@ SOURCE=.\LaunchUtil.cpp
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\CAdvancedOptions.h
# End Source File
# Begin Source File
SOURCE=.\CAutoStart.h
# End Source File
# Begin Source File
SOURCE=.\CGlobalOptions.h
# End Source File
# Begin Source File
SOURCE=.\LaunchUtil.h
# End Source File
# Begin Source File

View File

@ -40,11 +40,14 @@
#define IDS_SERVER_IS_CLIENT 36
#define IDS_ADD_SCREEN 37
#define IDS_EDIT_SCREEN 38
#define IDS_INVALID_TIME 39
#define IDD_MAIN 101
#define IDD_ADD 102
#define IDD_WAIT 103
#define IDI_SYNERGY 104
#define IDD_AUTOSTART 105
#define IDD_ADVANCED_OPTIONS 106
#define IDD_GLOBAL_OPTIONS 107
#define IDC_MAIN_CLIENT_RADIO 1000
#define IDC_MAIN_SERVER_RADIO 1001
#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002
@ -76,18 +79,31 @@
#define IDC_AUTOSTART_INSTALL_USER 1033
#define IDC_AUTOSTART_INSTALL_SYSTEM 1034
#define IDC_MAIN_AUTOSTART 1035
#define IDC_MAIN_DEBUG 1036
#define IDC_MAIN_OPTIONS 1036
#define IDC_ADD_HD_CAPS_CHECK 1037
#define IDC_MAIN_ADVANCED 1037
#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
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#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_CONTROL_VALUE 1038
#define _APS_NEXT_CONTROL_VALUE 1044
#define _APS_NEXT_SYMED_VALUE 101
#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

@ -15,19 +15,29 @@ NULL =
DEPTH = ../..
VDEPTH = ./$(VPATH)/$(DEPTH)
EXTRA_DIST = \
resource.h \
synergyc.dsp \
synergyc.ico \
synergyc.rc \
EXTRA_DIST = \
CMSWindowsClientTaskBarReceiver.cpp \
CMSWindowsClientTaskBarReceiver.h \
resource.h \
synergyc.dsp \
synergyc.ico \
synergyc.rc \
tb_error.ico \
tb_idle.ico \
tb_run.ico \
tb_wait.ico \
$(NULL)
MAINTAINERCLEANFILES = \
Makefile.in \
MAINTAINERCLEANFILES = \
Makefile.in \
$(NULL)
bin_PROGRAMS = synergyc
synergyc_SOURCES = \
CClientTaskBarReceiver.cpp \
CClientTaskBarReceiver.h \
CXWindowsClientTaskBarReceiver.cpp \
CXWindowsClientTaskBarReceiver.h \
synergyc.cpp \
$(NULL)
synergyc_LDADD = \

View File

@ -4,6 +4,15 @@
//
#define IDS_FAILED 1
#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
//

View File

@ -25,6 +25,7 @@
#include "CMutex.h"
#include "CThread.h"
#include "XThread.h"
#include "CFunctionJob.h"
#include "CLog.h"
#include "LogOutputters.h"
#include "CString.h"
@ -34,12 +35,15 @@
#define DAEMON_RUNNING(running_)
#if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
#include "CMSWindowsSecondaryScreen.h"
#include "CMSWindowsClientTaskBarReceiver.h"
#include "resource.h"
#undef DAEMON_RUNNING
#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
#elif UNIX_LIKE
#include "CXWindowsSecondaryScreen.h"
#include "CXWindowsClientTaskBarReceiver.h"
#endif
// 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
//
static CClient* s_client = NULL;
static CClient* s_client = NULL;
static CClientTaskBarReceiver* s_taskBarReceiver = NULL;
static
int
@ -137,6 +176,7 @@ realMain(void)
// open client
try {
s_taskBarReceiver->setClient(s_client);
s_client->open();
opened = true;
@ -160,11 +200,10 @@ realMain(void)
DAEMON_RUNNING(false); \
locked = true; \
} \
if (s_client != NULL) { \
if (opened) { \
s_client->close(); \
} \
if (opened) { \
s_client->close(); \
} \
s_taskBarReceiver->setClient(NULL); \
delete s_client; \
s_client = NULL; \
} while (false)
@ -205,6 +244,43 @@ realMain(void)
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
@ -273,7 +349,7 @@ help()
static
bool
isArg(int argi, int argc, const char** argv,
isArg(int argi, int argc, const char* const* argv,
const char* name1, const char* name2,
int minRequiredParameters = 0)
{
@ -294,7 +370,7 @@ isArg(int argi, int argc, const char** argv,
static
void
parse(int argc, const char** argv)
parse(int argc, const char* const* argv)
{
assert(ARG->m_pname != 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
@ -441,8 +507,6 @@ useSystemLog()
#if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
static bool s_hasImportantLogMessages = false;
//
@ -494,7 +558,10 @@ static
int
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
bye = &byeThrow;
@ -513,14 +580,62 @@ static
int
daemonStartup95(int, const char**)
{
useSystemLog();
return realMain();
CSystemLogger sysLogger(DAEMON_NAME);
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
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{
CArch arch;
CArch arch(instance);
CLOG;
CArgs args;
@ -533,52 +648,27 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
// send PRINT and FATAL output to a message box
CLOG->insert(new CMessageBoxOutputter);
// 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()) {
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;
}
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance);
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// parse command line
parse(__argc, const_cast<const char**>(__argv));
// daemonize if requested
int result;
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try {
result = 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));
result = kExitFailed;
}
}
else {
// cannot start a service from the command line so just
// run normally (except with log messages redirected).
useSystemLog();
result = realMain();
}
try {
// run in foreground or as a daemon
result = run(__argc, __argv);
}
else {
// run
result = realMain();
catch (...) {
// 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;
}
// done with task bar receiver
delete s_taskBarReceiver;
// let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting.
if (ARG->m_backend && s_hasImportantLogMessages) {
@ -598,7 +688,7 @@ static
int
daemonStartup(int, const char**)
{
useSystemLog();
CSystemLogger sysLogger(DAEMON_NAME);
return realMain();
}
@ -612,8 +702,13 @@ main(int argc, char** argv)
// get program name
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(argc, const_cast<const char**>(argv));
parse(argc, argv);
// daemonize if requested
int result;
@ -630,6 +725,9 @@ main(int argc, char** argv)
result = realMain();
}
// done with task bar receiver
delete s_taskBarReceiver;
return result;
}

View File

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

@ -15,19 +15,29 @@ NULL =
DEPTH = ../..
VDEPTH = ./$(VPATH)/$(DEPTH)
EXTRA_DIST = \
resource.h \
synergys.ico \
synergys.dsp \
synergys.rc \
EXTRA_DIST = \
CMSWindowsServerTaskBarReceiver.cpp \
CMSWindowsServerTaskBarReceiver.h \
resource.h \
synergys.ico \
synergys.dsp \
synergys.rc \
tb_error.ico \
tb_idle.ico \
tb_run.ico \
tb_wait.ico \
$(NULL)
MAINTAINERCLEANFILES = \
Makefile.in \
MAINTAINERCLEANFILES = \
Makefile.in \
$(NULL)
bin_PROGRAMS = synergys
synergys_SOURCES = \
CServerTaskBarReceiver.cpp \
CServerTaskBarReceiver.h \
CXWindowsServerTaskBarReceiver.cpp \
CXWindowsServerTaskBarReceiver.h \
synergys.cpp \
$(NULL)
synergys_LDADD = \

View File

@ -4,14 +4,24 @@
//
#define IDS_FAILED 1
#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
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_RESOURCE_VALUE 109
#define _APS_NEXT_COMMAND_VALUE 40005
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -24,6 +24,7 @@
#include "CMutex.h"
#include "CThread.h"
#include "XThread.h"
#include "CFunctionJob.h"
#include "CLog.h"
#include "LogOutputters.h"
#include "CArch.h"
@ -33,12 +34,15 @@
#define DAEMON_RUNNING(running_)
#if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
#include "CMSWindowsPrimaryScreen.h"
#include "CMSWindowsServerTaskBarReceiver.h"
#include "resource.h"
#undef DAEMON_RUNNING
#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
#elif UNIX_LIKE
#include "CXWindowsPrimaryScreen.h"
#include "CXWindowsServerTaskBarReceiver.h"
#endif
// 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
//
static CServer* s_server = NULL;
static CServer* s_server = NULL;
static CServerTaskBarReceiver* s_taskBarReceiver = NULL;
static
int
@ -168,6 +207,7 @@ realMain(void)
// open server
try {
s_taskBarReceiver->setServer(s_server);
s_server->open();
opened = true;
@ -177,18 +217,17 @@ realMain(void)
s_server->mainLoop();
// clean up
#define FINALLY do { \
if (!locked) { \
DAEMON_RUNNING(false); \
locked = true; \
} \
if (s_server != NULL) { \
if (opened) { \
s_server->close(); \
} \
} \
delete s_server; \
s_server = NULL; \
#define FINALLY do { \
if (!locked) { \
DAEMON_RUNNING(false); \
locked = true; \
} \
if (opened) { \
s_server->close(); \
} \
s_taskBarReceiver->setServer(NULL); \
delete s_server; \
s_server = NULL; \
} while (false)
FINALLY;
}
@ -227,6 +266,41 @@ realMain(void)
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
@ -329,7 +403,7 @@ PLATFORM_EXTRA
static
bool
isArg(int argi, int argc, const char** argv,
isArg(int argi, int argc, const char* const* argv,
const char* name1, const char* name2,
int minRequiredParameters = 0)
{
@ -350,7 +424,7 @@ isArg(int argi, int argc, const char** argv,
static
void
parse(int argc, const char** argv)
parse(int argc, const char* const* argv)
{
assert(ARG->m_pname != 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
@ -570,8 +634,6 @@ useSystemLog()
#if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
static bool s_hasImportantLogMessages = false;
//
@ -623,7 +685,10 @@ static
int
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
bye = &byeThrow;
@ -645,14 +710,65 @@ static
int
daemonStartup95(int, const char**)
{
useSystemLog();
return realMain();
CSystemLogger sysLogger(DAEMON_NAME);
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
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{
CArch arch;
CArch arch(instance);
CLOG;
CArgs args;
@ -665,55 +781,27 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
// send PRINT and FATAL output to a message box
CLOG->insert(new CMessageBoxOutputter);
// 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()) {
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;
}
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance);
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// parse command line
parse(__argc, const_cast<const char**>(__argv));
// load configuration
loadConfig();
// daemonize if requested
int result;
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try {
result = 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));
result = kExitFailed;
}
}
else {
// cannot start a service from the command line so just
// run normally (except with log messages redirected).
useSystemLog();
result = realMain();
}
try {
// run in foreground or as a daemon
result = run(__argc, __argv);
}
else {
// run
result = realMain();
catch (...) {
// 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;
}
// done with task bar receiver
delete s_taskBarReceiver;
// let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting.
if (ARG->m_backend && s_hasImportantLogMessages) {
@ -733,7 +821,7 @@ static
int
daemonStartup(int, const char**)
{
useSystemLog();
CSystemLogger sysLogger(DAEMON_NAME);
return realMain();
}
@ -747,8 +835,13 @@ main(int argc, char** argv)
// get program name
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(argc, const_cast<const char**>(argv));
parse(argc, argv);
// load configuration
loadConfig();
@ -768,6 +861,9 @@ main(int argc, char** argv)
result = realMain();
}
// done with task bar receiver
delete s_taskBarReceiver;
return result;
}

View File

@ -94,6 +94,14 @@ LINK32=link.exe
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\CMSWindowsServerTaskBarReceiver.cpp
# End Source File
# Begin Source File
SOURCE=.\CServerTaskBarReceiver.cpp
# End Source File
# Begin Source File
SOURCE=.\synergys.cpp
# End Source File
# Begin Source File
@ -106,6 +114,14 @@ SOURCE=.\synergys.rc
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\CMSWindowsServerTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\CServerTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\resource.h
# End Source File
# End Group
@ -116,6 +132,22 @@ SOURCE=.\resource.h
SOURCE=.\synergys.ico
# 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 Target
# 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_SLEEP
#undef ARCH_STRING
#undef ARCH_TASKBAR
#undef ARCH_TIME
// include appropriate architecture implementation
@ -35,6 +36,7 @@
# include "CArchNetworkWinsock.h"
# include "CArchSleepWindows.h"
# include "CArchStringWindows.h"
# include "CArchTaskBarWindows.h"
# include "CArchTimeWindows.h"
#elif UNIX_LIKE
# include "CArchConsoleUnix.h"
@ -47,6 +49,7 @@
# include "CArchNetworkBSD.h"
# include "CArchSleepUnix.h"
# include "CArchStringUnix.h"
# include "CArchTaskBarXWindows.h"
# include "CArchTimeUnix.h"
#endif
@ -82,6 +85,10 @@
# error unsupported platform for string
#endif
#if !defined(ARCH_TASKBAR)
# error unsupported platform for taskbar
#endif
#if !defined(ARCH_TIME)
# error unsupported platform for time
#endif
@ -92,7 +99,7 @@
CArch* CArch::s_instance = NULL;
CArch::CArch(ARCH_ARGS)
CArch::CArch(ARCH_ARGS* args)
{
// only once instance of CArch
assert(s_instance == NULL);
@ -108,11 +115,13 @@ CArch::CArch(ARCH_ARGS)
m_time = new ARCH_TIME;
m_console = new ARCH_CONSOLE;
m_daemon = new ARCH_DAEMON;
m_taskbar = new ARCH_TASKBAR(args);
}
CArch::~CArch()
{
// clean up
delete m_taskbar;
delete m_daemon;
delete m_console;
delete m_time;
@ -337,10 +346,10 @@ CArch::wait(CArchThread thread, double timeout)
return m_mt->wait(thread, timeout);
}
bool
CArch::waitForEvent(double timeout)
IArchMultithread::EWaitResult
CArch::waitForEvent(CArchThread thread, double timeout)
{
return m_mt->waitForEvent(timeout);
return m_mt->waitForEvent(thread, timeout);
}
bool
@ -577,6 +586,24 @@ CArch::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
CArch::time()
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -55,7 +55,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(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
CArchMultithreadWindows::waitForEvent(double timeout)
{
@ -499,6 +582,7 @@ CArchMultithreadWindows::waitForEvent(double timeout)
return false;
}
}
*/
bool
CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2)

View File

@ -69,7 +69,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(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 {
public:
//! Result of waitForEvent()
enum EWaitResult {
kEvent, //!< An event is pending
kExit, //!< Thread exited
kTimeout //!< Wait timed out
};
//! Type of thread entry point
typedef void* (*ThreadFunc)(void*);
//! Type of thread identifier
@ -102,10 +109,12 @@ public:
//! 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
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)
*/
@ -206,14 +215,18 @@ public:
//! Wait for a user event
/*!
Waits for up to \c timeout seconds for a pending user event.
Returns true if an event occurred, false otherwise.
Waits for up to \c timeout seconds for a pending user event or
\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.
(Cancellation point)
*/
virtual bool waitForEvent(double timeout) = 0;
virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
//! 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 \
CArchSleepWindows.cpp \
CArchStringWindows.cpp \
CArchTaskBarWindows.cpp \
CArchTimeWindows.cpp \
XArchWindows.cpp \
CArchConsoleWindows.h \
@ -37,6 +38,7 @@ EXTRA_DIST = \
CArchNetworkWinsock.h \
CArchSleepWindows.h \
CArchStringWindows.h \
CArchTaskBarWindows.h \
CArchTimeWindows.h \
XArchWindows.h \
$(NULL)
@ -59,6 +61,8 @@ libarch_a_SOURCES = \
IArchNetwork.h \
IArchSleep.h \
IArchString.h \
IArchTaskBar.h \
IArchTaskBarReceiver.h \
IArchTime.h \
XArch.h \
$(NULL)
@ -72,6 +76,7 @@ EXTRA_libarch_a_SOURCES = \
CArchNetworkBSD.cpp \
CArchSleepUnix.cpp \
CArchStringUnix.cpp \
CArchTaskBarXWindows.cpp \
CArchTimeUnix.cpp \
CMultibyte.cpp \
CMultibyteOS.cpp \
@ -87,6 +92,7 @@ EXTRA_libarch_a_SOURCES = \
CArchNetworkBSD.h \
CArchSleepUnix.h \
CArchStringUnix.h \
CArchTaskBarXWindows.h \
CArchTimeUnix.h \
XArchUnix.h \
$(NULL)

View File

@ -115,6 +115,10 @@ SOURCE=.\CArchDaemonWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchFileWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchImpl.h
# End Source File
# Begin Source File
@ -143,6 +147,10 @@ SOURCE=.\CArchStringWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchTaskBarWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchTimeWindows.h
# End Source File
# Begin Source File
@ -151,6 +159,14 @@ SOURCE=.\IArchConsole.h
# End 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
# End Source File
# Begin Source File
@ -171,6 +187,14 @@ SOURCE=.\IArchString.h
# End 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
# End Source File
# Begin Source File
@ -232,6 +256,11 @@ SOURCE=.\CArchStringWindows.cpp
# End Source File
# Begin Source File
SOURCE=.\CArchTaskBarWindows.cpp
# PROP Exclude_From_Build 1
# End Source File
# Begin Source File
SOURCE=.\CArchTimeWindows.cpp
# PROP Exclude_From_Build 1
# 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 "";
}
//
// 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;
};
//! 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

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@
#include "IClipboard.h"
#include "CNetworkAddress.h"
#include "CMutex.h"
#include "CJobList.h"
class CSecondaryScreen;
class CServerProxy;
@ -36,6 +37,13 @@ This class implements the top-level client algorithms for synergy.
*/
class CClient : public IScreenReceiver, public IClient {
public:
enum EStatus {
kNotRunning,
kRunning,
kError,
kMaxStatus
};
/*!
This client will attempt to connect the server using \c clientName
as its name.
@ -93,6 +101,22 @@ public:
*/
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
//@{
@ -103,6 +127,12 @@ public:
*/
bool wasRejected() const;
//! Get the status
/*!
Returns the current status and status message.
*/
EStatus getStatus(CString* = NULL) const;
//@}
// IScreenReceiver overrides
@ -140,6 +170,12 @@ public:
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
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
void openSecondaryScreen();
void closeSecondaryScreen();
@ -169,6 +205,11 @@ private:
bool m_ownClipboard[kClipboardEnd];
IClipboard::Time m_timeClipboard[kClipboardEnd];
CString m_dataClipboard[kClipboardEnd];
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
};
#endif

View File

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

View File

@ -97,10 +97,16 @@ CThread::wait(double timeout) const
return ARCH->wait(m_thread, timeout);
}
bool
CThread::waitForEvent(double timeout)
CThread::EWaitResult
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*

View File

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

View File

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

View File

@ -30,9 +30,11 @@ public:
/*!
Cancels the calling thread after \c timeout seconds unless destroyed
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
~CTimerThread();
@ -45,6 +47,7 @@ private:
private:
double m_timeout;
bool* m_timedOut;
CThread* m_callingThread;
CThread* m_timingThread;
};

View File

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

View File

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

View File

@ -122,6 +122,11 @@ CMSWindowsScreen::closeDesktop()
// remove timer
if (m_timer != 0) {
KillTimer(NULL, m_timer);
m_timer = 0;
}
if (m_oneShotTimer != 0) {
KillTimer(NULL, m_oneShotTimer);
m_oneShotTimer = 0;
}
// disconnect from desktop
@ -134,6 +139,18 @@ CMSWindowsScreen::closeDesktop()
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
CMSWindowsScreen::isMultimon() const
{
@ -205,8 +222,12 @@ CMSWindowsScreen::mainLoop()
event.m_result = 0;
for (;;) {
// wait for an event in a cancellable way
CThread::waitForEvent();
GetMessage(&event.m_msg, NULL, 0, 0);
if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) {
continue;
}
if (!PeekMessage(&event.m_msg, NULL, 0, 0, PM_REMOVE)) {
continue;
}
// handle quit message
if (event.m_msg.message == WM_QUIT) {
@ -467,27 +488,35 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event)
}
case WM_TIMER:
// if current desktop is not the input desktop then switch to it.
// windows 95 doesn't support multiple desktops so don't bother
// to check under it.
if (!m_is95Family) {
HDESK desk = openInputDesktop();
if (desk != NULL) {
if (isCurrentDesktop(desk)) {
CloseDesktop(desk);
}
else if (!m_screensaver->isActive()) {
// don't switch desktops when the screensaver is
// active. we'd most likely switch to the
// screensaver desktop which would have the side
// effect of forcing the screensaver to stop.
switchDesktop(desk);
}
else {
CloseDesktop(desk);
if (msg->wParam == m_timer) {
// if current desktop is not the input desktop then switch to it.
// windows 95 doesn't support multiple desktops so don't bother
// to check under it.
if (!m_is95Family) {
HDESK desk = openInputDesktop();
if (desk != NULL) {
if (isCurrentDesktop(desk)) {
CloseDesktop(desk);
}
else if (!m_screensaver->isActive()) {
// don't switch desktops when the screensaver is
// active. we'd most likely switch to the
// screensaver desktop which would have the side
// effect of forcing the screensaver to stop.
switchDesktop(desk);
}
else {
CloseDesktop(desk);
}
}
}
}
else if (msg->wParam == m_oneShotTimer) {
// one shot timer expired
KillTimer(NULL, m_oneShotTimer);
m_oneShotTimer = 0;
m_eventHandler->onOneShotTimerExpired(0);
}
return true;
}

View File

@ -64,6 +64,14 @@ public:
*/
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
//@{
@ -169,6 +177,9 @@ private:
// the timer used to check for desktop switching
UINT m_timer;
// the one shot timer
UINT m_oneShotTimer;
// the current desk and it's name
HDESK m_desk;
CString m_deskName;

View File

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

View File

@ -53,6 +53,7 @@ public:
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual void onOneShotTimerExpired(UInt32 id);
virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND);
@ -69,6 +70,7 @@ protected:
virtual void hideWindow();
virtual void warpCursor(SInt32 x, SInt32 y);
virtual void updateKeys();
virtual void releaseKeys();
virtual void setToggleState(KeyModifierMask);
virtual KeyModifierMask getToggleState() const;
@ -98,7 +100,6 @@ private:
KeyModifierMask, EKeyAction) const;
void doKeystrokes(const Keystrokes&, SInt32 count);
void releaseKeys();
void toggleKey(UINT virtualKey, KeyModifierMask mask);
UINT virtualKeyToScanCode(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);
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);
if (display != NULL) {
// release keys that are still pressed
releaseKeys(display);
doReleaseKeys(display);
// no longer impervious to server grabs
XTestGrabControl(display, False);
@ -901,7 +901,7 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const
}
void
CXWindowsSecondaryScreen::releaseKeys(Display* display)
CXWindowsSecondaryScreen::doReleaseKeys(Display* display)
{
assert(display != NULL);
@ -969,6 +969,15 @@ CXWindowsSecondaryScreen::updateKeys()
updateModifiers(display);
}
void
CXWindowsSecondaryScreen::releaseKeys()
{
CDisplayLock display(m_screen);
if (display != NULL) {
doReleaseKeys(display);
}
}
void
CXWindowsSecondaryScreen::updateModifiers(Display* display)
{

View File

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

View File

@ -66,7 +66,8 @@ CServer::CServer(const CString& serverName) :
m_switchWaitEngaged(false),
m_switchTwoTapDelay(0.0),
m_switchTwoTapEngaged(false),
m_switchTwoTapArmed(false)
m_switchTwoTapArmed(false),
m_status(kNotRunning)
{
// do nothing
}
@ -85,14 +86,17 @@ CServer::open()
try {
LOG((CLOG_INFO "opening screen"));
openPrimaryScreen();
setStatus(kNotRunning);
}
catch (XScreen&) {
catch (XScreen& e) {
// can't open screen
setStatus(kError, e.what());
LOG((CLOG_INFO "failed to open screen"));
throw;
}
catch (XUnknownClient& e) {
// can't open screen
setStatus(kServerNameUnknown);
LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str()));
throw;
}
@ -108,6 +112,7 @@ CServer::mainLoop()
}
try {
setStatus(kNotRunning);
LOG((CLOG_NOTE "starting server"));
// start listening for new clients
@ -138,11 +143,13 @@ CServer::mainLoop()
stopThreads(); \
delete m_httpServer; \
m_httpServer = NULL; \
runStatusJobs(); \
} while (false)
FINALLY;
}
catch (XMT& e) {
LOG((CLOG_ERR "server error: %s", e.what()));
setStatus(kError, e.what());
// clean up
LOG((CLOG_NOTE "stopping server"));
@ -151,12 +158,15 @@ CServer::mainLoop()
}
catch (XBase& e) {
LOG((CLOG_ERR "server error: %s", e.what()));
setStatus(kError, e.what());
// clean up
LOG((CLOG_NOTE "stopping server"));
FINALLY;
}
catch (XThread&) {
setStatus(kNotRunning);
// clean up
LOG((CLOG_NOTE "stopping server"));
FINALLY;
@ -164,6 +174,7 @@ CServer::mainLoop()
}
catch (...) {
LOG((CLOG_DEBUG "unknown server error"));
setStatus(kError);
// clean up
LOG((CLOG_NOTE "stopping server"));
@ -260,6 +271,9 @@ CServer::setConfig(const CConfig& config)
sendOptions(client);
}
// notify of status
runStatusJobs();
return true;
}
@ -287,12 +301,52 @@ CServer::setStreamFilterFactory(IStreamFilterFactory* adopted)
m_streamFilterFactory = adopted;
}
void
CServer::addStatusJob(IJob* job)
{
m_statusJobs.addJob(job);
}
void
CServer::removeStatusJob(IJob* job)
{
m_statusJobs.removeJob(job);
}
CString
CServer::getPrimaryScreenName() const
{
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
CServer::getConfig(CConfig* config) const
{
@ -302,6 +356,28 @@ CServer::getConfig(CConfig* config) const
*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
CServer::getActivePrimarySides() const
{
@ -325,6 +401,8 @@ CServer::getActivePrimarySides() const
void
CServer::onError()
{
setStatus(kError);
// stop all running threads but don't wait too long since some
// threads may be unable to proceed until this thread returns.
stopThreads(3.0);
@ -743,6 +821,10 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
case kBottom:
clearWait = (m_y <= ay + ah - 1 + zoneSize);
break;
default:
clearWait = false;
break;
}
if (clearWait) {
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 (!allowSwitch && m_switchWaitDelay > 0.0) {
if (isNewDirection) {
if (isNewDirection || !m_switchWaitEngaged) {
m_switchWaitEngaged = true;
m_switchWaitX = x;
m_switchWaitY = y;
@ -1413,6 +1495,7 @@ CServer::acceptClients(void*)
break;
}
catch (XSocketAddressInUse& e) {
setStatus(kError, e.what());
LOG((CLOG_WARN "bind failed: %s", e.what()));
// give up if we've waited too long
@ -1427,6 +1510,7 @@ CServer::acceptClients(void*)
}
// accept connections and begin processing them
setStatus(kRunning);
LOG((CLOG_DEBUG1 "waiting for client connections"));
for (;;) {
// accept connection
@ -1439,16 +1523,15 @@ CServer::acceptClients(void*)
startThread(new TMethodJob<CServer>(
this, &CServer::runClient, socket));
}
// clean up
delete listen;
}
catch (XBase& e) {
setStatus(kError, e.what());
LOG((CLOG_ERR "cannot listen for clients: %s", e.what()));
delete listen;
exitMainLoopWithError();
}
catch (...) {
setStatus(kNotRunning);
delete listen;
throw;
}
@ -1923,71 +2006,84 @@ CServer::addConnection(IClient* client)
LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str()));
CLock lock(&m_mutex);
{
CLock lock(&m_mutex);
// name must be in our configuration
if (!m_config.isScreen(client->getName())) {
throw XUnknownClient(client->getName());
// name must be in our configuration
if (!m_config.isScreen(client->getName())) {
throw XUnknownClient(client->getName());
}
// can only have one screen with a given name at any given time
if (m_clients.count(client->getName()) != 0) {
throw XDuplicateClient(client->getName());
}
// save screen info
m_clients.insert(std::make_pair(client->getName(), client));
LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str()));
}
// can only have one screen with a given name at any given time
if (m_clients.count(client->getName()) != 0) {
throw XDuplicateClient(client->getName());
}
// save screen info
m_clients.insert(std::make_pair(client->getName(), client));
LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str()));
runStatusJobs();
}
void
CServer::removeConnection(const CString& name)
{
LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str()));
CLock lock(&m_mutex);
bool updateStatus;
{
CLock lock(&m_mutex);
// find client
CClientList::iterator index = m_clients.find(name);
assert(index != m_clients.end());
// find client
CClientList::iterator index = m_clients.find(name);
assert(index != m_clients.end());
// if this is active screen then we have to jump off of it
IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active;
if (active == index->second && active != m_primaryClient) {
// record new position (center of primary screen)
m_primaryClient->getCursorCenter(m_x, m_y);
// if this is active screen then we have to jump off of it
IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active;
if (active == index->second && active != m_primaryClient) {
// record new position (center of primary screen)
m_primaryClient->getCursorCenter(m_x, m_y);
// stop waiting to switch if we were
if (active == m_switchScreen) {
clearSwitchState();
// stop waiting to switch if we were
if (active == m_switchScreen) {
clearSwitchState();
}
// 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));
// cut over
m_active = m_primaryClient;
// enter new screen (unless we already have because of the
// screen saver)
if (m_activeSaver == NULL) {
m_primaryClient->enter(m_x, m_y, m_seqNum,
m_primaryClient->getToggleMask(), false);
}
}
// 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));
// cut over
m_active = m_primaryClient;
// enter new screen (unless we already have because of the
// screen saver)
if (m_activeSaver == NULL) {
m_primaryClient->enter(m_x, m_y, m_seqNum,
m_primaryClient->getToggleMask(), false);
// if this screen had the cursor when the screen saver activated
// then we can't switch back to it when the screen saver
// deactivates.
if (m_activeSaver == index->second) {
m_activeSaver = NULL;
}
// done with client
delete index->second;
m_clients.erase(index);
// remove any thread for this client
m_clientThreads.erase(name);
updateStatus = (m_clients.size() <= 1);
}
// if this screen had the cursor when the screen saver activated
// then we can't switch back to it when the screen saver
// deactivates.
if (m_activeSaver == index->second) {
m_activeSaver = NULL;
if (updateStatus) {
runStatusJobs();
}
// done with client
delete index->second;
m_clients.erase(index);
// remove any thread for this client
m_clientThreads.erase(name);
}

View File

@ -22,9 +22,11 @@
#include "CCondVar.h"
#include "CMutex.h"
#include "CThread.h"
#include "CJobList.h"
#include "CStopwatch.h"
#include "stdlist.h"
#include "stdmap.h"
#include "stdvector.h"
class CClientProxy;
class CHTTPServer;
@ -42,6 +44,14 @@ This class implements the top-level server algorithms for synergy.
*/
class CServer : public IServer, public IPrimaryScreenReceiver {
public:
enum EStatus {
kNotRunning,
kRunning,
kServerNameUnknown,
kError,
kMaxStatus
};
/*!
The server will look itself up in the configuration using \c serverName
as its name.
@ -116,6 +126,22 @@ public:
*/
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
//@{
@ -132,6 +158,24 @@ public:
*/
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
@ -170,6 +214,12 @@ protected:
private:
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
UInt32 getActivePrimarySides() const;
@ -352,6 +402,11 @@ private:
CStopwatch m_switchTwoTapTimer;
bool m_switchTwoTapEngaged;
bool m_switchTwoTapArmed;
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
};
#endif

View File

@ -74,17 +74,6 @@ CSecondaryScreen::open()
// create and prepare our window
createWindow();
// assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
grabClipboard(id);
}
// update keyboard state
updateKeys();
// disable the screen saver
getScreen()->openScreensaver(false);
// subclass hook
onPostOpen();
@ -95,6 +84,30 @@ CSecondaryScreen::open()
close();
throw;
}
}
void
CSecondaryScreen::close()
{
onPreClose();
destroyWindow();
getScreen()->close();
onPostClose();
}
void
CSecondaryScreen::remoteControl()
{
// assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
grabClipboard(id);
}
// update keyboard state
updateKeys();
// disable the screen saver
getScreen()->openScreensaver(false);
// hide the cursor
{
@ -105,13 +118,9 @@ CSecondaryScreen::open()
}
void
CSecondaryScreen::close()
CSecondaryScreen::localControl()
{
onPreClose();
getScreen()->closeScreensaver();
destroyWindow();
getScreen()->close();
onPostClose();
}
void

View File

@ -41,11 +41,10 @@ public:
//! Open screen
/*!
Opens the screen. This includes initializing the screen,
hiding the cursor, and disabling the screen saver. It also causes
events to the reported to an IScreenReceiver (which is set through
some other interface). Calls close() before returning (rethrowing)
if it fails for any reason.
Opens the screen. It also causes events to the reported to an
IScreenReceiver (which is set through some other interface).
Calls close() before returning (rethrowing) if it fails for any
reason.
*/
void open();
@ -64,12 +63,26 @@ public:
*/
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
/*!
Closes the screen. This restores the screen saver, shows the cursor
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.
Closes the screen.
*/
void close();
@ -332,6 +345,13 @@ protected:
*/
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
/*!
Toggle modifiers that don't match the given state so that they do.