+#endif
+
+#if defined(Q_OS_WIN)
+static const char synergyConfigName[] = "synergy.sgc";
+static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*.sgc);;All files (*.*)"));
+#else
+static const char synergyConfigName[] = "synergy.conf";
+static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*.conf);;All files (*.*)"));
+#endif
+
+static const char* synergyIconFiles[] =
+{
+ ":/res/icons/16x16/synergy-disconnected.png",
+ ":/res/icons/16x16/synergy-disconnected.png",
+ ":/res/icons/16x16/synergy-connected.png"
+};
+
+class QThreadImpl : public QThread
+{
+public:
+ static void msleep(unsigned long msecs)
+ {
+ QThread::msleep(msecs);
+ }
+};
+
+MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
+ m_Settings(settings),
+ m_AppConfig(appConfig),
+ m_pSynergy(NULL),
+ m_SynergyState(synergyDisconnected),
+ m_ServerConfig(&m_Settings, 5, 3),
+ m_pTempConfigFile(NULL),
+ m_pTrayIcon(NULL),
+ m_pTrayIconMenu(NULL),
+ m_alreadyHidden(false),
+ m_SetupWizard(NULL)
+{
+ setupUi(this);
+
+ createMenuBar();
+ loadSettings();
+ initConnections();
+
+ m_pUpdateIcon->hide();
+ m_pUpdateLabel->hide();
+ m_versionChecker.setApp(appPath(appConfig.synergycName()));
+
+ m_SetupWizard = new SetupWizard(*this, false);
+ connect(m_SetupWizard, SIGNAL(finished(int)), this, SLOT(refreshStartButton()));
+
+ if (appConfig.processMode() == Service)
+ {
+ connect(&m_IpcLogReader, SIGNAL(receivedLine(const QString&)), this, SLOT(appendLog(const QString&)));
+ m_IpcLogReader.start();
+ }
+}
+
+MainWindow::~MainWindow()
+{
+ stopSynergy();
+ saveSettings();
+ delete m_SetupWizard;
+}
+
+void MainWindow::start(bool firstRun)
+{
+ refreshStartButton();
+
+ if (!firstRun && appConfig().autoConnect() && appConfig().processMode() == Desktop)
+ startSynergy();
+
+ createTrayIcon();
+
+ // always show. auto-hide only happens when we have a connection.
+ show();
+
+ m_versionChecker.checkLatest();
+}
+
+void MainWindow::refreshStartButton()
+{
+ if (appConfig().processMode() == Service)
+ {
+ m_pButtonToggleStart->setText(tr("&Apply"));
+ }
+ else
+ {
+ m_pButtonToggleStart->setText(tr("&Start"));
+ }
+}
+
+void MainWindow::setStatus(const QString &status)
+{
+ m_pStatusLabel->setText(status);
+}
+
+void MainWindow::createTrayIcon()
+{
+ m_pTrayIconMenu = new QMenu(this);
+
+ m_pTrayIconMenu->addAction(m_pActionStartSynergy);
+ m_pTrayIconMenu->addAction(m_pActionStopSynergy);
+ m_pTrayIconMenu->addSeparator();
+
+ m_pTrayIconMenu->addAction(m_pActionMinimize);
+ m_pTrayIconMenu->addAction(m_pActionRestore);
+ m_pTrayIconMenu->addSeparator();
+ m_pTrayIconMenu->addAction(m_pActionQuit);
+
+ m_pTrayIcon = new QSystemTrayIcon(this);
+ m_pTrayIcon->setContextMenu(m_pTrayIconMenu);
+
+ setIcon(synergyDisconnected);
+
+ m_pTrayIcon->show();
+}
+
+void MainWindow::createMenuBar()
+{
+ QMenuBar* menubar = new QMenuBar(this);
+ QMenu* pMenuFile = new QMenu(tr("&File"), menubar);
+ QMenu* pMenuEdit = new QMenu(tr("&Edit"), menubar);
+ QMenu* pMenuWindow = new QMenu(tr("&Window"), menubar);
+ QMenu* pMenuHelp = new QMenu(tr("&Help"), menubar);
+
+ menubar->addAction(pMenuFile->menuAction());
+ menubar->addAction(pMenuEdit->menuAction());
+#if !defined(Q_OS_MAC)
+ menubar->addAction(pMenuWindow->menuAction());
+#endif
+ menubar->addAction(pMenuHelp->menuAction());
+
+ pMenuFile->addAction(m_pActionStartSynergy);
+ pMenuFile->addAction(m_pActionStopSynergy);
+ pMenuFile->addSeparator();
+ pMenuFile->addAction(m_pActionWizard);
+ pMenuFile->addAction(m_pActionSave);
+ pMenuFile->addSeparator();
+ pMenuFile->addAction(m_pActionQuit);
+ pMenuEdit->addAction(m_pActionSettings);
+ pMenuWindow->addAction(m_pActionMinimize);
+ pMenuWindow->addAction(m_pActionRestore);
+ pMenuHelp->addAction(m_pActionAbout);
+
+ setMenuBar(menubar);
+}
+
+void MainWindow::loadSettings()
+{
+ // the next two must come BEFORE loading groupServerChecked and groupClientChecked or
+ // disabling and/or enabling the right widgets won't automatically work
+ m_pRadioExternalConfig->setChecked(settings().value("externalConfig", false).toBool());
+ m_pRadioInternalConfig->setChecked(settings().value("internalConfig", true).toBool());
+
+ m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool());
+ m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString());
+ m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool());
+ m_pLineEditHostname->setText(settings().value("serverHostname").toString());
+}
+
+void MainWindow::initConnections()
+{
+ connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide()));
+ connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal()));
+ connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(startSynergy()));
+ connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy()));
+ connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
+ connect(&m_versionChecker, SIGNAL(updateFound(const QString&)), this, SLOT(updateFound(const QString&)));
+
+ if (m_pTrayIcon)
+ connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
+}
+
+void MainWindow::saveSettings()
+{
+ // program settings
+ settings().setValue("groupServerChecked", m_pGroupServer->isChecked());
+ settings().setValue("externalConfig", m_pRadioExternalConfig->isChecked());
+ settings().setValue("configFile", m_pLineEditConfigFile->text());
+ settings().setValue("internalConfig", m_pRadioInternalConfig->isChecked());
+ settings().setValue("groupClientChecked", m_pGroupClient->isChecked());
+ settings().setValue("serverHostname", m_pLineEditHostname->text());
+
+ settings().sync();
+}
+
+void MainWindow::setIcon(qSynergyState state)
+{
+ QIcon icon;
+ icon.addFile(synergyIconFiles[state]);
+
+ if (m_pTrayIcon)
+ m_pTrayIcon->setIcon(icon);
+}
+
+void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
+{
+ if (reason == QSystemTrayIcon::DoubleClick)
+ {
+ if (isVisible())
+ {
+ hide();
+ }
+ else
+ {
+ showNormal();
+ activateWindow();
+ }
+ }
+}
+
+void MainWindow::logOutput()
+{
+ if (m_pSynergy)
+ {
+ QString text(m_pSynergy->readAllStandardOutput());
+ foreach(QString line, text.split(QRegExp("\r|\n|\r\n")))
+ {
+ if (!line.isEmpty())
+ {
+ appendLog(line);
+ if (line.contains("has connected") ||
+ line.contains("connected to server"))
+ {
+ // only set connected state and hide, if we get
+ // "has connected" message. this is a little bit
+ // hacky, but it works for now (until we have IPC).
+ setSynergyState(synergyConnected);
+
+ // only hide once after each new connection.
+ if (!m_alreadyHidden && appConfig().autoHide())
+ {
+ hide();
+ m_alreadyHidden = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+void MainWindow::logError()
+{
+ if (m_pSynergy)
+ {
+ appendLog(m_pSynergy->readAllStandardError());
+ }
+}
+
+void MainWindow::updateFound(const QString &version)
+{
+ m_pUpdateIcon->show();
+ m_pUpdateLabel->show();
+ m_pUpdateLabel->setText(
+ tr("Version %1 is now available, visit website.
")
+ .arg(version).arg("http://synergy-foss.org"));
+}
+
+void MainWindow::appendLog(const QString& text)
+{
+ foreach(QString line, text.split(QRegExp("\r|\n|\r\n")))
+ if (!line.isEmpty())
+ m_pLogOutput->append(line);
+}
+
+void MainWindow::clearLog()
+{
+ m_pLogOutput->clear();
+}
+
+void MainWindow::startSynergy()
+{
+ // TODO: refactor this out into 2 methods.
+ bool desktopMode = appConfig().processMode() == Desktop;
+ bool serviceMode = appConfig().processMode() == Service;
+
+ if (desktopMode)
+ {
+ // cause the service to stop creating processes.
+ sendDaemonCommand("", false);
+
+ stopSynergy();
+ setSynergyState(synergyConnecting);
+ }
+
+ QString app;
+ QStringList args;
+
+ args << "-f" << "--no-tray" << "--debug" << appConfig().logLevelText();
+
+ if (!appConfig().screenName().isEmpty())
+ args << "--name" << appConfig().screenName();
+
+ if (appConfig().gameModeIndex() != 0)
+ {
+ if (appConfig().gameModeIndex() == 1)
+ {
+ args << "--game-mode" << "xinput";
+ }
+ else if (appConfig().gameModeIndex() == 2)
+ {
+ args << "--game-mode" << "joyinfoex";
+ }
+
+ if (appConfig().gamePollingDynamic())
+ {
+ args << "--game-poll" << "dynamic";
+ }
+ else
+ {
+ args << "--game-poll" << "static";
+ args << "--game-poll-freq" << QString::number(appConfig().gamePollingFrequency());
+ }
+ }
+
+ if (desktopMode)
+ {
+ setSynergyProcess(new QProcess(this));
+ }
+
+ if ((synergyType() == synergyClient && !clientArgs(args, app))
+ || (synergyType() == synergyServer && !serverArgs(args, app)))
+ {
+ if (desktopMode)
+ {
+ stopSynergy();
+ return;
+ }
+ }
+
+ if (desktopMode)
+ {
+ connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus)));
+ connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput()));
+ connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError()));
+ }
+
+ // put a space between last log output and new instance.
+ if (!m_pLogOutput->toPlainText().isEmpty())
+ appendLog("");
+
+ if (desktopMode)
+ {
+ appendLog("starting " + QString(synergyType() == synergyServer ? "server" : "client"));
+ }
+
+ if (serviceMode)
+ {
+ appendLog("applying service mode: " + QString(synergyType() == synergyServer ? "server" : "client"));
+ }
+
+ // show command if debug log level...
+ if (appConfig().logLevel() >= 4) {
+ appendLog(QString("command: %1 %2").arg(app, args.join(" ")));
+ }
+
+ appendLog("config file: " + configFilename());
+ appendLog("log level: " + appConfig().logLevelText());
+
+ if (appConfig().logToFile())
+ appendLog("log file: " + appConfig().logFilename());
+
+ if (desktopMode)
+ {
+ synergyProcess()->start(app, args);
+ if (!synergyProcess()->waitForStarted())
+ {
+ stopSynergy();
+ show();
+ QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable
%1
could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app)));
+ return;
+ }
+ }
+
+ if (serviceMode)
+ {
+ QString command(app + " " + args.join(" "));
+ sendDaemonCommand(command, true);
+ }
+}
+
+bool MainWindow::clientArgs(QStringList& args, QString& app)
+{
+ app = appPath(appConfig().synergycName());
+
+ if (!QFile::exists(app))
+ {
+ show();
+ QMessageBox::warning(this, tr("Synergy client not found"),
+ tr("The executable for the synergy client does not exist."));
+ return false;
+ }
+
+ if (m_pLineEditHostname->text().isEmpty())
+ {
+ show();
+ QMessageBox::warning(this, tr("Hostname is empty"),
+ tr("Please fill in a hostname for the synergy client to connect to."));
+ return false;
+ }
+
+ if (appConfig().logToFile())
+ {
+ appConfig().persistLogDir();
+ args << "--log" << appConfig().logFilename();
+ }
+
+ args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port());
+
+ return true;
+}
+
+QString MainWindow::configFilename()
+{
+ QString filename;
+ if (m_pRadioInternalConfig->isChecked())
+ {
+ // TODO: no need to use a temporary file, since we need it to
+ // be permenant (since it'll be used for Windows services, etc).
+ m_pTempConfigFile = new QTemporaryFile();
+ if (!m_pTempConfigFile->open())
+ {
+ QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start synergy can not be written."));
+ return false;
+ }
+
+ serverConfig().save(*m_pTempConfigFile);
+ filename = m_pTempConfigFile->fileName();
+
+ m_pTempConfigFile->close();
+ }
+ else
+ {
+ if (!QFile::exists(m_pLineEditConfigFile->text()))
+ {
+ if (QMessageBox::warning(this, tr("Configuration filename invalid"),
+ tr("You have not filled in a valid configuration file for the synergy server. "
+ "Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes
+ || !on_m_pButtonBrowseConfigFile_clicked())
+ return false;
+ }
+
+ filename = m_pLineEditConfigFile->text();
+ }
+ return filename;
+}
+
+QString MainWindow::address()
+{
+ return (!appConfig().interface().isEmpty() ? appConfig().interface() : "") + ":" + QString::number(appConfig().port());
+}
+
+QString MainWindow::appPath(const QString& name)
+{
+ return appConfig().synergyProgramDir() + name;
+}
+
+bool MainWindow::serverArgs(QStringList& args, QString& app)
+{
+ app = appPath(appConfig().synergysName());
+
+ if (!QFile::exists(app))
+ {
+ QMessageBox::warning(this, tr("Synergy server not found"),
+ tr("The executable for the synergy server does not exist."));
+ return false;
+ }
+
+ if (appConfig().logToFile())
+ {
+ appConfig().persistLogDir();
+ args << "--log" << appConfig().logFilename();
+ }
+
+ args << "-c" << configFilename() << "--address" << address();
+
+ return true;
+}
+
+void MainWindow::stopSynergy()
+{
+ if (synergyProcess())
+ {
+ appendLog("stopping synergy");
+
+ if (synergyProcess()->isOpen())
+ synergyProcess()->close();
+ delete synergyProcess();
+ setSynergyProcess(NULL);
+
+ setSynergyState(synergyDisconnected);
+ }
+
+ // HACK: deleting the object deletes the physical file, which is
+ // bad, since it could be in use by the Windows service!
+ //delete m_pTempConfigFile;
+ m_pTempConfigFile = NULL;
+
+ // reset so that new connects cause auto-hide.
+ m_alreadyHidden = false;
+}
+
+void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus)
+{
+ // on Windows, we always seem to have an exit code != 0.
+#if !defined(Q_OS_WIN)
+ if (exitCode != 0)
+ {
+ QMessageBox::critical(this, tr("Synergy terminated with an error"), QString(tr("Synergy terminated unexpectedly with an exit code of %1.
Please see the log output for details.")).arg(exitCode));
+ stopSynergy();
+ }
+#else
+ Q_UNUSED(exitCode);
+#endif
+
+ setSynergyState(synergyDisconnected);
+
+ // do not call stopSynergy() in case of clean synergy shutdown, because this must have (well, should have...)
+ // come from our own delete synergyProcess() in stopSynergy(), so we would do a double-delete...
+}
+
+void MainWindow::setSynergyState(qSynergyState state)
+{
+ // ignore state stuff when in service mode (for now anyway).
+ if (appConfig().processMode() == Service)
+ return;
+
+ if (synergyState() == state)
+ return;
+
+ if (state == synergyConnected || state == synergyConnecting)
+ {
+ disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
+ connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
+ m_pButtonToggleStart->setText(tr("&Stop"));
+ }
+ else
+ {
+ disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
+ connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
+ m_pButtonToggleStart->setText(tr("&Start"));
+ }
+
+ m_pGroupClient->setEnabled(state == synergyDisconnected);
+ m_pGroupServer->setEnabled(state == synergyDisconnected);
+ m_pActionStartSynergy->setEnabled(state == synergyDisconnected);
+ m_pActionStopSynergy->setEnabled(state == synergyConnected);
+
+ switch (state)
+ {
+ case synergyConnected:
+ setStatus(tr("Synergy is running."));
+ break;
+ case synergyConnecting:
+ setStatus(tr("Synergy is starting."));
+ break;
+ case synergyDisconnected:
+ setStatus(tr("Synergy is not running."));
+ break;
+ }
+
+ setIcon(state);
+
+ m_SynergyState = state;
+}
+
+void MainWindow::setVisible(bool visible)
+{
+ m_pActionMinimize->setEnabled(visible);
+ m_pActionRestore->setEnabled(!visible);
+ QMainWindow::setVisible(visible);
+
+#if MAC_OS_X_VERSION_10_7
+ // dock hide only supported on lion :(
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ GetCurrentProcess(&psn);
+ if (visible)
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ else
+ TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
+#endif
+}
+
+bool MainWindow::on_m_pButtonBrowseConfigFile_clicked()
+{
+ QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a synergys config file"), QString(), synergyConfigFilter);
+
+ if (!fileName.isEmpty())
+ {
+ m_pLineEditConfigFile->setText(fileName);
+ return true;
+ }
+
+ return false;
+}
+
+bool MainWindow::on_m_pActionSave_triggered()
+{
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as..."));
+
+ if (!fileName.isEmpty() && !serverConfig().save(fileName))
+ {
+ QMessageBox::warning(this, tr("Save failed"), tr("Could not save configuration to file."));
+ return true;
+ }
+
+ return false;
+}
+
+void MainWindow::on_m_pActionAbout_triggered()
+{
+ AboutDialog dlg(this, appPath(appConfig().synergycName()));
+ dlg.exec();
+}
+
+void MainWindow::on_m_pActionSettings_triggered()
+{
+ SettingsDialog dlg(this, appConfig());
+ dlg.exec();
+}
+
+void MainWindow::on_m_pButtonConfigureServer_clicked()
+{
+ ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName());
+ dlg.exec();
+}
+
+void MainWindow::sendDaemonCommand(const QString& command, bool showErrors)
+{
+ sendIpcMessage(Command, command.toStdString().c_str(), showErrors);
+}
+
+// TODO: put this in an IPC client class.
+void MainWindow::sendIpcMessage(qIpcMessage type, const char* data, bool showErrors)
+{
+#if defined(Q_OS_WIN)
+
+ const WCHAR* name = L"\\\\.\\pipe\\Synergy";
+ char message[1024];
+ message[0] = type;
+ char* messagePtr = message;
+ messagePtr++;
+ strcpy(messagePtr, data);
+
+ HANDLE pipe = CreateFile(
+ name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+ if (showErrors && pipe == INVALID_HANDLE_VALUE)
+ {
+ appendLog(QString("ERROR: could not connect to service, error: ") +
+ QString::number(GetLastError()));
+ return;
+ }
+
+ DWORD dwMode = PIPE_READMODE_MESSAGE;
+ BOOL stateSuccess = SetNamedPipeHandleState(pipe, &dwMode, NULL, NULL);
+
+ if (showErrors && !stateSuccess)
+ {
+ appendLog(QString("ERROR: could not set service pipe state, error: ") +
+ QString::number(GetLastError()));
+ return;
+ }
+
+ DWORD written;
+ BOOL writeSuccess = WriteFile(
+ pipe, message, strlen(message), &written, NULL);
+
+ if (showErrors && !writeSuccess)
+ {
+ appendLog(QString("ERROR: could not write to service pipe, error: ") +
+ QString::number(GetLastError()));
+ return;
+ }
+
+ CloseHandle(pipe);
+
+#endif
+}
+
+void MainWindow::on_m_pActionWizard_triggered()
+{
+ m_SetupWizard->show();
+}
diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h
new file mode 100644
index 00000000..d0dfb9b7
--- /dev/null
+++ b/src/gui/src/MainWindow.h
@@ -0,0 +1,149 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(MAINWINDOW__H)
+
+#define MAINWINDOW__H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "ui_MainWindowBase.h"
+
+#include "ServerConfig.h"
+#include "AppConfig.h"
+#include "VersionChecker.h"
+#include "IpcLogReader.h"
+
+class QAction;
+class QMenu;
+class QLineEdit;
+class QGroupBox;
+class QPushButton;
+class QTextEdit;
+class QComboBox;
+class QTabWidget;
+class QCheckBox;
+class QRadioButton;
+class QTemporaryFile;
+
+class LogDialog;
+class QSynergyApplication;
+class SetupWizard;
+
+class MainWindow : public QMainWindow, public Ui::MainWindowBase
+{
+ Q_OBJECT
+
+ friend class QSynergyApplication;
+ friend class SetupWizard;
+
+ public:
+ enum qSynergyState
+ {
+ synergyDisconnected,
+ synergyConnecting,
+ synergyConnected
+ };
+
+ enum qSynergyType
+ {
+ synergyClient,
+ synergyServer
+ };
+
+ enum qIpcMessage {
+ Command = 1
+ };
+
+ public:
+ MainWindow(QSettings& settings, AppConfig& appConfig);
+ ~MainWindow();
+
+ public:
+ void setVisible(bool visible);
+ int synergyType() const { return m_pGroupClient->isChecked() ? synergyClient : synergyServer; }
+ int synergyState() const { return m_SynergyState; }
+ QString hostname() const { return m_pLineEditHostname->text(); }
+ QString configFilename();
+ QString address();
+ QString appPath(const QString& name);
+ void start(bool firstRun);
+ void clearLog();
+
+ public slots:
+ void appendLog(const QString& text);
+
+ protected slots:
+ void on_m_pGroupClient_toggled(bool on) { m_pGroupServer->setChecked(!on); }
+ void on_m_pGroupServer_toggled(bool on) { m_pGroupClient->setChecked(!on); }
+ bool on_m_pButtonBrowseConfigFile_clicked();
+ void on_m_pButtonConfigureServer_clicked();
+ bool on_m_pActionSave_triggered();
+ void on_m_pActionAbout_triggered();
+ void on_m_pActionSettings_triggered();
+ void on_m_pActionWizard_triggered();
+ void synergyFinished(int exitCode, QProcess::ExitStatus);
+ void iconActivated(QSystemTrayIcon::ActivationReason reason);
+ void startSynergy();
+ void stopSynergy();
+ void logOutput();
+ void logError();
+ void updateFound(const QString& version);
+ void refreshStartButton();
+
+ protected:
+ QSettings& settings() { return m_Settings; }
+ AppConfig& appConfig() { return m_AppConfig; }
+ QProcess*& synergyProcess() { return m_pSynergy; }
+ void setSynergyProcess(QProcess* p) { m_pSynergy = p; }
+ ServerConfig& serverConfig() { return m_ServerConfig; }
+ void initConnections();
+ void createMenuBar();
+ void createStatusBar();
+ void createTrayIcon();
+ void loadSettings();
+ void saveSettings();
+ void setIcon(qSynergyState state);
+ void setSynergyState(qSynergyState state);
+ bool checkForApp(int which, QString& app);
+ bool clientArgs(QStringList& args, QString& app);
+ bool serverArgs(QStringList& args, QString& app);
+ void setStatus(const QString& status);
+ void sendDaemonCommand(const QString& command, bool showErrors);
+ void sendIpcMessage(qIpcMessage type, const char* buffer, bool showErrors);
+
+ private:
+ QSettings& m_Settings;
+ AppConfig& m_AppConfig;
+ QProcess* m_pSynergy;
+ int m_SynergyState;
+ ServerConfig m_ServerConfig;
+ QTemporaryFile* m_pTempConfigFile;
+ QSystemTrayIcon* m_pTrayIcon;
+ QMenu* m_pTrayIconMenu;
+ bool m_alreadyHidden;
+ VersionChecker m_versionChecker;
+ SetupWizard* m_SetupWizard;
+ IpcLogReader m_IpcLogReader;
+};
+
+#endif
+
diff --git a/src/gui/src/NewScreenWidget.cpp b/src/gui/src/NewScreenWidget.cpp
new file mode 100644
index 00000000..f3e4b25e
--- /dev/null
+++ b/src/gui/src/NewScreenWidget.cpp
@@ -0,0 +1,47 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "NewScreenWidget.h"
+#include "ScreenSetupModel.h"
+
+#include
+#include
+
+NewScreenWidget::NewScreenWidget(QWidget* parent) :
+ QLabel(parent)
+{
+}
+
+void NewScreenWidget::mousePressEvent(QMouseEvent* event)
+{
+ Screen newScreen(tr("Unnamed"));
+
+ QByteArray itemData;
+ QDataStream dataStream(&itemData, QIODevice::WriteOnly);
+ dataStream << -1 << -1 << newScreen;
+
+ QMimeData* pMimeData = new QMimeData;
+ pMimeData->setData(ScreenSetupModel::mimeType(), itemData);
+
+ QDrag* pDrag = new QDrag(this);
+ pDrag->setMimeData(pMimeData);
+ pDrag->setPixmap(*pixmap());
+ pDrag->setHotSpot(event->pos());
+
+ pDrag->exec(Qt::CopyAction, Qt::CopyAction);
+}
+
diff --git a/src/gui/src/NewScreenWidget.h b/src/gui/src/NewScreenWidget.h
new file mode 100644
index 00000000..a26fceb3
--- /dev/null
+++ b/src/gui/src/NewScreenWidget.h
@@ -0,0 +1,39 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(NEWSCREENWIDGET__H)
+
+#define NEWSCREENWIDGET__H
+
+#include
+
+class QMouseEvent;
+class QWidget;
+
+class NewScreenWidget : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ NewScreenWidget(QWidget* parent);
+
+ protected:
+ void mousePressEvent(QMouseEvent* event);
+};
+
+#endif
+
diff --git a/src/gui/src/QSynergyApplication.cpp b/src/gui/src/QSynergyApplication.cpp
new file mode 100644
index 00000000..c0b0dcf8
--- /dev/null
+++ b/src/gui/src/QSynergyApplication.cpp
@@ -0,0 +1,38 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "QSynergyApplication.h"
+#include "MainWindow.h"
+
+#include
+#include
+
+QSynergyApplication::QSynergyApplication(int& argc, char** argv) :
+ QApplication(argc, argv)
+{
+}
+
+void QSynergyApplication::commitData(QSessionManager&)
+{
+ foreach(QWidget* widget, topLevelWidgets())
+ {
+ MainWindow* mainWindow = qobject_cast(widget);
+ if (mainWindow)
+ mainWindow->saveSettings();
+ }
+}
+
diff --git a/src/gui/src/QSynergyApplication.h b/src/gui/src/QSynergyApplication.h
new file mode 100644
index 00000000..a6e9223f
--- /dev/null
+++ b/src/gui/src/QSynergyApplication.h
@@ -0,0 +1,36 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(QSYNERGYAPPLICATION__H)
+
+#define QSYNERGYAPPLICATION__H
+
+#include
+
+class QSessionManager;
+
+class QSynergyApplication : public QApplication
+{
+ public:
+ QSynergyApplication(int& argc, char** argv);
+
+ public:
+ void commitData(QSessionManager& manager);
+};
+
+#endif
+
diff --git a/src/gui/src/Screen.cpp b/src/gui/src/Screen.cpp
new file mode 100644
index 00000000..b35b2eee
--- /dev/null
+++ b/src/gui/src/Screen.cpp
@@ -0,0 +1,146 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "Screen.h"
+
+#include
+#include
+
+Screen::Screen() :
+ m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")),
+ m_Swapped(false)
+{
+ init();
+}
+
+Screen::Screen(const QString& name) :
+ m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")),
+ m_Swapped(false)
+{
+ init();
+ setName(name);
+}
+
+void Screen::init()
+{
+ name().clear();
+ aliases().clear();
+ modifiers().clear();
+ switchCorners().clear();
+ fixes().clear();
+ setSwitchCornerSize(0);
+
+ // m_Modifiers, m_SwitchCorners and m_Fixes are QLists we use like fixed-size arrays,
+ // thus we need to make sure to fill them with the required number of elements.
+ for (int i = 0; i < NumModifiers; i++)
+ modifiers() << i;
+
+ for (int i = 0; i < NumSwitchCorners; i++)
+ switchCorners() << false;
+
+ for (int i = 0; i < NumFixes; i++)
+ fixes() << false;
+}
+
+void Screen::loadSettings(QSettings& settings)
+{
+ setName(settings.value("name").toString());
+
+ if (name().isEmpty())
+ return;
+
+ setSwitchCornerSize(settings.value("switchCornerSize").toInt());
+
+ readSettings(settings, aliases(), "alias", QString(""));
+ readSettings(settings, modifiers(), "modifier", static_cast(DefaultMod), NumModifiers);
+ readSettings(settings, switchCorners(), "switchCorner", false, NumSwitchCorners);
+ readSettings(settings, fixes(), "fix", false, NumFixes);
+}
+
+void Screen::saveSettings(QSettings& settings) const
+{
+ settings.setValue("name", name());
+
+ if (name().isEmpty())
+ return;
+
+ settings.setValue("switchCornerSize", switchCornerSize());
+
+ writeSettings(settings, aliases(), "alias");
+ writeSettings(settings, modifiers(), "modifier");
+ writeSettings(settings, switchCorners(), "switchCorner");
+ writeSettings(settings, fixes(), "fix");
+}
+
+QTextStream& Screen::writeScreensSection(QTextStream& outStream) const
+{
+ outStream << "\t" << name() << ":" << endl;
+
+ for (int i = 0; i < modifiers().size(); i++)
+ if (modifier(i) != i)
+ outStream << "\t\t" << modifierName(i) << " = " << modifierName(modifier(i)) << endl;
+
+ for (int i = 0; i < fixes().size(); i++)
+ outStream << "\t\t" << fixName(i) << " = " << (fixes()[i] ? "true" : "false") << endl;
+
+ outStream << "\t\t" << "switchCorners = none ";
+ for (int i = 0; i < switchCorners().size(); i++)
+ if (switchCorners()[i])
+ outStream << "+" << switchCornerName(i) << " ";
+ outStream << endl;
+
+ outStream << "\t\t" << "switchCornerSize = " << switchCornerSize() << endl;
+
+ return outStream;
+}
+
+QTextStream& Screen::writeAliasesSection(QTextStream& outStream) const
+{
+ if (!aliases().isEmpty())
+ {
+ outStream << "\t" << name() << ":" << endl;
+
+ foreach (const QString& alias, aliases())
+ outStream << "\t\t" << alias << endl;
+ }
+
+ return outStream;
+}
+
+QDataStream& operator<<(QDataStream& outStream, const Screen& screen)
+{
+ return outStream
+ << screen.name()
+ << screen.switchCornerSize()
+ << screen.aliases()
+ << screen.modifiers()
+ << screen.switchCorners()
+ << screen.fixes()
+ ;
+}
+
+QDataStream& operator>>(QDataStream& inStream, Screen& screen)
+{
+ return inStream
+ >> screen.m_Name
+ >> screen.m_SwitchCornerSize
+ >> screen.m_Aliases
+ >> screen.m_Modifiers
+ >> screen.m_SwitchCorners
+ >> screen.m_Fixes
+ ;
+}
diff --git a/src/gui/src/Screen.h b/src/gui/src/Screen.h
new file mode 100644
index 00000000..63771281
--- /dev/null
+++ b/src/gui/src/Screen.h
@@ -0,0 +1,104 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(SCREEN__H)
+
+#define SCREEN__H
+
+#include
+#include
+#include
+#include
+
+#include "BaseConfig.h"
+
+class QSettings;
+class QTextStream;
+
+class ScreenSettingsDialog;
+
+class Screen : public BaseConfig
+{
+ friend QDataStream& operator<<(QDataStream& outStream, const Screen& screen);
+ friend QDataStream& operator>>(QDataStream& inStream, Screen& screen);
+ friend class ScreenSettingsDialog;
+ friend class ScreenSetupModel;
+ friend class ScreenSetupView;
+
+ public:
+ Screen();
+ Screen(const QString& name);
+
+ public:
+ const QPixmap* pixmap() const { return &m_Pixmap; }
+ const QString& name() const { return m_Name; }
+ const QStringList& aliases() const { return m_Aliases; }
+
+ bool isNull() const { return m_Name.isEmpty(); }
+ int modifier(int m) const { return m_Modifiers[m] == DefaultMod ? m : m_Modifiers[m]; }
+ const QList& modifiers() const { return m_Modifiers; }
+ bool switchCorner(int c) const { return m_SwitchCorners[c]; }
+ const QList& switchCorners() const { return m_SwitchCorners; }
+ int switchCornerSize() const { return m_SwitchCornerSize; }
+ bool fix(Fix f) const { return m_Fixes[f]; }
+ const QList& fixes() const { return m_Fixes; }
+
+ void loadSettings(QSettings& settings);
+ void saveSettings(QSettings& settings) const;
+ QTextStream& writeScreensSection(QTextStream& outStream) const;
+ QTextStream& writeAliasesSection(QTextStream& outStream) const;
+
+ bool swapped() const { return m_Swapped; }
+
+ protected:
+ void init();
+ void setName(const QString& name) { m_Name = name; }
+ QPixmap* pixmap() { return &m_Pixmap; }
+ QString& name() { return m_Name; }
+
+ void setPixmap(const QPixmap& pixmap) { m_Pixmap = pixmap; }
+ QStringList& aliases() { return m_Aliases; }
+ void setModifier(int m, int n) { m_Modifiers[m] = n; }
+ QList& modifiers() { return m_Modifiers; }
+ void addAlias(const QString& alias) { m_Aliases.append(alias); }
+ void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; }
+ QList& switchCorners() { return m_SwitchCorners; }
+ void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; }
+ void setFix(int f, bool on) { m_Fixes[f] = on; }
+ QList& fixes() { return m_Fixes; }
+ void setSwapped(bool on) { m_Swapped = on; }
+
+ private:
+ QPixmap m_Pixmap;
+ QString m_Name;
+
+ QStringList m_Aliases;
+ QList m_Modifiers;
+ QList m_SwitchCorners;
+ int m_SwitchCornerSize;
+ QList m_Fixes;
+
+ bool m_Swapped;
+};
+
+typedef QList ScreenList;
+
+QDataStream& operator<<(QDataStream& outStream, const Screen& screen);
+QDataStream& operator>>(QDataStream& inStream, Screen& screen);
+
+#endif
+
diff --git a/src/gui/src/ScreenSettingsDialog.cpp b/src/gui/src/ScreenSettingsDialog.cpp
new file mode 100644
index 00000000..96276e89
--- /dev/null
+++ b/src/gui/src/ScreenSettingsDialog.cpp
@@ -0,0 +1,121 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ScreenSettingsDialog.h"
+#include "Screen.h"
+
+#include
+#include
+
+ScreenSettingsDialog::ScreenSettingsDialog(QWidget* parent, Screen* pScreen) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::ScreenSettingsDialogBase(),
+ m_pScreen(pScreen)
+{
+ setupUi(this);
+
+ QRegExp validScreenName("[a-z_][a-z0-9\\._-]{,31}", Qt::CaseInsensitive);
+
+ m_pLineEditName->setText(m_pScreen->name());
+ m_pLineEditName->setValidator(new QRegExpValidator(validScreenName, m_pLineEditName));
+ m_pLineEditName->selectAll();
+
+ m_pLineEditAlias->setValidator(new QRegExpValidator(validScreenName, m_pLineEditName));
+
+ for (int i = 0; i < m_pScreen->aliases().count(); i++)
+ new QListWidgetItem(m_pScreen->aliases()[i], m_pListAliases);
+
+ m_pComboBoxShift->setCurrentIndex(m_pScreen->modifier(Screen::Shift));
+ m_pComboBoxCtrl->setCurrentIndex(m_pScreen->modifier(Screen::Ctrl));
+ m_pComboBoxAlt->setCurrentIndex(m_pScreen->modifier(Screen::Alt));
+ m_pComboBoxMeta->setCurrentIndex(m_pScreen->modifier(Screen::Meta));
+ m_pComboBoxSuper->setCurrentIndex(m_pScreen->modifier(Screen::Super));
+
+ m_pCheckBoxCornerTopLeft->setChecked(m_pScreen->switchCorner(Screen::TopLeft));
+ m_pCheckBoxCornerTopRight->setChecked(m_pScreen->switchCorner(Screen::TopRight));
+ m_pCheckBoxCornerBottomLeft->setChecked(m_pScreen->switchCorner(Screen::BottomLeft));
+ m_pCheckBoxCornerBottomRight->setChecked(m_pScreen->switchCorner(Screen::BottomRight));
+ m_pSpinBoxSwitchCornerSize->setValue(m_pScreen->switchCornerSize());
+
+ m_pCheckBoxCapsLock->setChecked(m_pScreen->fix(Screen::CapsLock));
+ m_pCheckBoxNumLock->setChecked(m_pScreen->fix(Screen::NumLock));
+ m_pCheckBoxScrollLock->setChecked(m_pScreen->fix(Screen::ScrollLock));
+ m_pCheckBoxXTest->setChecked(m_pScreen->fix(Screen::XTest));
+}
+
+void ScreenSettingsDialog::accept()
+{
+ if (m_pLineEditName->text().isEmpty())
+ {
+ QMessageBox::warning(this, tr("Screen name is empty"), tr("The name for a screen can not be empty. Please fill in a name or cancel the dialog."));
+ return;
+ }
+
+ m_pScreen->init();
+
+ m_pScreen->setName(m_pLineEditName->text());
+
+ for (int i = 0; i < m_pListAliases->count(); i++)
+ m_pScreen->addAlias(m_pListAliases->item(i)->text());
+
+ m_pScreen->setModifier(Screen::Shift, m_pComboBoxShift->currentIndex());
+ m_pScreen->setModifier(Screen::Ctrl, m_pComboBoxCtrl->currentIndex());
+ m_pScreen->setModifier(Screen::Alt, m_pComboBoxAlt->currentIndex());
+ m_pScreen->setModifier(Screen::Meta, m_pComboBoxMeta->currentIndex());
+ m_pScreen->setModifier(Screen::Super, m_pComboBoxSuper->currentIndex());
+
+ m_pScreen->setSwitchCorner(Screen::TopLeft, m_pCheckBoxCornerTopLeft->isChecked());
+ m_pScreen->setSwitchCorner(Screen::TopRight, m_pCheckBoxCornerTopRight->isChecked());
+ m_pScreen->setSwitchCorner(Screen::BottomLeft, m_pCheckBoxCornerBottomLeft->isChecked());
+ m_pScreen->setSwitchCorner(Screen::BottomRight, m_pCheckBoxCornerBottomRight->isChecked());
+ m_pScreen->setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value());
+
+ m_pScreen->setFix(Screen::CapsLock, m_pCheckBoxCapsLock->isChecked());
+ m_pScreen->setFix(Screen::NumLock, m_pCheckBoxNumLock->isChecked());
+ m_pScreen->setFix(Screen::ScrollLock, m_pCheckBoxScrollLock->isChecked());
+ m_pScreen->setFix(Screen::XTest, m_pCheckBoxXTest->isChecked());
+
+ QDialog::accept();
+}
+
+void ScreenSettingsDialog::on_m_pButtonAddAlias_clicked()
+{
+ if (!m_pLineEditAlias->text().isEmpty() && m_pListAliases->findItems(m_pLineEditAlias->text(), Qt::MatchFixedString).isEmpty())
+ {
+ new QListWidgetItem(m_pLineEditAlias->text(), m_pListAliases);
+ m_pLineEditAlias->clear();
+ }
+}
+
+void ScreenSettingsDialog::on_m_pLineEditAlias_textChanged(const QString& text)
+{
+ m_pButtonAddAlias->setEnabled(!text.isEmpty());
+}
+
+void ScreenSettingsDialog::on_m_pButtonRemoveAlias_clicked()
+{
+ QList items = m_pListAliases->selectedItems();
+
+ for (int i = 0; i < items.count(); i++)
+ delete items[i];
+}
+
+void ScreenSettingsDialog::on_m_pListAliases_itemSelectionChanged()
+{
+ m_pButtonRemoveAlias->setEnabled(!m_pListAliases->selectedItems().isEmpty());
+}
+
diff --git a/src/gui/src/ScreenSettingsDialog.h b/src/gui/src/ScreenSettingsDialog.h
new file mode 100644
index 00000000..876b31e4
--- /dev/null
+++ b/src/gui/src/ScreenSettingsDialog.h
@@ -0,0 +1,52 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(SCREENSETTINGSDIALOG__H)
+
+#define SCREENSETTINGSDIALOG__H
+
+#include
+
+#include "ui_ScreenSettingsDialogBase.h"
+
+class QWidget;
+class QString;
+
+class Screen;
+
+class ScreenSettingsDialog : public QDialog, public Ui::ScreenSettingsDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ScreenSettingsDialog(QWidget* parent, Screen* pScreen = NULL);
+
+ public slots:
+ void accept();
+
+ private slots:
+ void on_m_pButtonAddAlias_clicked();
+ void on_m_pButtonRemoveAlias_clicked();
+ void on_m_pLineEditAlias_textChanged(const QString& text);
+ void on_m_pListAliases_itemSelectionChanged();
+
+ private:
+ Screen* m_pScreen;
+};
+
+#endif
+
diff --git a/src/gui/src/ScreenSetupModel.cpp b/src/gui/src/ScreenSetupModel.cpp
new file mode 100644
index 00000000..f2fb4126
--- /dev/null
+++ b/src/gui/src/ScreenSetupModel.cpp
@@ -0,0 +1,142 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ScreenSetupModel.h"
+#include "Screen.h"
+
+#include
+#include
+
+const QString ScreenSetupModel::m_MimeType = "application/x-qsynergy-screen";
+
+ScreenSetupModel::ScreenSetupModel(ScreenList& screens, int numColumns, int numRows) :
+ QAbstractTableModel(NULL),
+ m_Screens(screens),
+ m_NumColumns(numColumns),
+ m_NumRows(numRows)
+{
+ if (m_NumColumns * m_NumRows > screens.size())
+ qFatal("Not enough elements (%u) in screens QList for %d columns and %d rows", screens.size(), m_NumColumns, m_NumRows);
+}
+
+QVariant ScreenSetupModel::data(const QModelIndex& index, int role) const
+{
+ if (index.isValid() && index.row() < m_NumRows && index.column() < m_NumColumns)
+ {
+ switch(role)
+ {
+ case Qt::DecorationRole:
+ if (screen(index).isNull())
+ break;
+ return QIcon(*screen(index).pixmap());
+
+ case Qt::ToolTipRole:
+ if (screen(index).isNull())
+ break;
+ return QString(tr(
+ "Screen: %1"
+ "
Double click to edit settings"
+ "
Drag screen to the trashcan to remove it")).arg(screen(index).name());
+
+ case Qt::DisplayRole:
+ if (screen(index).isNull())
+ break;
+ return screen(index).name();
+ }
+ }
+
+ return QVariant();
+}
+
+Qt::ItemFlags ScreenSetupModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid() || index.row() >= m_NumRows || index.column() >= m_NumColumns)
+ return 0;
+
+ if (!screen(index).isNull())
+ return Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
+
+ return Qt::ItemIsDropEnabled;
+}
+
+Qt::DropActions ScreenSetupModel::supportedDropActions() const
+{
+ return Qt::MoveAction | Qt::CopyAction;
+}
+
+QStringList ScreenSetupModel::mimeTypes() const
+{
+ return QStringList() << m_MimeType;
+}
+
+QMimeData* ScreenSetupModel::mimeData(const QModelIndexList& indexes) const
+{
+ QMimeData* pMimeData = new QMimeData();
+ QByteArray encodedData;
+
+ QDataStream stream(&encodedData, QIODevice::WriteOnly);
+
+ foreach (const QModelIndex& index, indexes)
+ if (index.isValid())
+ stream << index.column() << index.row() << screen(index);
+
+ pMimeData->setData(m_MimeType, encodedData);
+
+ return pMimeData;
+}
+
+bool ScreenSetupModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
+{
+ if (action == Qt::IgnoreAction)
+ return true;
+
+ if (!data->hasFormat(m_MimeType))
+ return false;
+
+ if (!parent.isValid() || row != -1 || column != -1)
+ return false;
+
+ QByteArray encodedData = data->data(m_MimeType);
+ QDataStream stream(&encodedData, QIODevice::ReadOnly);
+
+ int sourceColumn = -1;
+ int sourceRow = -1;
+
+ stream >> sourceColumn;
+ stream >> sourceRow;
+
+ // don't drop screen onto itself
+ if (sourceColumn == parent.column() && sourceRow == parent.row())
+ return false;
+
+ Screen droppedScreen;
+ stream >> droppedScreen;
+
+ Screen oldScreen = screen(parent.column(), parent.row());
+ if (!oldScreen.isNull() && sourceColumn != -1 && sourceRow != -1)
+ {
+ // mark the screen so it isn't deleted after the dragndrop succeeded
+ // see ScreenSetupView::startDrag()
+ oldScreen.setSwapped(true);
+ screen(sourceColumn, sourceRow) = oldScreen;
+ }
+
+ screen(parent.column(), parent.row()) = droppedScreen;
+
+ return true;
+}
+
diff --git a/src/gui/src/ScreenSetupModel.h b/src/gui/src/ScreenSetupModel.h
new file mode 100644
index 00000000..1537e7f1
--- /dev/null
+++ b/src/gui/src/ScreenSetupModel.h
@@ -0,0 +1,70 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(SCREENSETUPMODEL__H)
+
+#define SCREENSETUPMODEL__H
+
+#include
+#include
+#include
+#include
+
+#include "Screen.h"
+
+class ScreenSetupView;
+class ServerConfigDialog;
+
+class ScreenSetupModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+ friend class ScreenSetupView;
+ friend class ServerConfigDialog;
+
+ public:
+ ScreenSetupModel(ScreenList& screens, int numColumns, int numRows);
+
+ public:
+ static const QString& mimeType() { return m_MimeType; }
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+ int rowCount() const { return m_NumRows; }
+ int columnCount() const { return m_NumColumns; }
+ int rowCount(const QModelIndex&) const { return rowCount(); }
+ int columnCount(const QModelIndex&) const { return columnCount(); }
+ Qt::DropActions supportedDropActions() const;
+ Qt::ItemFlags flags(const QModelIndex& index) const;
+ QStringList mimeTypes() const;
+ QMimeData* mimeData(const QModelIndexList& indexes) const;
+
+ protected:
+ bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
+ const Screen& screen(const QModelIndex& index) const { return screen(index.column(), index.row()); }
+ Screen& screen(const QModelIndex& index) { return screen(index.column(), index.row()); }
+ const Screen& screen(int column, int row) const { return m_Screens[row * m_NumColumns + column]; }
+ Screen& screen(int column, int row) { return m_Screens[row * m_NumColumns + column]; }
+
+ private:
+ ScreenList& m_Screens;
+ const int m_NumColumns;
+ const int m_NumRows;
+
+ static const QString m_MimeType;
+};
+
+#endif
+
diff --git a/src/gui/src/ScreenSetupView.cpp b/src/gui/src/ScreenSetupView.cpp
new file mode 100644
index 00000000..b238eaed
--- /dev/null
+++ b/src/gui/src/ScreenSetupView.cpp
@@ -0,0 +1,160 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ScreenSetupView.h"
+#include "ScreenSetupModel.h"
+#include "ScreenSettingsDialog.h"
+
+#include
+#include
+
+ScreenSetupView::ScreenSetupView(QWidget* parent) :
+ QTableView(parent)
+{
+ setDropIndicatorShown(true);
+ setDragDropMode(DragDrop);
+ setSelectionMode(SingleSelection);
+
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ setIconSize(QSize(64, 64));
+ horizontalHeader()->hide();
+ verticalHeader()->hide();
+}
+
+void ScreenSetupView::setModel(ScreenSetupModel* model)
+{
+ QTableView::setModel(model);
+ setTableSize();
+}
+
+ScreenSetupModel* ScreenSetupView::model() const
+{
+ return qobject_cast(QTableView::model());
+}
+
+void ScreenSetupView::setTableSize()
+{
+ for (int i = 0; i < model()->columnCount(); i++)
+ setColumnWidth(i, width() / model()->columnCount());
+
+ for (int i = 0; i < model()->rowCount(); i++)
+ setRowHeight(i, height() / model()->rowCount());
+}
+
+void ScreenSetupView::resizeEvent(QResizeEvent* event)
+{
+ setTableSize();
+ event->ignore();
+}
+
+void ScreenSetupView::mouseDoubleClickEvent(QMouseEvent* event)
+{
+ if (event->buttons() & Qt::LeftButton)
+ {
+ int col = columnAt(event->pos().x());
+ int row = rowAt(event->pos().y());
+
+ if (!model()->screen(col, row).isNull())
+ {
+ ScreenSettingsDialog dlg(this, &model()->screen(col, row));
+ dlg.exec();
+ }
+ }
+ else
+ event->ignore();
+}
+
+void ScreenSetupView::dragEnterEvent(QDragEnterEvent* event)
+{
+ // we accept anything that enters us by a drag as long as the
+ // mime type is okay. anything else is dealt with in dragMoveEvent()
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ event->accept();
+ else
+ event->ignore();
+}
+
+void ScreenSetupView::dragMoveEvent(QDragMoveEvent* event)
+{
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ {
+ // where does the event come from? myself or someone else?
+ if (event->source() == this)
+ {
+ // myself is ok, but then it must be a move action, never a copy
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+ else
+ {
+ int col = columnAt(event->pos().x());
+ int row = rowAt(event->pos().y());
+
+ // a drop from outside is not allowed if there's a screen already there.
+ if (!model()->screen(col, row).isNull())
+ event->ignore();
+ else
+ event->acceptProposedAction();
+ }
+ }
+ else
+ event->ignore();
+}
+
+// this is reimplemented from QAbstractItemView::startDrag()
+void ScreenSetupView::startDrag(Qt::DropActions)
+{
+ QModelIndexList indexes = selectedIndexes();
+
+ if (indexes.count() != 1)
+ return;
+
+ QMimeData* pData = model()->mimeData(indexes);
+ if (pData == NULL)
+ return;
+
+ QPixmap pixmap = *model()->screen(indexes[0]).pixmap();
+ QDrag* pDrag = new QDrag(this);
+ pDrag->setPixmap(pixmap);
+ pDrag->setMimeData(pData);
+ pDrag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2));
+
+ if (pDrag->exec(Qt::MoveAction, Qt::MoveAction) == Qt::MoveAction)
+ {
+ selectionModel()->clear();
+
+ // make sure to only delete the drag source if screens weren't swapped
+ // see ScreenSetupModel::dropMimeData
+ if (!model()->screen(indexes[0]).swapped())
+ model()->screen(indexes[0]) = Screen();
+ else
+ model()->screen(indexes[0]).setSwapped(false);
+ }
+}
+
+QStyleOptionViewItem ScreenSetupView::viewOptions() const
+{
+ QStyleOptionViewItem option = QTableView::viewOptions();
+ option.showDecorationSelected = true;
+ option.decorationPosition = QStyleOptionViewItem::Top;
+ option.displayAlignment = Qt::AlignCenter;
+ option.textElideMode = Qt::ElideMiddle;
+ return option;
+}
+
diff --git a/src/gui/src/ScreenSetupView.h b/src/gui/src/ScreenSetupView.h
new file mode 100644
index 00000000..b6e4d156
--- /dev/null
+++ b/src/gui/src/ScreenSetupView.h
@@ -0,0 +1,56 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(SCREENSETUPVIEW__H)
+
+#define SCREENSETUPVIEW__H
+
+#include
+#include
+
+#include "Screen.h"
+
+class QWidget;
+class QMouseEvent;
+class QResizeEvent;
+class QDragEnterEvent;
+class ScreenSetupModel;
+
+class ScreenSetupView : public QTableView
+{
+ Q_OBJECT
+
+ public:
+ ScreenSetupView(QWidget* parent);
+
+ public:
+ void setModel(ScreenSetupModel* model);
+ ScreenSetupModel* model() const;
+
+ protected:
+ void mouseDoubleClickEvent(QMouseEvent*);
+ void setTableSize();
+ void resizeEvent(QResizeEvent*);
+ void dragEnterEvent(QDragEnterEvent* event);
+ void dragMoveEvent(QDragMoveEvent* event);
+ void startDrag(Qt::DropActions supportedActions);
+ QStyleOptionViewItem viewOptions() const;
+ void scrollTo(const QModelIndex&, ScrollHint) {}
+};
+
+#endif
+
diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp
new file mode 100644
index 00000000..386d447d
--- /dev/null
+++ b/src/gui/src/ServerConfig.cpp
@@ -0,0 +1,264 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ServerConfig.h"
+#include "Hotkey.h"
+
+#include
+
+static const struct
+{
+ int x;
+ int y;
+ const char* name;
+} neighbourDirs[] =
+{
+ { 0, -1, "up" },
+ { 1, 0, "right" },
+ { 0, 1, "down" },
+ { -1, 0, "left" },
+};
+
+
+ServerConfig::ServerConfig(QSettings* settings, int numColumns, int numRows) :
+ m_pSettings(settings),
+ m_Screens(),
+ m_NumColumns(numColumns),
+ m_NumRows(numRows)
+{
+ Q_ASSERT(m_pSettings);
+
+ loadSettings();
+}
+
+ServerConfig::~ServerConfig()
+{
+ saveSettings();
+}
+
+bool ServerConfig::save(const QString& fileName) const
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return false;
+
+ save(file);
+ file.close();
+
+ return true;
+}
+
+void ServerConfig::save(QFile& file) const
+{
+ QTextStream outStream(&file);
+ outStream << *this;
+}
+
+void ServerConfig::init()
+{
+ switchCorners().clear();
+ screens().clear();
+
+ // m_NumSwitchCorners is used as a fixed size array. See Screen::init()
+ for (int i = 0; i < NumSwitchCorners; i++)
+ switchCorners() << false;
+
+ // There must always be screen objects for each cell in the screens QList. Unused screens
+ // are identified by having an empty name.
+ for (int i = 0; i < numColumns() * numRows(); i++)
+ addScreen(Screen());
+}
+
+void ServerConfig::saveSettings()
+{
+ settings().beginGroup("internalConfig");
+ settings().remove("");
+
+ settings().setValue("numColumns", numColumns());
+ settings().setValue("numRows", numRows());
+
+ settings().setValue("hasHeartbeat", hasHeartbeat());
+ settings().setValue("heartbeat", heartbeat());
+ settings().setValue("relativeMouseMoves", relativeMouseMoves());
+ settings().setValue("screenSaverSync", screenSaverSync());
+ settings().setValue("win32KeepForeground", win32KeepForeground());
+ settings().setValue("hasSwitchDelay", hasSwitchDelay());
+ settings().setValue("switchDelay", switchDelay());
+ settings().setValue("hasSwitchDoubleTap", hasSwitchDoubleTap());
+ settings().setValue("switchDoubleTap", switchDoubleTap());
+ settings().setValue("switchCornerSize", switchCornerSize());
+
+ writeSettings(settings(), switchCorners(), "switchCorner");
+
+ settings().beginWriteArray("screens");
+ for (int i = 0; i < screens().size(); i++)
+ {
+ settings().setArrayIndex(i);
+ screens()[i].saveSettings(settings());
+ }
+ settings().endArray();
+
+ settings().beginWriteArray("hotkeys");
+ for (int i = 0; i < hotkeys().size(); i++)
+ {
+ settings().setArrayIndex(i);
+ hotkeys()[i].saveSettings(settings());
+ }
+ settings().endArray();
+
+ settings().endGroup();
+}
+
+void ServerConfig::loadSettings()
+{
+ settings().beginGroup("internalConfig");
+
+ setNumColumns(settings().value("numColumns", 5).toInt());
+ setNumRows(settings().value("numRows", 3).toInt());
+
+ // we need to know the number of columns and rows before we can set up ourselves
+ init();
+
+ haveHeartbeat(settings().value("hasHeartbeat", false).toBool());
+ setHeartbeat(settings().value("heartbeat", 5000).toInt());
+ setRelativeMouseMoves(settings().value("relativeMouseMoves", false).toBool());
+ setScreenSaverSync(settings().value("screenSaverSync", true).toBool());
+ setWin32KeepForeground(settings().value("win32KeepForeground", false).toBool());
+ haveSwitchDelay(settings().value("hasSwitchDelay", false).toBool());
+ setSwitchDelay(settings().value("switchDelay", 250).toInt());
+ haveSwitchDoubleTap(settings().value("hasSwitchDoubleTap", false).toBool());
+ setSwitchDoubleTap(settings().value("switchDoubleTap", 250).toInt());
+ setSwitchCornerSize(settings().value("switchCornerSize").toInt());
+
+ readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners);
+
+ int numScreens = settings().beginReadArray("screens");
+ Q_ASSERT(numScreens <= screens().size());
+ for (int i = 0; i < numScreens; i++)
+ {
+ settings().setArrayIndex(i);
+ screens()[i].loadSettings(settings());
+ }
+ settings().endArray();
+
+ int numHotkeys = settings().beginReadArray("hotkeys");
+ for (int i = 0; i < numHotkeys; i++)
+ {
+ settings().setArrayIndex(i);
+ Hotkey h;
+ h.loadSettings(settings());
+ hotkeys().append(h);
+ }
+ settings().endArray();
+
+ settings().endGroup();
+}
+
+int ServerConfig::adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const
+{
+ if (screens()[idx].isNull())
+ return -1;
+
+ // if we're at the left or right end of the table, don't find results going further left or right
+ if ((deltaColumn > 0 && (idx+1) % numColumns() == 0)
+ || (deltaColumn < 0 && idx % numColumns() == 0))
+ return -1;
+
+ int arrayPos = idx + deltaColumn + deltaRow * numColumns();
+
+ if (arrayPos >= screens().size() || arrayPos < 0)
+ return -1;
+
+ return arrayPos;
+}
+
+QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config)
+{
+ outStream << "section: screens" << endl;
+
+ foreach (const Screen& s, config.screens())
+ if (!s.isNull())
+ s.writeScreensSection(outStream);
+
+ outStream << "end" << endl << endl;
+
+ outStream << "section: aliases" << endl;
+
+ foreach (const Screen& s, config.screens())
+ if (!s.isNull())
+ s.writeAliasesSection(outStream);
+
+ outStream << "end" << endl << endl;
+
+ outStream << "section: links" << endl;
+
+ for (int i = 0; i < config.screens().size(); i++)
+ if (!config.screens()[i].isNull())
+ {
+ outStream << "\t" << config.screens()[i].name() << ":" << endl;
+
+ for (unsigned int j = 0; j < sizeof(neighbourDirs) / sizeof(neighbourDirs[0]); j++)
+ {
+ int idx = config.adjacentScreenIndex(i, neighbourDirs[j].x, neighbourDirs[j].y);
+ if (idx != -1 && !config.screens()[idx].isNull())
+ outStream << "\t\t" << neighbourDirs[j].name << " = " << config.screens()[idx].name() << endl;
+ }
+ }
+
+ outStream << "end" << endl << endl;
+
+ outStream << "section: options" << endl;
+
+ if (config.hasHeartbeat())
+ outStream << "\t" << "heartbeat = " << config.heartbeat() << endl;
+
+ outStream << "\t" << "relativeMouseMoves = " << (config.relativeMouseMoves() ? "true" : "false") << endl;
+ outStream << "\t" << "screenSaverSync = " << (config.screenSaverSync() ? "true" : "false") << endl;
+ outStream << "\t" << "win32KeepForeground = " << (config.win32KeepForeground() ? "true" : "false") << endl;
+
+ if (config.hasSwitchDelay())
+ outStream << "\t" << "switchDelay = " << config.switchDelay() << endl;
+
+ if (config.hasSwitchDoubleTap())
+ outStream << "\t" << "switchDoubleTap = " << config.switchDoubleTap() << endl;
+
+ outStream << "\t" << "switchCorners = none ";
+ for (int i = 0; i < config.switchCorners().size(); i++)
+ if (config.switchCorners()[i])
+ outStream << "+" << config.switchCornerName(i) << " ";
+ outStream << endl;
+
+ outStream << "\t" << "switchCornerSize = " << config.switchCornerSize() << endl;
+
+ foreach(const Hotkey& hotkey, config.hotkeys())
+ outStream << hotkey;
+
+ outStream << "end" << endl << endl;
+
+ return outStream;
+}
+
+int ServerConfig::numScreens() const
+{
+ int rval = 0;
+
+ foreach(const Screen& s, screens())
+ if (!s.isNull())
+ rval++;
+
+ return rval;
+}
diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h
new file mode 100644
index 00000000..327b5556
--- /dev/null
+++ b/src/gui/src/ServerConfig.h
@@ -0,0 +1,113 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(SERVERCONFIG__H)
+
+#define SERVERCONFIG__H
+
+#include
+
+#include "Screen.h"
+#include "BaseConfig.h"
+#include "Hotkey.h"
+
+class QTextStream;
+class QSettings;
+class QString;
+class QFile;
+class ServerConfigDialog;
+
+class ServerConfig : public BaseConfig
+{
+ friend class ServerConfigDialog;
+ friend QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config);
+
+ public:
+ ServerConfig(QSettings* settings, int numColumns, int numRows);
+ ~ServerConfig();
+
+ public:
+ const ScreenList& screens() const { return m_Screens; }
+ int numColumns() const { return m_NumColumns; }
+ int numRows() const { return m_NumRows; }
+ bool hasHeartbeat() const { return m_HasHeartbeat; }
+ int heartbeat() const { return m_Heartbeat; }
+ bool relativeMouseMoves() const { return m_RelativeMouseMoves; }
+ bool screenSaverSync() const { return m_ScreenSaverSync; }
+ bool win32KeepForeground() const { return m_Win32KeepForeground; }
+ bool hasSwitchDelay() const { return m_HasSwitchDelay; }
+ int switchDelay() const { return m_SwitchDelay; }
+ bool hasSwitchDoubleTap() const { return m_HasSwitchDoubleTap; }
+ int switchDoubleTap() const { return m_SwitchDoubleTap; }
+ bool switchCorner(int c) const { return m_SwitchCorners[c]; }
+ int switchCornerSize() const { return m_SwitchCornerSize; }
+ const QList& switchCorners() const { return m_SwitchCorners; }
+ const HotkeyList& hotkeys() const { return m_Hotkeys; }
+
+ void saveSettings();
+ void loadSettings();
+ bool save(const QString& fileName) const;
+ void save(QFile& file) const;
+ int numScreens() const;
+
+ protected:
+ QSettings& settings() { return *m_pSettings; }
+ ScreenList& screens() { return m_Screens; }
+ void setScreens(const ScreenList& screens) { m_Screens = screens; }
+ void addScreen(const Screen& screen) { m_Screens.append(screen); }
+ void setNumColumns(int n) { m_NumColumns = n; }
+ void setNumRows(int n) { m_NumRows = n; }
+ void haveHeartbeat(bool on) { m_HasHeartbeat = on; }
+ void setHeartbeat(int val) { m_Heartbeat = val; }
+ void setRelativeMouseMoves(bool on) { m_RelativeMouseMoves = on; }
+ void setScreenSaverSync(bool on) { m_ScreenSaverSync = on; }
+ void setWin32KeepForeground(bool on) { m_Win32KeepForeground = on; }
+ void haveSwitchDelay(bool on) { m_HasSwitchDelay = on; }
+ void setSwitchDelay(int val) { m_SwitchDelay = val; }
+ void haveSwitchDoubleTap(bool on) { m_HasSwitchDoubleTap = on; }
+ void setSwitchDoubleTap(int val) { m_SwitchDoubleTap = val; }
+ void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; }
+ void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; }
+ QList& switchCorners() { return m_SwitchCorners; }
+ HotkeyList& hotkeys() { return m_Hotkeys; }
+
+ void init();
+ int adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const;
+
+ private:
+ QSettings* m_pSettings;
+ ScreenList m_Screens;
+ int m_NumColumns;
+ int m_NumRows;
+ bool m_HasHeartbeat;
+ int m_Heartbeat;
+ bool m_RelativeMouseMoves;
+ bool m_ScreenSaverSync;
+ bool m_Win32KeepForeground;
+ bool m_HasSwitchDelay;
+ int m_SwitchDelay;
+ bool m_HasSwitchDoubleTap;
+ int m_SwitchDoubleTap;
+ int m_SwitchCornerSize;
+ QList m_SwitchCorners;
+ HotkeyList m_Hotkeys;
+};
+
+QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config);
+
+#endif
+
diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp
new file mode 100644
index 00000000..aea75278
--- /dev/null
+++ b/src/gui/src/ServerConfigDialog.cpp
@@ -0,0 +1,196 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ServerConfigDialog.h"
+#include "ServerConfig.h"
+#include "HotkeyDialog.h"
+#include "ActionDialog.h"
+
+#include
+#include
+
+ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, const QString& defaultScreenName) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::ServerConfigDialogBase(),
+ m_OrigServerConfig(config),
+ m_ServerConfig(config),
+ m_ScreenSetupModel(serverConfig().screens(), serverConfig().numColumns(), serverConfig().numRows())
+{
+ setupUi(this);
+
+ m_pCheckBoxHeartbeat->setChecked(serverConfig().hasHeartbeat());
+ m_pSpinBoxHeartbeat->setValue(serverConfig().heartbeat());
+
+ m_pCheckBoxRelativeMouseMoves->setChecked(serverConfig().relativeMouseMoves());
+ m_pCheckBoxScreenSaverSync->setChecked(serverConfig().screenSaverSync());
+ m_pCheckBoxWin32KeepForeground->setChecked(serverConfig().win32KeepForeground());
+
+ m_pCheckBoxSwitchDelay->setChecked(serverConfig().hasSwitchDelay());
+ m_pSpinBoxSwitchDelay->setValue(serverConfig().switchDelay());
+
+ m_pCheckBoxSwitchDoubleTap->setChecked(serverConfig().hasSwitchDoubleTap());
+ m_pSpinBoxSwitchDoubleTap->setValue(serverConfig().switchDoubleTap());
+
+ m_pCheckBoxCornerTopLeft->setChecked(serverConfig().switchCorner(BaseConfig::TopLeft));
+ m_pCheckBoxCornerTopRight->setChecked(serverConfig().switchCorner(BaseConfig::TopRight));
+ m_pCheckBoxCornerBottomLeft->setChecked(serverConfig().switchCorner(BaseConfig::BottomLeft));
+ m_pCheckBoxCornerBottomRight->setChecked(serverConfig().switchCorner(BaseConfig::BottomRight));
+ m_pSpinBoxSwitchCornerSize->setValue(serverConfig().switchCornerSize());
+
+ foreach(const Hotkey& hotkey, serverConfig().hotkeys())
+ m_pListHotkeys->addItem(hotkey.text());
+
+ m_pScreenSetupView->setModel(&m_ScreenSetupModel);
+
+ if (serverConfig().numScreens() == 0)
+ model().screen(serverConfig().numColumns() / 2, serverConfig().numRows() / 2) = Screen(defaultScreenName);
+}
+
+void ServerConfigDialog::accept()
+{
+ serverConfig().haveHeartbeat(m_pCheckBoxHeartbeat->isChecked());
+ serverConfig().setHeartbeat(m_pSpinBoxHeartbeat->value());
+
+ serverConfig().setRelativeMouseMoves(m_pCheckBoxRelativeMouseMoves->isChecked());
+ serverConfig().setScreenSaverSync(m_pCheckBoxScreenSaverSync->isChecked());
+ serverConfig().setWin32KeepForeground(m_pCheckBoxWin32KeepForeground->isChecked());
+
+ serverConfig().haveSwitchDelay(m_pCheckBoxSwitchDelay->isChecked());
+ serverConfig().setSwitchDelay(m_pSpinBoxSwitchDelay->value());
+
+ serverConfig().haveSwitchDoubleTap(m_pCheckBoxSwitchDoubleTap->isChecked());
+ serverConfig().setSwitchDoubleTap(m_pSpinBoxSwitchDoubleTap->value());
+
+ serverConfig().setSwitchCorner(BaseConfig::TopLeft, m_pCheckBoxCornerTopLeft->isChecked());
+ serverConfig().setSwitchCorner(BaseConfig::TopRight, m_pCheckBoxCornerTopRight->isChecked());
+ serverConfig().setSwitchCorner(BaseConfig::BottomLeft, m_pCheckBoxCornerBottomLeft->isChecked());
+ serverConfig().setSwitchCorner(BaseConfig::BottomRight, m_pCheckBoxCornerBottomRight->isChecked());
+ serverConfig().setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value());
+
+ // now that the dialog has been accepted, copy the new server config to the original one,
+ // which is a reference to the one in MainWindow.
+ setOrigServerConfig(serverConfig());
+
+ QDialog::accept();
+}
+
+void ServerConfigDialog::on_m_pButtonNewHotkey_clicked()
+{
+ Hotkey hotkey;
+ HotkeyDialog dlg(this, hotkey);
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ serverConfig().hotkeys().append(hotkey);
+ m_pListHotkeys->addItem(hotkey.text());
+ }
+}
+
+void ServerConfigDialog::on_m_pButtonEditHotkey_clicked()
+{
+ int idx = m_pListHotkeys->currentRow();
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idx];
+ HotkeyDialog dlg(this, hotkey);
+ if (dlg.exec() == QDialog::Accepted)
+ m_pListHotkeys->currentItem()->setText(hotkey.text());
+}
+
+void ServerConfigDialog::on_m_pButtonRemoveHotkey_clicked()
+{
+ int idx = m_pListHotkeys->currentRow();
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+ serverConfig().hotkeys().removeAt(idx);
+ m_pListActions->clear();
+ delete m_pListHotkeys->item(idx);
+}
+
+void ServerConfigDialog::on_m_pListHotkeys_itemSelectionChanged()
+{
+ bool itemsSelected = !m_pListHotkeys->selectedItems().isEmpty();
+ m_pButtonEditHotkey->setEnabled(itemsSelected);
+ m_pButtonRemoveHotkey->setEnabled(itemsSelected);
+ m_pButtonNewAction->setEnabled(itemsSelected);
+
+ if (itemsSelected && serverConfig().hotkeys().size() > 0)
+ {
+ m_pListActions->clear();
+
+ int idx = m_pListHotkeys->row(m_pListHotkeys->selectedItems()[0]);
+
+ // There's a bug somewhere around here: We get idx == 1 right after we deleted the next to last item, so idx can
+ // only possibly be 0. GDB shows we got called indirectly from the delete line in
+ // on_m_pButtonRemoveHotkey_clicked() above, but the delete is of course necessary and seems correct.
+ // The while() is a generalized workaround for all that and shouldn't be required.
+ while (idx >= 0 && idx >= serverConfig().hotkeys().size())
+ idx--;
+
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+
+ const Hotkey& hotkey = serverConfig().hotkeys()[idx];
+ foreach(const Action& action, hotkey.actions())
+ m_pListActions->addItem(action.text());
+ }
+}
+
+void ServerConfigDialog::on_m_pButtonNewAction_clicked()
+{
+ int idx = m_pListHotkeys->currentRow();
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idx];
+
+ Action action;
+ ActionDialog dlg(this, serverConfig(), hotkey, action);
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ hotkey.actions().append(action);
+ m_pListActions->addItem(action.text());
+ }
+}
+
+void ServerConfigDialog::on_m_pButtonEditAction_clicked()
+{
+ int idxHotkey = m_pListHotkeys->currentRow();
+ Q_ASSERT(idxHotkey >= 0 && idxHotkey < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idxHotkey];
+
+ int idxAction = m_pListActions->currentRow();
+ Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size());
+ Action& action = hotkey.actions()[idxAction];
+
+ ActionDialog dlg(this, serverConfig(), hotkey, action);
+ if (dlg.exec() == QDialog::Accepted)
+ m_pListActions->currentItem()->setText(action.text());
+}
+
+void ServerConfigDialog::on_m_pButtonRemoveAction_clicked()
+{
+ int idxHotkey = m_pListHotkeys->currentRow();
+ Q_ASSERT(idxHotkey >= 0 && idxHotkey < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idxHotkey];
+
+ int idxAction = m_pListActions->currentRow();
+ Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size());
+
+ hotkey.actions().removeAt(idxAction);
+ delete m_pListActions->currentItem();
+}
+
+void ServerConfigDialog::on_m_pListActions_itemSelectionChanged()
+{
+ m_pButtonEditAction->setEnabled(!m_pListActions->selectedItems().isEmpty());
+ m_pButtonRemoveAction->setEnabled(!m_pListActions->selectedItems().isEmpty());
+}
diff --git a/src/gui/src/ServerConfigDialog.h b/src/gui/src/ServerConfigDialog.h
new file mode 100644
index 00000000..9fb34b16
--- /dev/null
+++ b/src/gui/src/ServerConfigDialog.h
@@ -0,0 +1,62 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(SERVERCONFIGDIALOG__H)
+
+#define SERVERCONFIGDIALOG__H
+
+#include "ScreenSetupModel.h"
+#include "ServerConfig.h"
+
+#include "ui_ServerConfigDialogBase.h"
+
+#include
+
+class ServerConfigDialog : public QDialog, public Ui::ServerConfigDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ServerConfigDialog(QWidget* parent, ServerConfig& config, const QString& defaultScreenName);
+
+ public slots:
+ void accept();
+
+ protected slots:
+ void on_m_pButtonNewHotkey_clicked();
+ void on_m_pListHotkeys_itemSelectionChanged();
+ void on_m_pButtonEditHotkey_clicked();
+ void on_m_pButtonRemoveHotkey_clicked();
+
+ void on_m_pButtonNewAction_clicked();
+ void on_m_pListActions_itemSelectionChanged();
+ void on_m_pButtonEditAction_clicked();
+ void on_m_pButtonRemoveAction_clicked();
+
+ protected:
+ ServerConfig& serverConfig() { return m_ServerConfig; }
+ void setOrigServerConfig(const ServerConfig& s) { m_OrigServerConfig = s; }
+ ScreenSetupModel& model() { return m_ScreenSetupModel; }
+
+ private:
+ ServerConfig& m_OrigServerConfig;
+ ServerConfig m_ServerConfig;
+ ScreenSetupModel m_ScreenSetupModel;
+};
+
+#endif
+
diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp
new file mode 100644
index 00000000..cf1cc517
--- /dev/null
+++ b/src/gui/src/SettingsDialog.cpp
@@ -0,0 +1,89 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "SettingsDialog.h"
+
+#include
+#include
+
+#include "AppConfig.h"
+
+SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::SettingsDialogBase(),
+ m_AppConfig(config)
+{
+ setupUi(this);
+
+ m_pCheckBoxAutoConnect->setChecked(appConfig().autoConnect());
+ m_pLineEditScreenName->setText(appConfig().screenName());
+ m_pSpinBoxPort->setValue(appConfig().port());
+ m_pLineEditInterface->setText(appConfig().interface());
+ m_pComboLogLevel->setCurrentIndex(appConfig().logLevel());
+ m_pCheckBoxLogToFile->setChecked(appConfig().logToFile());
+ m_pLineEditLogFilename->setText(appConfig().logFilename());
+ m_pCheckBoxAutoStart->setChecked(appConfig().autoStart());
+ m_pCheckBoxAutoHide->setChecked(appConfig().autoHide());
+
+#ifdef Q_OS_WIN
+ m_pComboBoxGameDevice->setCurrentIndex(appConfig().gameModeIndex());
+ m_pRadioButtonGamePollDynamic->setChecked(appConfig().gamePollingDynamic());
+ m_pRadioButtonGamePollStatic->setChecked(!appConfig().gamePollingDynamic());
+ m_pSpinBoxGamePoll->setValue(appConfig().gamePollingFrequency());
+#else
+ m_pGroupGameDevices->setEnabled(false);
+#endif
+}
+
+void SettingsDialog::accept()
+{
+ appConfig().setAutoConnect(m_pCheckBoxAutoConnect->isChecked());
+ appConfig().setScreenName(m_pLineEditScreenName->text());
+ appConfig().setPort(m_pSpinBoxPort->value());
+ appConfig().setInterface(m_pLineEditInterface->text());
+ appConfig().setLogLevel(m_pComboLogLevel->currentIndex());
+ appConfig().setLogToFile(m_pCheckBoxLogToFile->isChecked());
+ appConfig().setLogFilename(m_pLineEditLogFilename->text());
+ appConfig().setAutoStart(m_pCheckBoxAutoStart->isChecked());
+ appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
+ appConfig().setGameModeIndex(m_pComboBoxGameDevice->currentIndex());
+ appConfig().setGamePollingDynamic(m_pRadioButtonGamePollDynamic->isChecked());
+ appConfig().setGamePollingFrequency(m_pSpinBoxGamePoll->value());
+
+ QDialog::accept();
+}
+
+void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i)
+{
+ bool checked = i == 2;
+
+ m_pLineEditLogFilename->setEnabled(checked);
+ m_pButtonBrowseLog->setEnabled(checked);
+}
+
+void SettingsDialog::on_m_pButtonBrowseLog_clicked()
+{
+ QString fileName = QFileDialog::getSaveFileName(
+ this, tr("Save log file to..."),
+ m_pLineEditLogFilename->text(),
+ "Logs (*.log *.txt)");
+
+ if (!fileName.isEmpty())
+ {
+ m_pLineEditLogFilename->setText(fileName);
+ }
+}
diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h
new file mode 100644
index 00000000..25f3ec7e
--- /dev/null
+++ b/src/gui/src/SettingsDialog.h
@@ -0,0 +1,48 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(SETTINGSDIALOG_H)
+
+#define SETTINGSDIALOG_H
+
+#include
+#include "ui_SettingsDialogBase.h"
+
+class AppConfig;
+
+class SettingsDialog : public QDialog, public Ui::SettingsDialogBase
+{
+ Q_OBJECT
+
+ public:
+ SettingsDialog(QWidget* parent, AppConfig& config);
+ static QString browseForSynergyc(QWidget* parent, const QString& programDir, const QString& synergycName);
+ static QString browseForSynergys(QWidget* parent, const QString& programDir, const QString& synergysName);
+
+ protected:
+ void accept();
+ AppConfig& appConfig() { return m_AppConfig; }
+
+ private:
+ AppConfig& m_AppConfig;
+
+ private slots:
+ void on_m_pCheckBoxLogToFile_stateChanged(int );
+ void on_m_pButtonBrowseLog_clicked();
+};
+
+#endif
diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp
new file mode 100644
index 00000000..180d8939
--- /dev/null
+++ b/src/gui/src/SetupWizard.cpp
@@ -0,0 +1,120 @@
+#include "SetupWizard.h"
+#include "MainWindow.h"
+
+#include
+#include
+
+SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) :
+ m_MainWindow(mainWindow),
+ m_StartMain(startMain)
+{
+ setupUi(this);
+
+#if defined(Q_OS_MAC)
+
+ // the mac style needs a little more room because of the
+ // graphic on the left.
+ resize(600, 500);
+ setMinimumSize(size());
+
+#elif defined(Q_OS_WIN)
+
+ // when areo is disabled on windows, the next/back buttons
+ // are hidden (must be a qt bug) -- resizing the window
+ // to +1 of the original height seems to fix this.
+ // NOTE: calling setMinimumSize after this will break
+ // it again, so don't do that.
+ resize(size().width(), size().height() + 1);
+
+#endif
+
+#if !defined(Q_OS_WIN)
+ m_pServiceRadioButton->setEnabled(false);
+ m_pServiceRadioButton->setText(tr("Service (Windows only)"));
+ m_pServiceLabel->setEnabled(false);
+ m_pDesktopRadioButton->setChecked(true);
+#endif
+
+ connect(this, SIGNAL(finished(int)), this, SLOT(handlefinished()));
+ connect(m_pServerRadioButton, SIGNAL(toggled(bool)), m_MainWindow.m_pGroupServer, SLOT(setChecked(bool)));
+ connect(m_pClientRadioButton, SIGNAL(toggled(bool)), m_MainWindow.m_pGroupClient, SLOT(setChecked(bool)));
+}
+
+SetupWizard::~SetupWizard()
+{
+}
+
+bool SetupWizard::validateCurrentPage()
+{
+ QMessageBox message;
+ message.setWindowTitle(tr("Setup Synergy"));
+ message.setIcon(QMessageBox::Information);
+
+ bool result = false;
+ if (currentPage() == m_pNodePage)
+ {
+ result = m_pClientRadioButton->isChecked() ||
+ m_pServerRadioButton->isChecked();
+
+ if (!result)
+ {
+ message.setText(tr("Please select an option."));
+ message.exec();
+ }
+ }
+ else if(currentPage() == m_pStartupPage)
+ {
+ result = m_pServiceRadioButton->isChecked() ||
+ m_pDesktopRadioButton->isChecked() ||
+ m_pNoneRadioButton->isChecked();
+
+ if (!result)
+ {
+ message.setText(tr("Please select an option."));
+ message.exec();
+ }
+ }
+ return result;
+}
+
+void SetupWizard::handlefinished()
+{
+ close();
+
+ AppConfig& appConfig = m_MainWindow.appConfig();
+ if (m_pServiceRadioButton->isChecked())
+ {
+ appConfig.setProcessMode(Service);
+ }
+ else if (m_pDesktopRadioButton->isChecked())
+ {
+ appConfig.setProcessMode(Desktop);
+ appConfig.setAutoStart(true);
+ appConfig.setAutoConnect(true);
+ }
+ else if (m_pNoneRadioButton->isChecked())
+ {
+ // still use desktop mode, but don't auto start.
+ appConfig.setProcessMode(Desktop);
+ }
+ appConfig.setWizardHasRun(true);
+ appConfig.saveSettings();
+
+ QSettings& settings = m_MainWindow.settings();
+ if (m_pServerRadioButton->isChecked())
+ {
+ settings.setValue("groupServerChecked", true);
+ settings.setValue("groupClientChecked", false);
+ }
+ if (m_pClientRadioButton->isChecked())
+ {
+ settings.setValue("groupClientChecked", true);
+ settings.setValue("groupServerChecked", false);
+ }
+ settings.sync();
+
+ if (m_StartMain)
+ {
+ m_MainWindow.start(true);
+ }
+}
diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h
new file mode 100644
index 00000000..8a760974
--- /dev/null
+++ b/src/gui/src/SetupWizard.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+#include "ui_SetupWizardBase.h"
+
+class MainWindow;
+
+class SetupWizard : public QWizard, public Ui::SetupWizardBase
+{
+ Q_OBJECT
+public:
+ SetupWizard(MainWindow& mainWindow, bool startMain);
+ virtual ~SetupWizard();
+ bool validateCurrentPage();
+protected slots:
+ void handlefinished();
+private:
+ MainWindow& m_MainWindow;
+ bool m_StartMain;
+};
diff --git a/src/gui/src/TrashScreenWidget.cpp b/src/gui/src/TrashScreenWidget.cpp
new file mode 100644
index 00000000..1be9aabc
--- /dev/null
+++ b/src/gui/src/TrashScreenWidget.cpp
@@ -0,0 +1,42 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "TrashScreenWidget.h"
+#include "ScreenSetupModel.h"
+
+#include
+#include
+
+void TrashScreenWidget::dragEnterEvent(QDragEnterEvent* event)
+{
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+ else
+ event->ignore();
+}
+
+void TrashScreenWidget::dropEvent(QDropEvent* event)
+{
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ event->acceptProposedAction();
+ else
+ event->ignore();
+}
+
diff --git a/src/gui/src/TrashScreenWidget.h b/src/gui/src/TrashScreenWidget.h
new file mode 100644
index 00000000..34a5608e
--- /dev/null
+++ b/src/gui/src/TrashScreenWidget.h
@@ -0,0 +1,41 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#if !defined(TRASHSCREENWIDGET__H)
+
+#define TRASHSCREENWIDGET__H
+
+#include
+
+class QWidget;
+class QDragEnterEvent;
+class QDropEvent;
+
+class TrashScreenWidget : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ TrashScreenWidget(QWidget* parent) : QLabel(parent) {}
+
+ public:
+ void dragEnterEvent(QDragEnterEvent* event);
+ void dropEvent(QDropEvent* event);
+};
+
+#endif
+
diff --git a/src/gui/src/UpdateCheck.cpp b/src/gui/src/UpdateCheck.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/src/gui/src/VersionChecker.cpp b/src/gui/src/VersionChecker.cpp
new file mode 100644
index 00000000..76a64600
--- /dev/null
+++ b/src/gui/src/VersionChecker.cpp
@@ -0,0 +1,102 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "VersionChecker.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#define VERSION_REGEX "(\\d\\.\\d\\.\\d)"
+#define VERSION_URL "http://synergy-plus.googlecode.com/svn/web/version.txt"
+
+VersionChecker::VersionChecker()
+{
+ m_manager = new QNetworkAccessManager(this);
+
+ connect(m_manager, SIGNAL(finished(QNetworkReply*)),
+ this, SLOT(replyFinished(QNetworkReply*)));
+}
+
+VersionChecker::~VersionChecker()
+{
+ delete m_manager;
+}
+
+void VersionChecker::checkLatest()
+{
+ m_manager->get(QNetworkRequest(QUrl(VERSION_URL)));
+}
+
+void VersionChecker::replyFinished(QNetworkReply* reply)
+{
+ QString newestVersion = QString(reply->readAll());
+ if (!newestVersion.isEmpty())
+ {
+ QString currentVersion = getVersion();
+ if (compareVersions(currentVersion, newestVersion) > 0)
+ emit updateFound(newestVersion);
+ }
+}
+
+int VersionChecker::compareVersions(const QString& left, const QString& right)
+{
+ if (left.compare(right) == 0)
+ return 0; // versions are same.
+
+ QStringList leftSplit = left.split(QRegExp("\\."));
+ if (leftSplit.size() != 3)
+ return 1; // assume right wins.
+
+ QStringList rightSplit = right.split(QRegExp("\\."));
+ if (rightSplit.size() != 3)
+ return -1; // assume left wins.
+
+ int leftMajor = leftSplit.at(0).toInt();
+ int leftMinor = leftSplit.at(1).toInt();
+ int leftRev = leftSplit.at(2).toInt();
+
+ int rightMajor = rightSplit.at(0).toInt();
+ int rightMinor = rightSplit.at(1).toInt();
+ int rightRev = rightSplit.at(2).toInt();
+
+ bool rightWins =
+ (rightMajor > leftMajor) ||
+ ((rightMajor >= leftMajor) && (rightMinor > leftMinor)) ||
+ ((rightMajor >= leftMajor) && (rightMinor >= leftMinor) && (rightRev > leftRev));
+
+ return rightWins ? 1 : -1;
+}
+
+QString VersionChecker::getVersion()
+{
+ QProcess process;
+ process.start(m_app, QStringList() << "--version");
+
+ process.setReadChannel(QProcess::StandardOutput);
+ if (process.waitForStarted() && process.waitForFinished())
+ {
+ QRegExp rx(VERSION_REGEX);
+ QString text = process.readLine();
+ if (rx.indexIn(text) != -1)
+ return rx.cap(1);
+ }
+
+ return tr("Unknown");
+}
diff --git a/src/gui/src/VersionChecker.h b/src/gui/src/VersionChecker.h
new file mode 100644
index 00000000..88924a4d
--- /dev/null
+++ b/src/gui/src/VersionChecker.h
@@ -0,0 +1,43 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+
+class QNetworkAccessManager;
+class QNetworkReply;
+
+class VersionChecker : public QObject
+{
+ Q_OBJECT
+public:
+ VersionChecker();
+ virtual ~VersionChecker();
+ void checkLatest();
+ QString getVersion();
+ void setApp(const QString& app) { m_app = app; }
+ int compareVersions(const QString& left, const QString& right);
+public slots:
+ void replyFinished(QNetworkReply* reply);
+signals:
+ void updateFound(const QString& version);
+private:
+ QNetworkAccessManager* m_manager;
+ QString m_app;
+};
diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp
new file mode 100644
index 00000000..e81193ff
--- /dev/null
+++ b/src/gui/src/main.cpp
@@ -0,0 +1,103 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#define TRAY_RETRY_COUNT 10
+#define TRAY_RETRY_WAIT 2000
+
+#include "QSynergyApplication.h"
+#include "MainWindow.h"
+#include "AppConfig.h"
+#include "SetupWizard.h"
+
+#include
+#include
+#include
+
+class QThreadImpl : public QThread
+{
+public:
+ static void msleep(unsigned long msecs)
+ {
+ QThread::msleep(msecs);
+ }
+};
+
+int waitForTray();
+
+int main(int argc, char* argv[])
+{
+ QCoreApplication::setOrganizationName("Synergy");
+ QCoreApplication::setOrganizationDomain("http://synergy-foss.org/");
+ QCoreApplication::setApplicationName("Synergy");
+
+ QSynergyApplication app(argc, argv);
+
+ if (!waitForTray())
+ return -1;
+
+ QApplication::setQuitOnLastWindowClosed(false);
+
+ QTranslator qtTranslator;
+ qtTranslator.load("qt_" + QLocale::system().name(),
+ QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+ app.installTranslator(&qtTranslator);
+
+ QTranslator synergyTranslator;
+ synergyTranslator.load("res/lang/" + QLocale::system().name());
+ app.installTranslator(&synergyTranslator);
+
+ QSettings settings;
+ AppConfig appConfig(&settings);
+ MainWindow mainWindow(settings, appConfig);
+ SetupWizard setupWizard(mainWindow, true);
+
+ if (appConfig.wizardHasRun())
+ {
+ mainWindow.start(false);
+ }
+ else
+ {
+ setupWizard.show();
+ }
+
+ return app.exec();
+}
+
+int waitForTray()
+{
+ // on linux, the system tray may not be available immediately after logging in,
+ // so keep retrying but give up after a short time.
+ int trayAttempts = 0;
+ while (true)
+ {
+ if (QSystemTrayIcon::isSystemTrayAvailable())
+ {
+ break;
+ }
+
+ if (++trayAttempts > TRAY_RETRY_COUNT)
+ {
+ QMessageBox::critical(NULL, "Synergy",
+ QObject::tr("System tray is unavailable, quitting."));
+ return false;
+ }
+
+ QThreadImpl::msleep(TRAY_RETRY_WAIT);
+ }
+ return true;
+}
+
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
new file mode 100644
index 00000000..4cbb3c78
--- /dev/null
+++ b/src/lib/CMakeLists.txt
@@ -0,0 +1,25 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+add_subdirectory(arch)
+add_subdirectory(base)
+add_subdirectory(client)
+add_subdirectory(common)
+add_subdirectory(io)
+add_subdirectory(mt)
+add_subdirectory(net)
+add_subdirectory(platform)
+add_subdirectory(server)
+add_subdirectory(synergy)
diff --git a/src/lib/arch/CArch.cpp b/src/lib/arch/CArch.cpp
new file mode 100644
index 00000000..0ff78cd6
--- /dev/null
+++ b/src/lib/arch/CArch.cpp
@@ -0,0 +1,54 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArch.h"
+
+//
+// CArch
+//
+
+CArch* CArch::s_instance = NULL;
+
+CArch::CArch()
+{
+}
+
+CArch::~CArch()
+{
+}
+
+void
+CArch::init()
+{
+ // initialization that requires ARCH is done here.
+ ARCH_NETWORK::init();
+#if SYSAPI_WIN32
+ ARCH_TASKBAR::init();
+ CArchMiscWindows::init();
+#endif
+}
+
+CArch*
+CArch::getInstance()
+{
+ if (s_instance == NULL) {
+ s_instance = new CArch();
+ s_instance->init();
+ }
+
+ return s_instance;
+}
diff --git a/src/lib/arch/CArch.h b/src/lib/arch/CArch.h
new file mode 100644
index 00000000..a6efe54c
--- /dev/null
+++ b/src/lib/arch/CArch.h
@@ -0,0 +1,143 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+// TODO: consider whether or not to use either encapsulation (as below)
+// or inheritance (as it is now) for the ARCH stuff.
+//
+// case for encapsulation:
+// pros:
+// - compiler errors for missing pv implementations are not absolutely bonkers.
+// - function names don't have to be so verbose.
+// - easier to understand and debug.
+// - ctors in IArch implementations can call other implementations.
+// cons:
+// - slightly more code for calls to ARCH.
+// - you'll have to modify each ARCH call.
+//
+// also, we may want to consider making each encapsulated
+// class lazy-loaded so that apps like the daemon don't load
+// stuff when they don't need it.
+
+#ifndef CARCH_H
+#define CARCH_H
+
+#include "common.h"
+
+#if SYSAPI_WIN32
+# include "CArchConsoleWindows.h"
+# include "CArchDaemonWindows.h"
+# include "CArchFileWindows.h"
+# include "CArchLogWindows.h"
+# include "CArchIpcLogWindows.h"
+# include "CArchMiscWindows.h"
+# include "CArchMultithreadWindows.h"
+# include "CArchNetworkWinsock.h"
+# include "CArchSleepWindows.h"
+# include "CArchStringWindows.h"
+# include "CArchSystemWindows.h"
+# include "CArchTaskBarWindows.h"
+# include "CArchTimeWindows.h"
+# include "CArchPluginWindows.h"
+#elif SYSAPI_UNIX
+# include "CArchConsoleUnix.h"
+# include "CArchDaemonUnix.h"
+# include "CArchFileUnix.h"
+# include "CArchLogUnix.h"
+# include "CArchIpcLogUnix.h"
+# if HAVE_PTHREAD
+# include "CArchMultithreadPosix.h"
+# endif
+# include "CArchNetworkBSD.h"
+# include "CArchSleepUnix.h"
+# include "CArchStringUnix.h"
+# include "CArchSystemUnix.h"
+# include "CArchTaskBarXWindows.h"
+# include "CArchTimeUnix.h"
+# include "CArchPluginUnix.h"
+#endif
+
+/*!
+\def ARCH
+This macro evaluates to the singleton CArch object.
+*/
+#define ARCH (CArch::getInstance())
+
+//! Delegating implementation of architecture dependent interfaces
+/*!
+This class is a centralized interface to all architecture dependent
+interface implementations (except miscellaneous functions). It
+instantiates an implementation of each interface and delegates calls
+to each method to those implementations. Clients should use the
+\c ARCH macro to access this object. Clients must also instantiate
+exactly one of these objects before attempting to call any method,
+typically at the beginning of \c main().
+*/
+class CArch : public ARCH_CONSOLE,
+ public ARCH_DAEMON,
+ public ARCH_FILE,
+ public ARCH_LOG,
+ public ARCH_MULTITHREAD,
+ public ARCH_NETWORK,
+ public ARCH_SLEEP,
+ public ARCH_STRING,
+ public ARCH_SYSTEM,
+ public ARCH_TASKBAR,
+ public ARCH_TIME {
+public:
+ ~CArch();
+
+ //
+ // accessors
+ //
+
+ //! Return the singleton instance
+ /*!
+ The client must have instantiated exactly once CArch object before
+ calling this function.
+ */
+ static CArch* getInstance();
+
+ ARCH_IPC_LOG& ipcLog() const { return (ARCH_IPC_LOG&)m_ipcLog; }
+ ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; }
+
+private:
+ CArch();
+ void init();
+
+private:
+ static CArch* s_instance;
+ ARCH_IPC_LOG m_ipcLog;
+ ARCH_PLUGIN m_plugin;
+};
+
+//! Convenience object to lock/unlock an arch mutex
+class CArchMutexLock {
+public:
+ CArchMutexLock(CArchMutex mutex) : m_mutex(mutex)
+ {
+ ARCH->lockMutex(m_mutex);
+ }
+ ~CArchMutexLock()
+ {
+ ARCH->unlockMutex(m_mutex);
+ }
+
+private:
+ CArchMutex m_mutex;
+};
+
+#endif
diff --git a/src/lib/arch/CArchConsoleStd.cpp b/src/lib/arch/CArchConsoleStd.cpp
new file mode 100644
index 00000000..b579bc0a
--- /dev/null
+++ b/src/lib/arch/CArchConsoleStd.cpp
@@ -0,0 +1,31 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchConsoleStd.h"
+#include "CLog.h"
+#include
+
+void
+CArchConsoleStd::writeConsole(ELevel level, const char* str)
+{
+ if ((level >= kFATAL) && (level <= kWARNING))
+ std::cerr << str << std::endl;
+ else
+ std::cout << str << std::endl;
+
+ std::cout.flush();
+}
\ No newline at end of file
diff --git a/src/lib/arch/CArchConsoleStd.h b/src/lib/arch/CArchConsoleStd.h
new file mode 100644
index 00000000..1863d2a3
--- /dev/null
+++ b/src/lib/arch/CArchConsoleStd.h
@@ -0,0 +1,33 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "IArchConsole.h"
+
+//! Cross platform implementation of IArchConsole
+class CArchConsoleStd : public IArchConsole {
+public:
+ CArchConsoleStd() { }
+ virtual ~CArchConsoleStd() { }
+
+ // IArchConsole overrides
+ virtual void openConsole(const char* title) { }
+ virtual void closeConsole() { }
+ virtual void showConsole(bool) { }
+ virtual void writeConsole(ELevel level, const char*);
+};
diff --git a/src/lib/arch/CArchConsoleUnix.cpp b/src/lib/arch/CArchConsoleUnix.cpp
new file mode 100644
index 00000000..4f934f74
--- /dev/null
+++ b/src/lib/arch/CArchConsoleUnix.cpp
@@ -0,0 +1,22 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchConsoleUnix.h"
+
+CArchConsoleUnix::CArchConsoleUnix() { }
+
+CArchConsoleUnix::~CArchConsoleUnix() { }
diff --git a/src/lib/arch/CArchConsoleUnix.h b/src/lib/arch/CArchConsoleUnix.h
new file mode 100644
index 00000000..18141d07
--- /dev/null
+++ b/src/lib/arch/CArchConsoleUnix.h
@@ -0,0 +1,28 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "CArchConsoleStd.h"
+
+#define ARCH_CONSOLE CArchConsoleUnix
+
+class CArchConsoleUnix : public CArchConsoleStd {
+public:
+ CArchConsoleUnix();
+ virtual ~CArchConsoleUnix();
+};
diff --git a/src/lib/arch/CArchConsoleWindows.cpp b/src/lib/arch/CArchConsoleWindows.cpp
new file mode 100644
index 00000000..33315573
--- /dev/null
+++ b/src/lib/arch/CArchConsoleWindows.cpp
@@ -0,0 +1,22 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchConsoleWindows.h"
+
+CArchConsoleWindows::CArchConsoleWindows() { }
+
+CArchConsoleWindows::~CArchConsoleWindows() { }
diff --git a/src/lib/arch/CArchConsoleWindows.h b/src/lib/arch/CArchConsoleWindows.h
new file mode 100644
index 00000000..f86f7501
--- /dev/null
+++ b/src/lib/arch/CArchConsoleWindows.h
@@ -0,0 +1,28 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "CArchConsoleStd.h"
+
+#define ARCH_CONSOLE CArchConsoleWindows
+
+class CArchConsoleWindows : public CArchConsoleStd {
+public:
+ CArchConsoleWindows();
+ virtual ~CArchConsoleWindows();
+};
diff --git a/src/lib/arch/CArchDaemonNone.cpp b/src/lib/arch/CArchDaemonNone.cpp
new file mode 100644
index 00000000..65d375f6
--- /dev/null
+++ b/src/lib/arch/CArchDaemonNone.cpp
@@ -0,0 +1,85 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchDaemonNone.h"
+
+//
+// CArchDaemonNone
+//
+
+CArchDaemonNone::CArchDaemonNone()
+{
+ // do nothing
+}
+
+CArchDaemonNone::~CArchDaemonNone()
+{
+ // do nothing
+}
+
+void
+CArchDaemonNone::installDaemon(const char*,
+ const char*,
+ const char*,
+ const char*,
+ const char*,
+ bool)
+{
+ // do nothing
+}
+
+void
+CArchDaemonNone::uninstallDaemon(const char*, bool)
+{
+ // do nothing
+}
+
+int
+CArchDaemonNone::daemonize(const char* name, DaemonFunc func)
+{
+ // simply forward the call to func. obviously, this doesn't
+ // do any daemonizing.
+ return func(1, &name);
+}
+
+bool
+CArchDaemonNone::canInstallDaemon(const char*, bool)
+{
+ return false;
+}
+
+bool
+CArchDaemonNone::isDaemonInstalled(const char*, bool)
+{
+ return false;
+}
+
+void
+CArchDaemonNone::installDaemon()
+{
+}
+
+void
+CArchDaemonNone::uninstallDaemon()
+{
+}
+
+std::string
+CArchDaemonNone::commandLine() const
+{
+ return "";
+}
diff --git a/src/lib/arch/CArchDaemonNone.h b/src/lib/arch/CArchDaemonNone.h
new file mode 100644
index 00000000..8e942b0f
--- /dev/null
+++ b/src/lib/arch/CArchDaemonNone.h
@@ -0,0 +1,53 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHDAEMONNONE_H
+#define CARCHDAEMONNONE_H
+
+#include "IArchDaemon.h"
+
+#define ARCH_DAEMON CArchDaemonNone
+
+//! Dummy implementation of IArchDaemon
+/*!
+This class implements IArchDaemon for a platform that does not have
+daemons. The install and uninstall functions do nothing, the query
+functions return false, and \c daemonize() simply calls the passed
+function and returns its result.
+*/
+class CArchDaemonNone : public IArchDaemon {
+public:
+ CArchDaemonNone();
+ virtual ~CArchDaemonNone();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies,
+ bool allUsers);
+ virtual void uninstallDaemon(const char* name, bool allUsers);
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name, bool allUsers);
+ virtual bool isDaemonInstalled(const char* name, bool allUsers);
+ virtual void installDaemon();
+ virtual void uninstallDaemon();
+ virtual std::string commandLine() const;
+};
+
+#endif
diff --git a/src/lib/arch/CArchDaemonUnix.cpp b/src/lib/arch/CArchDaemonUnix.cpp
new file mode 100644
index 00000000..cc6c986a
--- /dev/null
+++ b/src/lib/arch/CArchDaemonUnix.cpp
@@ -0,0 +1,127 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchDaemonUnix.h"
+#include "XArchUnix.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include "CLog.h"
+
+//
+// CArchDaemonUnix
+//
+
+CArchDaemonUnix::CArchDaemonUnix()
+{
+ // do nothing
+}
+
+CArchDaemonUnix::~CArchDaemonUnix()
+{
+ // do nothing
+}
+
+
+#ifdef __APPLE__
+
+// In Mac OS X, fork()'d child processes can't use most APIs (the frameworks
+// that Synergy uses in fact prevent it and make the process just up and die),
+// so need to exec a copy of the program that doesn't fork so isn't limited.
+int
+execSelfNonDaemonized()
+{
+ extern char** NXArgv;
+ char** selfArgv = NXArgv;
+
+ setenv("_SYNERGY_DAEMONIZED", "", 1);
+
+ execvp(selfArgv[0], selfArgv);
+ return 0;
+}
+
+bool alreadyDaemonized() {
+ return getenv("_SYNERGY_DAEMONIZED") != NULL;
+}
+
+#endif
+
+int
+CArchDaemonUnix::daemonize(const char* name, DaemonFunc func)
+{
+#ifdef __APPLE__
+ if (alreadyDaemonized())
+ return func(1, &name);
+#endif
+
+ // fork so shell thinks we're done and so we're not a process
+ // group leader
+ switch (fork()) {
+ case -1:
+ // failed
+ throw XArchDaemonFailed(new XArchEvalUnix(errno));
+
+ case 0:
+ // child
+ break;
+
+ default:
+ // parent exits
+ exit(0);
+ }
+
+ // become leader of a new session
+ setsid();
+
+#ifndef __APPLE__
+ // NB: don't run chdir on apple; causes strange behaviour.
+ // chdir to root so we don't keep mounted filesystems points busy
+ // TODO: this is a bit of a hack - can we find a better solution?
+ int chdirErr = chdir("/");
+ if (chdirErr)
+ // NB: file logging actually isn't working at this point!
+ LOG((CLOG_ERR "chdir error: %i", chdirErr));
+#endif
+
+ // mask off permissions for any but owner
+ umask(077);
+
+ // close open files. we only expect stdin, stdout, stderr to be open.
+ close(0);
+ close(1);
+ close(2);
+
+ // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use
+ // of standard I/O safely goes in the bit bucket.
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDWR);
+
+ int dupErr = dup(1);
+ if (dupErr)
+ // NB: file logging actually isn't working at this point!
+ LOG((CLOG_ERR "dup error: %i", dupErr));
+
+#ifdef __APPLE__
+ return execSelfNonDaemonized();
+#endif
+
+ // invoke function
+ return func(1, &name);
+}
diff --git a/src/lib/arch/CArchDaemonUnix.h b/src/lib/arch/CArchDaemonUnix.h
new file mode 100644
index 00000000..8c2b9946
--- /dev/null
+++ b/src/lib/arch/CArchDaemonUnix.h
@@ -0,0 +1,38 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHDAEMONUNIX_H
+#define CARCHDAEMONUNIX_H
+
+#include "CArchDaemonNone.h"
+
+#undef ARCH_DAEMON
+#define ARCH_DAEMON CArchDaemonUnix
+
+//! Unix implementation of IArchDaemon
+class CArchDaemonUnix : public CArchDaemonNone {
+public:
+ CArchDaemonUnix();
+ virtual ~CArchDaemonUnix();
+
+ // IArchDaemon overrides
+ virtual int daemonize(const char* name, DaemonFunc func);
+};
+
+#define CONFIG_FILE "/etc/synergy/synergyd.conf"
+
+#endif
diff --git a/src/lib/arch/CArchDaemonWindows.cpp b/src/lib/arch/CArchDaemonWindows.cpp
new file mode 100644
index 00000000..04cb3084
--- /dev/null
+++ b/src/lib/arch/CArchDaemonWindows.cpp
@@ -0,0 +1,841 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchDaemonWindows.h"
+#include "CArch.h"
+#include "CArchMiscWindows.h"
+#include "XArchWindows.h"
+#include "stdvector.h"
+
+//
+// CArchDaemonWindows
+//
+
+CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL;
+
+CArchDaemonWindows::CArchDaemonWindows() :
+m_daemonThreadID(0)
+{
+ m_quitMessage = RegisterWindowMessage("SynergyDaemonExit");
+}
+
+CArchDaemonWindows::~CArchDaemonWindows()
+{
+ // do nothing
+}
+
+int
+CArchDaemonWindows::runDaemon(RunFunc runFunc)
+{
+ assert(s_daemon != NULL);
+
+ return s_daemon->doRunDaemon(runFunc);
+}
+
+void
+CArchDaemonWindows::daemonRunning(bool running)
+{
+ // if s_daemon is NULL we assume we're running on the windows
+ // 95 family and we just ignore this call so the caller doesn't
+ // have to go through the trouble of not calling it on the
+ // windows 95 family.
+ if (s_daemon != NULL) {
+ s_daemon->doDaemonRunning(running);
+ }
+}
+
+UINT
+CArchDaemonWindows::getDaemonQuitMessage()
+{
+ if (s_daemon != NULL) {
+ return s_daemon->doGetDaemonQuitMessage();
+ }
+ else {
+ return 0;
+ }
+}
+
+void
+CArchDaemonWindows::daemonFailed(int result)
+{
+ // if s_daemon is NULL we assume we're running on the windows
+ // 95 family and we just ignore this call so the caller doesn't
+ // have to go through the trouble of not calling it on the
+ // windows 95 family.
+ if (s_daemon != NULL) {
+ throw XArchDaemonRunFailed(result);
+ }
+}
+
+void
+CArchDaemonWindows::installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies,
+ bool allUsers)
+{
+ // if not for all users then use the user's autostart registry.
+ // key. if windows 95 family then use windows 95 services key.
+ if (!allUsers || CArchMiscWindows::isWindows95Family()) {
+ // open registry
+ HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
+ open95ServicesKey() : openUserStartupKey();
+ if (key == NULL) {
+ // can't open key
+ throw XArchDaemonInstallFailed(new XArchEvalWindows);
+ }
+
+ // construct entry
+ std::string value;
+ value += "\"";
+ value += pathname;
+ value += "\" ";
+ value += commandLine;
+
+ // install entry
+ CArchMiscWindows::setValue(key, name, value);
+
+ // clean up
+ CArchMiscWindows::closeKey(key);
+ }
+
+ // windows NT family services
+ else {
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ // can't open service manager
+ throw XArchDaemonInstallFailed(new XArchEvalWindows);
+ }
+
+ // create the service
+ SC_HANDLE service = CreateService(mgr,
+ name,
+ name,
+ 0,
+ SERVICE_WIN32_OWN_PROCESS |
+ SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ pathname,
+ NULL,
+ NULL,
+ dependencies,
+ NULL,
+ NULL);
+ if (service == NULL) {
+ // can't create service
+ DWORD err = GetLastError();
+ if (err != ERROR_SERVICE_EXISTS) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+ }
+ else {
+ // done with service (but only try to close if not null)
+ CloseServiceHandle(service);
+ }
+
+ // done with manager
+ CloseServiceHandle(mgr);
+
+ // open the registry key for this service
+ HKEY key = openNTServicesKey();
+ key = CArchMiscWindows::addKey(key, name);
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ try {
+ uninstallDaemon(name, allUsers);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+
+ // set the description
+ CArchMiscWindows::setValue(key, _T("Description"), description);
+
+ // set command line
+ key = CArchMiscWindows::addKey(key, _T("Parameters"));
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ CArchMiscWindows::closeKey(key);
+ try {
+ uninstallDaemon(name, allUsers);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+ CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
+
+ // done with registry
+ CArchMiscWindows::closeKey(key);
+ }
+}
+
+void
+CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers)
+{
+ // if not for all users then use the user's autostart registry.
+ // key. if windows 95 family then use windows 95 services key.
+ if (!allUsers || CArchMiscWindows::isWindows95Family()) {
+ // open registry
+ HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
+ open95ServicesKey() : openUserStartupKey();
+ if (key == NULL) {
+ // can't open key. daemon is probably not installed.
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows);
+ }
+
+ // remove entry
+ CArchMiscWindows::deleteValue(key, name);
+
+ // clean up
+ CArchMiscWindows::closeKey(key);
+ }
+
+ // windows NT family services
+ else {
+ // remove parameters for this service. ignore failures.
+ HKEY key = openNTServicesKey();
+ key = CArchMiscWindows::openKey(key, name);
+ if (key != NULL) {
+ CArchMiscWindows::deleteKey(key, _T("Parameters"));
+ CArchMiscWindows::closeKey(key);
+ }
+
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ // can't open service manager
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows);
+ }
+
+ // open the service. oddly, you must open a service to delete it.
+ SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP);
+ if (service == NULL) {
+ DWORD err = GetLastError();
+ CloseServiceHandle(mgr);
+ if (err != ERROR_SERVICE_DOES_NOT_EXIST) {
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
+ }
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
+ }
+
+ // stop the service. we don't care if we fail.
+ SERVICE_STATUS status;
+ ControlService(service, SERVICE_CONTROL_STOP, &status);
+
+ // delete the service
+ const bool okay = (DeleteService(service) == 0);
+ const DWORD err = GetLastError();
+
+ // clean up
+ CloseServiceHandle(service);
+ CloseServiceHandle(mgr);
+
+ // give windows a chance to remove the service before
+ // we check if it still exists.
+ ARCH->sleep(1);
+
+ // handle failure. ignore error if service isn't installed anymore.
+ if (!okay && isDaemonInstalled(name, allUsers)) {
+ if (err == ERROR_SUCCESS) {
+ // this seems to occur even though the uninstall was successful.
+ // it could be a timing issue, i.e., isDaemonInstalled is
+ // called too soon. i've added a sleep to try and stop this.
+ return;
+ }
+ if (err == ERROR_IO_PENDING) {
+ // this seems to be a spurious error
+ return;
+ }
+ if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
+ }
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
+ }
+ }
+}
+
+int
+CArchDaemonWindows::daemonize(const char* name, DaemonFunc func)
+{
+ assert(name != NULL);
+ assert(func != NULL);
+
+ // windows 95 family services
+ if (CArchMiscWindows::isWindows95Family()) {
+ typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD);
+
+ // mark this process as a service so it's not killed when the
+ // user logs off.
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows);
+ }
+ RegisterServiceProcessT RegisterServiceProcess =
+ reinterpret_cast(
+ GetProcAddress(kernel,
+ "RegisterServiceProcess"));
+ if (RegisterServiceProcess == NULL) {
+ // missing RegisterServiceProcess function
+ DWORD err = GetLastError();
+ FreeLibrary(kernel);
+ throw XArchDaemonFailed(new XArchEvalWindows(err));
+ }
+ if (RegisterServiceProcess(0, 1) == 0) {
+ // RegisterServiceProcess failed
+ DWORD err = GetLastError();
+ FreeLibrary(kernel);
+ throw XArchDaemonFailed(new XArchEvalWindows(err));
+ }
+ FreeLibrary(kernel);
+
+ // now simply call the daemon function
+ return func(1, &name);
+ }
+
+ // windows NT family services
+ else {
+ // save daemon function
+ m_daemonFunc = func;
+
+ // construct the service entry
+ SERVICE_TABLE_ENTRY entry[2];
+ entry[0].lpServiceName = const_cast(name);
+ entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry;
+ entry[1].lpServiceName = NULL;
+ entry[1].lpServiceProc = NULL;
+
+ // hook us up to the service control manager. this won't return
+ // (if successful) until the processes have terminated.
+ s_daemon = this;
+ if (StartServiceCtrlDispatcher(entry) == 0) {
+ // StartServiceCtrlDispatcher failed
+ s_daemon = NULL;
+ throw XArchDaemonFailed(new XArchEvalWindows);
+ }
+
+ s_daemon = NULL;
+ return m_daemonResult;
+ }
+}
+
+bool
+CArchDaemonWindows::canInstallDaemon(const char* /*name*/, bool allUsers)
+{
+ // if not for all users then use the user's autostart registry.
+ // key. if windows 95 family then use windows 95 services key.
+ if (!allUsers || CArchMiscWindows::isWindows95Family()) {
+ // check if we can open the registry key
+ HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
+ open95ServicesKey() : openUserStartupKey();
+ CArchMiscWindows::closeKey(key);
+ return (key != NULL);
+ }
+
+ // windows NT family services
+ else {
+ // check if we can open service manager for write
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ return false;
+ }
+ CloseServiceHandle(mgr);
+
+ // check if we can open the registry key
+ HKEY key = openNTServicesKey();
+// key = CArchMiscWindows::addKey(key, name);
+// key = CArchMiscWindows::addKey(key, _T("Parameters"));
+ CArchMiscWindows::closeKey(key);
+
+ return (key != NULL);
+ }
+}
+
+bool
+CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ return false;
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(mgr, name, GENERIC_READ);
+
+ // clean up
+ if (service != NULL) {
+ CloseServiceHandle(service);
+ }
+ CloseServiceHandle(mgr);
+
+ return (service != NULL);
+}
+
+HKEY
+CArchDaemonWindows::openNTServicesKey()
+{
+ static const char* s_keyNames[] = {
+ _T("SYSTEM"),
+ _T("CurrentControlSet"),
+ _T("Services"),
+ NULL
+ };
+
+ return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
+}
+
+HKEY
+CArchDaemonWindows::open95ServicesKey()
+{
+ static const char* s_keyNames[] = {
+ _T("Software"),
+ _T("Microsoft"),
+ _T("Windows"),
+ _T("CurrentVersion"),
+ _T("RunServices"),
+ NULL
+ };
+
+ return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
+}
+
+HKEY
+CArchDaemonWindows::openUserStartupKey()
+{
+ static const char* s_keyNames[] = {
+ _T("Software"),
+ _T("Microsoft"),
+ _T("Windows"),
+ _T("CurrentVersion"),
+ _T("Run"),
+ NULL
+ };
+
+ return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames);
+}
+
+bool
+CArchDaemonWindows::isRunState(DWORD state)
+{
+ switch (state) {
+ case SERVICE_START_PENDING:
+ case SERVICE_CONTINUE_PENDING:
+ case SERVICE_RUNNING:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+int
+CArchDaemonWindows::doRunDaemon(RunFunc run)
+{
+ // should only be called from DaemonFunc
+ assert(m_serviceMutex != NULL);
+ assert(run != NULL);
+
+ // create message queue for this thread
+ MSG dummy;
+ PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
+
+ int result = 0;
+ ARCH->lockMutex(m_serviceMutex);
+ m_daemonThreadID = GetCurrentThreadId();
+ while (m_serviceState != SERVICE_STOPPED) {
+ // wait until we're told to start
+ while (!isRunState(m_serviceState) &&
+ m_serviceState != SERVICE_STOP_PENDING) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+
+ // run unless told to stop
+ if (m_serviceState != SERVICE_STOP_PENDING) {
+ ARCH->unlockMutex(m_serviceMutex);
+ try {
+ result = run();
+ }
+ catch (...) {
+ ARCH->lockMutex(m_serviceMutex);
+ setStatusError(0);
+ m_serviceState = SERVICE_STOPPED;
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ ARCH->unlockMutex(m_serviceMutex);
+ throw;
+ }
+ ARCH->lockMutex(m_serviceMutex);
+ }
+
+ // notify of new state
+ if (m_serviceState == SERVICE_PAUSE_PENDING) {
+ m_serviceState = SERVICE_PAUSED;
+ }
+ else {
+ m_serviceState = SERVICE_STOPPED;
+ }
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+ return result;
+}
+
+void
+CArchDaemonWindows::doDaemonRunning(bool running)
+{
+ ARCH->lockMutex(m_serviceMutex);
+ if (running) {
+ m_serviceState = SERVICE_RUNNING;
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+}
+
+UINT
+CArchDaemonWindows::doGetDaemonQuitMessage()
+{
+ return m_quitMessage;
+}
+
+void
+CArchDaemonWindows::setStatus(DWORD state)
+{
+ setStatus(state, 0, 0);
+}
+
+void
+CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
+{
+ assert(s_daemon != NULL);
+
+ SERVICE_STATUS status;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
+ SERVICE_INTERACTIVE_PROCESS;
+ status.dwCurrentState = state;
+ status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ status.dwWin32ExitCode = NO_ERROR;
+ status.dwServiceSpecificExitCode = 0;
+ status.dwCheckPoint = step;
+ status.dwWaitHint = waitHint;
+ SetServiceStatus(s_daemon->m_statusHandle, &status);
+}
+
+void
+CArchDaemonWindows::setStatusError(DWORD error)
+{
+ assert(s_daemon != NULL);
+
+ SERVICE_STATUS status;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
+ SERVICE_INTERACTIVE_PROCESS;
+ status.dwCurrentState = SERVICE_STOPPED;
+ status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+ status.dwServiceSpecificExitCode = error;
+ status.dwCheckPoint = 0;
+ status.dwWaitHint = 0;
+ SetServiceStatus(s_daemon->m_statusHandle, &status);
+}
+
+void
+CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
+{
+ typedef std::vector ArgList;
+ typedef std::vector Arguments;
+ const char** argv = const_cast(argvIn);
+
+ // create synchronization objects
+ m_serviceMutex = ARCH->newMutex();
+ m_serviceCondVar = ARCH->newCondVar();
+
+ // register our service handler function
+ m_statusHandle = RegisterServiceCtrlHandler(argv[0],
+ &CArchDaemonWindows::serviceHandlerEntry);
+ if (m_statusHandle == 0) {
+ // cannot start as service
+ m_daemonResult = -1;
+ ARCH->closeCondVar(m_serviceCondVar);
+ ARCH->closeMutex(m_serviceMutex);
+ return;
+ }
+
+ // tell service control manager that we're starting
+ m_serviceState = SERVICE_START_PENDING;
+ setStatus(m_serviceState, 0, 10000);
+
+ std::string commandLine;
+
+ // if no arguments supplied then try getting them from the registry.
+ // the first argument doesn't count because it's the service name.
+ Arguments args;
+ ArgList myArgv;
+ if (argc <= 1) {
+ // read command line
+ HKEY key = openNTServicesKey();
+ key = CArchMiscWindows::openKey(key, argvIn[0]);
+ key = CArchMiscWindows::openKey(key, _T("Parameters"));
+ if (key != NULL) {
+ commandLine = CArchMiscWindows::readValueString(key,
+ _T("CommandLine"));
+ }
+
+ // if the command line isn't empty then parse and use it
+ if (!commandLine.empty()) {
+ // parse, honoring double quoted substrings
+ std::string::size_type i = commandLine.find_first_not_of(" \t");
+ while (i != std::string::npos && i != commandLine.size()) {
+ // find end of string
+ std::string::size_type e;
+ if (commandLine[i] == '\"') {
+ // quoted. find closing quote.
+ ++i;
+ e = commandLine.find("\"", i);
+
+ // whitespace must follow closing quote
+ if (e == std::string::npos ||
+ (e + 1 != commandLine.size() &&
+ commandLine[e + 1] != ' ' &&
+ commandLine[e + 1] != '\t')) {
+ args.clear();
+ break;
+ }
+
+ // extract
+ args.push_back(commandLine.substr(i, e - i));
+ i = e + 1;
+ }
+ else {
+ // unquoted. find next whitespace.
+ e = commandLine.find_first_of(" \t", i);
+ if (e == std::string::npos) {
+ e = commandLine.size();
+ }
+
+ // extract
+ args.push_back(commandLine.substr(i, e - i));
+ i = e + 1;
+ }
+
+ // next argument
+ i = commandLine.find_first_not_of(" \t", i);
+ }
+
+ // service name goes first
+ myArgv.push_back(argv[0]);
+
+ // get pointers
+ for (size_t j = 0; j < args.size(); ++j) {
+ myArgv.push_back(args[j].c_str());
+ }
+
+ // adjust argc/argv
+ argc = (DWORD)myArgv.size();
+ argv = &myArgv[0];
+ }
+ }
+
+ m_commandLine = commandLine;
+
+ try {
+ // invoke daemon function
+ m_daemonResult = m_daemonFunc(static_cast(argc), argv);
+ }
+ catch (XArchDaemonRunFailed& e) {
+ setStatusError(e.m_result);
+ m_daemonResult = -1;
+ }
+ catch (...) {
+ setStatusError(1);
+ m_daemonResult = -1;
+ }
+
+ // clean up
+ ARCH->closeCondVar(m_serviceCondVar);
+ ARCH->closeMutex(m_serviceMutex);
+
+ // we're going to exit now, so set status to stopped
+ m_serviceState = SERVICE_STOPPED;
+ setStatus(m_serviceState, 0, 10000);
+}
+
+void WINAPI
+CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv)
+{
+ s_daemon->serviceMain(argc, argv);
+}
+
+void
+CArchDaemonWindows::serviceHandler(DWORD ctrl)
+{
+ assert(m_serviceMutex != NULL);
+ assert(m_serviceCondVar != NULL);
+
+ ARCH->lockMutex(m_serviceMutex);
+
+ // ignore request if service is already stopped
+ if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
+ if (s_daemon != NULL) {
+ setStatus(m_serviceState);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+ return;
+ }
+
+ switch (ctrl) {
+ case SERVICE_CONTROL_PAUSE:
+ m_serviceState = SERVICE_PAUSE_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
+ while (isRunState(m_serviceState)) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ // FIXME -- maybe should flush quit messages from queue
+ m_serviceState = SERVICE_CONTINUE_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ break;
+
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+ m_serviceState = SERVICE_STOP_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ while (isRunState(m_serviceState)) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+ break;
+
+ default:
+ // unknown service command
+ // fall through
+
+ case SERVICE_CONTROL_INTERROGATE:
+ setStatus(m_serviceState);
+ break;
+ }
+
+ ARCH->unlockMutex(m_serviceMutex);
+}
+
+void WINAPI
+CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
+{
+ s_daemon->serviceHandler(ctrl);
+}
+
+void
+CArchDaemonWindows::start(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(
+ mgr, name, SERVICE_START);
+
+ if (service == NULL) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // start the service
+ if (!StartService(service, 0, NULL)) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+}
+
+void
+CArchDaemonWindows::stop(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(
+ mgr, name,
+ SERVICE_STOP | SERVICE_QUERY_STATUS);
+
+ if (service == NULL) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // ask the service to stop, asynchronously
+ SERVICE_STATUS ss;
+ if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) {
+ DWORD dwErrCode = GetLastError();
+ if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+ }
+}
+
+void
+CArchDaemonWindows::installDaemon()
+{
+ // install default daemon if not already installed.
+ if (!isDaemonInstalled(DEFAULT_DAEMON_NAME, true)) {
+ char path[MAX_PATH];
+ GetModuleFileName(CArchMiscWindows::instanceWin32(), path, MAX_PATH);
+ installDaemon(DEFAULT_DAEMON_NAME, DEFAULT_DAEMON_INFO, path, "", "", true);
+ }
+
+ start(DEFAULT_DAEMON_NAME);
+}
+
+void
+CArchDaemonWindows::uninstallDaemon()
+{
+ // remove legacy services if installed.
+ if (isDaemonInstalled(LEGACY_SERVER_DAEMON_NAME, true)) {
+ uninstallDaemon(LEGACY_SERVER_DAEMON_NAME, true);
+ }
+ if (isDaemonInstalled(LEGACY_CLIENT_DAEMON_NAME, true)) {
+ uninstallDaemon(LEGACY_CLIENT_DAEMON_NAME, true);
+ }
+
+ // remove new service if installed.
+ if (isDaemonInstalled(DEFAULT_DAEMON_NAME, true)) {
+ uninstallDaemon(DEFAULT_DAEMON_NAME, true);
+ }
+}
diff --git a/src/lib/arch/CArchDaemonWindows.h b/src/lib/arch/CArchDaemonWindows.h
new file mode 100644
index 00000000..dc699ac8
--- /dev/null
+++ b/src/lib/arch/CArchDaemonWindows.h
@@ -0,0 +1,159 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHDAEMONWINDOWS_H
+#define CARCHDAEMONWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchDaemon.h"
+#include "IArchMultithread.h"
+#include "stdstring.h"
+#include
+#include
+
+#define ARCH_DAEMON CArchDaemonWindows
+
+//! Win32 implementation of IArchDaemon
+class CArchDaemonWindows : public IArchDaemon {
+public:
+ typedef int (*RunFunc)(void);
+
+ CArchDaemonWindows();
+ virtual ~CArchDaemonWindows();
+
+ //! Run the daemon
+ /*!
+ When the client calls \c daemonize(), the \c DaemonFunc should call this
+ function after initialization and argument parsing to perform the
+ daemon processing. The \c runFunc should perform the daemon's
+ main loop, calling \c daemonRunning(true) when it enters the main loop
+ (i.e. after initialization) and \c daemonRunning(false) when it leaves
+ the main loop. The \c runFunc is called in a new thread and when the
+ daemon must exit the main loop due to some external control the
+ getDaemonQuitMessage() is posted to the thread. This function returns
+ what \c runFunc returns. \c runFunc should call \c daemonFailed() if
+ the daemon fails.
+ */
+ static int runDaemon(RunFunc runFunc);
+
+ //! Indicate daemon is in main loop
+ /*!
+ The \c runFunc passed to \c runDaemon() should call this function
+ to indicate when it has entered (\c running is \c true) or exited
+ (\c running is \c false) the main loop.
+ */
+ static void daemonRunning(bool running);
+
+ //! Indicate failure of running daemon
+ /*!
+ The \c runFunc passed to \c runDaemon() should call this function
+ to indicate failure. \c result is returned by \c daemonize().
+ */
+ static void daemonFailed(int result);
+
+ //! Get daemon quit message
+ /*!
+ The windows NT daemon tells daemon thread to exit by posting this
+ message to it. The thread must, of course, have a message queue
+ for this to work.
+ */
+ static UINT getDaemonQuitMessage();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies,
+ bool allUsers);
+ virtual void uninstallDaemon(const char* name, bool allUsers);
+ virtual void installDaemon();
+ virtual void uninstallDaemon();
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name, bool allUsers);
+ virtual bool isDaemonInstalled(const char* name, bool allUsers);
+ std::string commandLine() const { return m_commandLine; }
+
+private:
+ static HKEY openNTServicesKey();
+ static HKEY open95ServicesKey();
+ static HKEY openUserStartupKey();
+
+ int doRunDaemon(RunFunc runFunc);
+ void doDaemonRunning(bool running);
+ UINT doGetDaemonQuitMessage();
+
+ static void setStatus(DWORD state);
+ static void setStatus(DWORD state, DWORD step, DWORD waitHint);
+ static void setStatusError(DWORD error);
+
+ static bool isRunState(DWORD state);
+
+ void serviceMain(DWORD, LPTSTR*);
+ static void WINAPI serviceMainEntry(DWORD, LPTSTR*);
+
+ void serviceHandler(DWORD ctrl);
+ static void WINAPI serviceHandlerEntry(DWORD ctrl);
+
+ void start(const char* name);
+ void stop(const char* name);
+
+private:
+ class XArchDaemonRunFailed {
+ public:
+ XArchDaemonRunFailed(int result) : m_result(result) { }
+
+ public:
+ int m_result;
+ };
+
+private:
+ static CArchDaemonWindows* s_daemon;
+
+ CArchMutex m_serviceMutex;
+ CArchCond m_serviceCondVar;
+ DWORD m_serviceState;
+ bool m_serviceHandlerWaiting;
+ bool m_serviceRunning;
+
+ DWORD m_daemonThreadID;
+ DaemonFunc m_daemonFunc;
+ int m_daemonResult;
+
+ SERVICE_STATUS_HANDLE m_statusHandle;
+
+ UINT m_quitMessage;
+
+ std::string m_commandLine;
+};
+
+#define DEFAULT_DAEMON_NAME _T("Synergy")
+#define DEFAULT_DAEMON_INFO _T("Manages the Synergy foreground processes.")
+
+#define LEGACY_SERVER_DAEMON_NAME _T("Synergy Server")
+#define LEGACY_CLIENT_DAEMON_NAME _T("Synergy Client")
+
+static const TCHAR* const g_daemonKeyPath[] = {
+ _T("SOFTWARE"),
+ _T("The Synergy Project"),
+ _T("Synergy"),
+ _T("Service"),
+ NULL
+};
+
+#endif
diff --git a/src/lib/arch/CArchFileUnix.cpp b/src/lib/arch/CArchFileUnix.cpp
new file mode 100644
index 00000000..526811b9
--- /dev/null
+++ b/src/lib/arch/CArchFileUnix.cpp
@@ -0,0 +1,101 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchFileUnix.h"
+#include
+#include
+#include
+#include
+#include
+
+//
+// CArchFileUnix
+//
+
+CArchFileUnix::CArchFileUnix()
+{
+ // do nothing
+}
+
+CArchFileUnix::~CArchFileUnix()
+{
+ // do nothing
+}
+
+const char*
+CArchFileUnix::getBasename(const char* pathname)
+{
+ if (pathname == NULL) {
+ return NULL;
+ }
+
+ const char* basename = strrchr(pathname, '/');
+ if (basename != NULL) {
+ return basename + 1;
+ }
+ else {
+ return pathname;
+ }
+}
+
+std::string
+CArchFileUnix::getUserDirectory()
+{
+ char* buffer = NULL;
+ std::string dir;
+#if HAVE_GETPWUID_R
+ struct passwd pwent;
+ struct passwd* pwentp;
+#if defined(_SC_GETPW_R_SIZE_MAX)
+ long size = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (size == -1) {
+ size = BUFSIZ;
+ }
+#else
+ long size = BUFSIZ;
+#endif
+ buffer = new char[size];
+ getpwuid_r(getuid(), &pwent, buffer, size, &pwentp);
+#else
+ struct passwd* pwentp = getpwuid(getuid());
+#endif
+ if (pwentp != NULL && pwentp->pw_dir != NULL) {
+ dir = pwentp->pw_dir;
+ }
+ delete[] buffer;
+ return dir;
+}
+
+std::string
+CArchFileUnix::getSystemDirectory()
+{
+ return "/etc";
+}
+
+std::string
+CArchFileUnix::concatPath(const std::string& prefix,
+ const std::string& suffix)
+{
+ std::string path;
+ path.reserve(prefix.size() + 1 + suffix.size());
+ path += prefix;
+ if (path.size() == 0 || path[path.size() - 1] != '/') {
+ path += '/';
+ }
+ path += suffix;
+ return path;
+}
diff --git a/src/lib/arch/CArchFileUnix.h b/src/lib/arch/CArchFileUnix.h
new file mode 100644
index 00000000..0c4e9254
--- /dev/null
+++ b/src/lib/arch/CArchFileUnix.h
@@ -0,0 +1,39 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHFILEUNIX_H
+#define CARCHFILEUNIX_H
+
+#include "IArchFile.h"
+
+#define ARCH_FILE CArchFileUnix
+
+//! Unix implementation of IArchFile
+class CArchFileUnix : public IArchFile {
+public:
+ CArchFileUnix();
+ virtual ~CArchFileUnix();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+};
+
+#endif
diff --git a/src/lib/arch/CArchFileWindows.cpp b/src/lib/arch/CArchFileWindows.cpp
new file mode 100644
index 00000000..c75752c5
--- /dev/null
+++ b/src/lib/arch/CArchFileWindows.cpp
@@ -0,0 +1,135 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchFileWindows.h"
+#include
+#include
+#include
+#include
+
+//
+// CArchFileWindows
+//
+
+CArchFileWindows::CArchFileWindows()
+{
+ // do nothing
+}
+
+CArchFileWindows::~CArchFileWindows()
+{
+ // do nothing
+}
+
+const char*
+CArchFileWindows::getBasename(const char* pathname)
+{
+ if (pathname == NULL) {
+ return NULL;
+ }
+
+ // check for last slash
+ const char* basename = strrchr(pathname, '/');
+ if (basename != NULL) {
+ ++basename;
+ }
+ else {
+ basename = pathname;
+ }
+
+ // check for last backslash
+ const char* basename2 = strrchr(pathname, '\\');
+ if (basename2 != NULL && basename2 > basename) {
+ basename = basename2 + 1;
+ }
+
+ return basename;
+}
+
+std::string
+CArchFileWindows::getUserDirectory()
+{
+ // try %HOMEPATH%
+ TCHAR dir[MAX_PATH];
+ DWORD size = sizeof(dir) / sizeof(TCHAR);
+ DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size);
+ if (result != 0 && result <= size) {
+ // sanity check -- if dir doesn't appear to start with a
+ // drive letter and isn't a UNC name then don't use it
+ // FIXME -- allow UNC names
+ if (dir[0] != '\0' && (dir[1] == ':' ||
+ ((dir[0] == '\\' || dir[0] == '/') &&
+ (dir[1] == '\\' || dir[1] == '/')))) {
+ return dir;
+ }
+ }
+
+ // get the location of the personal files. that's as close to
+ // a home directory as we're likely to find.
+ ITEMIDLIST* idl;
+ if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) {
+ TCHAR* path = NULL;
+ if (SHGetPathFromIDList(idl, dir)) {
+ DWORD attr = GetFileAttributes(dir);
+ if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ path = dir;
+ }
+
+ IMalloc* shalloc;
+ if (SUCCEEDED(SHGetMalloc(&shalloc))) {
+ shalloc->Free(idl);
+ shalloc->Release();
+ }
+
+ if (path != NULL) {
+ return path;
+ }
+ }
+
+ // use root of C drive as a default
+ return "C:";
+}
+
+std::string
+CArchFileWindows::getSystemDirectory()
+{
+ // get windows directory
+ char dir[MAX_PATH];
+ if (GetWindowsDirectory(dir, sizeof(dir)) != 0) {
+ return dir;
+ }
+ else {
+ // can't get it. use C:\ as a default.
+ return "C:";
+ }
+}
+
+std::string
+CArchFileWindows::concatPath(const std::string& prefix,
+ const std::string& suffix)
+{
+ std::string path;
+ path.reserve(prefix.size() + 1 + suffix.size());
+ path += prefix;
+ if (path.size() == 0 ||
+ (path[path.size() - 1] != '\\' &&
+ path[path.size() - 1] != '/')) {
+ path += '\\';
+ }
+ path += suffix;
+ return path;
+}
diff --git a/src/lib/arch/CArchFileWindows.h b/src/lib/arch/CArchFileWindows.h
new file mode 100644
index 00000000..a3cf5075
--- /dev/null
+++ b/src/lib/arch/CArchFileWindows.h
@@ -0,0 +1,39 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHFILEWINDOWS_H
+#define CARCHFILEWINDOWS_H
+
+#include "IArchFile.h"
+
+#define ARCH_FILE CArchFileWindows
+
+//! Win32 implementation of IArchFile
+class CArchFileWindows : public IArchFile {
+public:
+ CArchFileWindows();
+ virtual ~CArchFileWindows();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+};
+
+#endif
diff --git a/src/lib/arch/CArchIpcLogUnix.cpp b/src/lib/arch/CArchIpcLogUnix.cpp
new file mode 100644
index 00000000..14a44e0e
--- /dev/null
+++ b/src/lib/arch/CArchIpcLogUnix.cpp
@@ -0,0 +1,46 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchIpcLogUnix.h"
+
+CArchIpcLogUnix::CArchIpcLogUnix()
+{
+}
+
+CArchIpcLogUnix::~CArchIpcLogUnix()
+{
+}
+
+void
+CArchIpcLogUnix::openLog(const char* name)
+{
+}
+
+void
+CArchIpcLogUnix::closeLog()
+{
+}
+
+void
+CArchIpcLogUnix::showLog(bool showIfEmpty)
+{
+}
+
+void
+CArchIpcLogUnix::writeLog(ELevel, const char*)
+{
+}
diff --git a/src/lib/arch/CArchIpcLogUnix.h b/src/lib/arch/CArchIpcLogUnix.h
new file mode 100644
index 00000000..2f11d02e
--- /dev/null
+++ b/src/lib/arch/CArchIpcLogUnix.h
@@ -0,0 +1,34 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "IArchLog.h"
+
+#define ARCH_IPC_LOG CArchIpcLogUnix
+
+class CArchIpcLogUnix : public IArchLog {
+public:
+ CArchIpcLogUnix();
+ virtual ~CArchIpcLogUnix();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool showIfEmpty);
+ virtual void writeLog(ELevel, const char*);
+};
diff --git a/src/lib/arch/CArchIpcLogWindows.cpp b/src/lib/arch/CArchIpcLogWindows.cpp
new file mode 100644
index 00000000..8cf02104
--- /dev/null
+++ b/src/lib/arch/CArchIpcLogWindows.cpp
@@ -0,0 +1,111 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchIpcLogWindows.h"
+#include "CArchMiscWindows.h"
+#include "XArch.h"
+#include "CThread.h"
+#include "TMethodJob.h"
+#include "CArch.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+//
+// CArchIpcLogWindows
+//
+
+CArchIpcLogWindows::CArchIpcLogWindows() :
+ m_pipe(NULL),
+ m_listenThread(NULL),
+ m_connected(false)
+{
+}
+
+CArchIpcLogWindows::~CArchIpcLogWindows()
+{
+ if (m_listenThread != NULL)
+ delete m_listenThread;
+}
+
+void
+CArchIpcLogWindows::openLog(const char* name)
+{
+ // grant access to everyone.
+ SECURITY_DESCRIPTOR sd;
+ InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+ SetSecurityDescriptorDacl(&sd, TRUE, static_cast(0), FALSE);
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = &sd;
+
+ HANDLE pipe = CreateNamedPipe(
+ TEXT("\\\\.\\pipe\\SynergyLog"),
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ 1024, 1024, 0, &sa);
+
+ if (pipe == INVALID_HANDLE_VALUE)
+ XArch("could not create named pipe.");
+
+ m_pipe = pipe;
+
+ m_listenThread = new CThread(new TMethodJob(
+ this, &CArchIpcLogWindows::connectThread, nullptr));
+}
+
+void
+CArchIpcLogWindows::connectThread(void*)
+{
+ // HACK: this seems like a hacky pile of bollocks. it will continuously call
+ // ConnectNamedPipe every second. if there is no client, it will block,
+ // but if there is a client it will return FALSE and GetLastError() will
+ // be ERROR_PIPE_CONNECTED. in any other case, the client has gone away
+ // and ConnectNamedPipe will go back to blocking (waiting for the client
+ // to reconnect).
+ while (true) {
+ BOOL result = ConnectNamedPipe(m_pipe, NULL);
+ if ((result == TRUE) || (GetLastError() == ERROR_PIPE_CONNECTED)) {
+ m_connected = true;
+ ARCH->sleep(1);
+ } else {
+ DisconnectNamedPipe(m_pipe);
+ }
+ }
+}
+
+void
+CArchIpcLogWindows::closeLog()
+{
+}
+
+void
+CArchIpcLogWindows::showLog(bool)
+{
+}
+
+void
+CArchIpcLogWindows::writeLog(ELevel level, const char* data)
+{
+ if (!m_connected)
+ return;
+
+ DWORD bytesWritten;
+ WriteFile(m_pipe, data, (DWORD)strlen(data), &bytesWritten, NULL);
+}
diff --git a/src/lib/arch/CArchIpcLogWindows.h b/src/lib/arch/CArchIpcLogWindows.h
new file mode 100644
index 00000000..85158a0e
--- /dev/null
+++ b/src/lib/arch/CArchIpcLogWindows.h
@@ -0,0 +1,48 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchLog.h"
+#include
+
+#define ARCH_IPC_LOG CArchIpcLogWindows
+
+class CThread;
+
+//! Win32 implementation of IArchLog (IPC version)
+class CArchIpcLogWindows : public IArchLog {
+public:
+ CArchIpcLogWindows();
+ virtual ~CArchIpcLogWindows();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool showIfEmpty);
+ virtual void writeLog(ELevel, const char*);
+
+private:
+ void connectThread(void*);
+
+private:
+ HANDLE m_pipe;
+ CThread* m_listenThread;
+ bool m_connected;
+};
diff --git a/src/lib/arch/CArchLogUnix.cpp b/src/lib/arch/CArchLogUnix.cpp
new file mode 100644
index 00000000..7e66bb76
--- /dev/null
+++ b/src/lib/arch/CArchLogUnix.cpp
@@ -0,0 +1,82 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchLogUnix.h"
+#include
+
+//
+// CArchLogUnix
+//
+
+CArchLogUnix::CArchLogUnix()
+{
+ // do nothing
+}
+
+CArchLogUnix::~CArchLogUnix()
+{
+ // do nothing
+}
+
+void
+CArchLogUnix::openLog(const char* name)
+{
+ openlog(name, 0, LOG_DAEMON);
+}
+
+void
+CArchLogUnix::closeLog()
+{
+ closelog();
+}
+
+void
+CArchLogUnix::showLog(bool)
+{
+ // do nothing
+}
+
+void
+CArchLogUnix::writeLog(ELevel level, const char* msg)
+{
+ // convert level
+ int priority;
+ switch (level) {
+ case kERROR:
+ priority = LOG_ERR;
+ break;
+
+ case kWARNING:
+ priority = LOG_WARNING;
+ break;
+
+ case kNOTE:
+ priority = LOG_NOTICE;
+ break;
+
+ case kINFO:
+ priority = LOG_INFO;
+ break;
+
+ default:
+ priority = LOG_DEBUG;
+ break;
+ }
+
+ // log it
+ syslog(priority, "%s", msg);
+}
diff --git a/src/lib/arch/CArchLogUnix.h b/src/lib/arch/CArchLogUnix.h
new file mode 100644
index 00000000..800526c5
--- /dev/null
+++ b/src/lib/arch/CArchLogUnix.h
@@ -0,0 +1,38 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHLOGUNIX_H
+#define CARCHLOGUNIX_H
+
+#include "IArchLog.h"
+
+#define ARCH_LOG CArchLogUnix
+
+//! Unix implementation of IArchLog
+class CArchLogUnix : public IArchLog {
+public:
+ CArchLogUnix();
+ virtual ~CArchLogUnix();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool);
+ virtual void writeLog(ELevel, const char*);
+};
+
+#endif
diff --git a/src/lib/arch/CArchLogWindows.cpp b/src/lib/arch/CArchLogWindows.cpp
new file mode 100644
index 00000000..fad39e2e
--- /dev/null
+++ b/src/lib/arch/CArchLogWindows.cpp
@@ -0,0 +1,93 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchLogWindows.h"
+#include "CArchMiscWindows.h"
+#include
+
+//
+// CArchLogWindows
+//
+
+CArchLogWindows::CArchLogWindows() : m_eventLog(NULL)
+{
+ // do nothing
+}
+
+CArchLogWindows::~CArchLogWindows()
+{
+ // do nothing
+}
+
+void
+CArchLogWindows::openLog(const char* name)
+{
+ if (m_eventLog == NULL && !CArchMiscWindows::isWindows95Family()) {
+ m_eventLog = RegisterEventSource(NULL, name);
+ }
+}
+
+void
+CArchLogWindows::closeLog()
+{
+ if (m_eventLog != NULL) {
+ DeregisterEventSource(m_eventLog);
+ m_eventLog = NULL;
+ }
+}
+
+void
+CArchLogWindows::showLog(bool)
+{
+ // do nothing
+}
+
+void
+CArchLogWindows::writeLog(ELevel level, const char* msg)
+{
+ if (m_eventLog != NULL) {
+ // convert priority
+ WORD type;
+ switch (level) {
+ case kERROR:
+ type = EVENTLOG_ERROR_TYPE;
+ break;
+
+ case kWARNING:
+ type = EVENTLOG_WARNING_TYPE;
+ break;
+
+ default:
+ type = EVENTLOG_INFORMATION_TYPE;
+ break;
+ }
+
+ // log it
+ // FIXME -- win32 wants to use a message table to look up event
+ // strings. log messages aren't organized that way so we'll
+ // just dump our string into the raw data section of the event
+ // so users can at least see the message. note that we use our
+ // level as the event category.
+ ReportEvent(m_eventLog, type, static_cast(level),
+ 0, // event ID
+ NULL,
+ 0,
+ (DWORD)strlen(msg) + 1, // raw data size
+ NULL,
+ const_cast(msg));// raw data
+ }
+}
diff --git a/src/lib/arch/CArchLogWindows.h b/src/lib/arch/CArchLogWindows.h
new file mode 100644
index 00000000..38ab2931
--- /dev/null
+++ b/src/lib/arch/CArchLogWindows.h
@@ -0,0 +1,44 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHLOGWINDOWS_H
+#define CARCHLOGWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "IArchLog.h"
+#include
+
+#define ARCH_LOG CArchLogWindows
+
+//! Win32 implementation of IArchLog
+class CArchLogWindows : public IArchLog {
+public:
+ CArchLogWindows();
+ virtual ~CArchLogWindows();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool showIfEmpty);
+ virtual void writeLog(ELevel, const char*);
+
+private:
+ HANDLE m_eventLog;
+};
+
+#endif
diff --git a/src/lib/arch/CArchMiscWindows.cpp b/src/lib/arch/CArchMiscWindows.cpp
new file mode 100644
index 00000000..1420fe25
--- /dev/null
+++ b/src/lib/arch/CArchMiscWindows.cpp
@@ -0,0 +1,554 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchMiscWindows.h"
+#include "CArchDaemonWindows.h"
+#include "CLog.h"
+
+#include
+#pragma warning(disable: 4099)
+#include
+#pragma warning(default: 4099)
+#include "Version.h"
+
+// parent process name for services in Vista
+#define SERVICE_LAUNCHER "services.exe"
+
+#ifndef ES_SYSTEM_REQUIRED
+#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001)
+#endif
+#ifndef ES_DISPLAY_REQUIRED
+#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002)
+#endif
+#ifndef ES_CONTINUOUS
+#define ES_CONTINUOUS ((DWORD)0x80000000)
+#endif
+typedef DWORD EXECUTION_STATE;
+
+//
+// CArchMiscWindows
+//
+
+CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL;
+DWORD CArchMiscWindows::s_busyState = 0;
+CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL;
+HICON CArchMiscWindows::s_largeIcon = NULL;
+HICON CArchMiscWindows::s_smallIcon = NULL;
+HINSTANCE CArchMiscWindows::s_instanceWin32 = NULL;
+
+void
+CArchMiscWindows::init()
+{
+ s_dialogs = new CDialogs;
+ isWindows95Family();
+}
+
+bool
+CArchMiscWindows::isWindows95Family()
+{
+ static bool init = false;
+ static bool result = false;
+
+ if (!init) {
+ OSVERSIONINFO version;
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (GetVersionEx(&version) == 0) {
+ // cannot determine OS; assume windows 95 family
+ result = true;
+ }
+ else {
+ result = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
+ }
+ init = true;
+ }
+ return result;
+}
+
+bool
+CArchMiscWindows::isWindowsModern()
+{
+ static bool init = false;
+ static bool result = false;
+
+ if (!init) {
+ OSVERSIONINFO version;
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (GetVersionEx(&version) == 0) {
+ // cannot determine OS; assume not modern
+ result = false;
+ }
+ else {
+ result = ((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
+ version.dwMajorVersion == 4 &&
+ version.dwMinorVersion > 0) ||
+ (version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+ version.dwMajorVersion > 4));
+ }
+ init = true;
+ }
+ return result;
+}
+
+void
+CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon)
+{
+ s_largeIcon = largeIcon;
+ s_smallIcon = smallIcon;
+}
+
+void
+CArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon)
+{
+ largeIcon = s_largeIcon;
+ smallIcon = s_smallIcon;
+}
+
+int
+CArchMiscWindows::runDaemon(RunFunc runFunc)
+{
+ return CArchDaemonWindows::runDaemon(runFunc);
+}
+
+void
+CArchMiscWindows::daemonRunning(bool running)
+{
+ CArchDaemonWindows::daemonRunning(running);
+}
+
+void
+CArchMiscWindows::daemonFailed(int result)
+{
+ CArchDaemonWindows::daemonFailed(result);
+}
+
+UINT
+CArchMiscWindows::getDaemonQuitMessage()
+{
+ return CArchDaemonWindows::getDaemonQuitMessage();
+}
+
+HKEY
+CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, false);
+}
+
+HKEY
+CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, false);
+}
+
+HKEY
+CArchMiscWindows::addKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, true);
+}
+
+HKEY
+CArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, true);
+}
+
+HKEY
+CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create)
+{
+ // ignore if parent is NULL
+ if (key == NULL) {
+ return NULL;
+ }
+
+ // open next key
+ HKEY newKey;
+ LONG result = RegOpenKeyEx(key, keyName, 0,
+ KEY_WRITE | KEY_QUERY_VALUE, &newKey);
+ if (result != ERROR_SUCCESS && create) {
+ DWORD disp;
+ result = RegCreateKeyEx(key, keyName, 0, TEXT(""),
+ 0, KEY_WRITE | KEY_QUERY_VALUE,
+ NULL, &newKey, &disp);
+ }
+ if (result != ERROR_SUCCESS) {
+ RegCloseKey(key);
+ return NULL;
+ }
+
+ // switch to new key
+ RegCloseKey(key);
+ return newKey;
+}
+
+HKEY
+CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create)
+{
+ for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) {
+ // open next key
+ key = openKey(key, keyNames[i], create);
+ }
+ return key;
+}
+
+void
+CArchMiscWindows::closeKey(HKEY key)
+{
+ assert(key != NULL);
+ if (key==NULL) return;
+ RegCloseKey(key);
+}
+
+void
+CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key==NULL || name==NULL) return;
+ RegDeleteKey(key, name);
+}
+
+void
+CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key==NULL || name==NULL) return;
+ RegDeleteValue(key, name);
+}
+
+bool
+CArchMiscWindows::hasValue(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
+ return (result == ERROR_SUCCESS &&
+ (type == REG_DWORD || type == REG_SZ));
+}
+
+CArchMiscWindows::EValueType
+CArchMiscWindows::typeOfValue(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
+ if (result != ERROR_SUCCESS) {
+ return kNO_VALUE;
+ }
+ switch (type) {
+ case REG_DWORD:
+ return kUINT;
+
+ case REG_SZ:
+ return kSTRING;
+
+ case REG_BINARY:
+ return kBINARY;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+CArchMiscWindows::setValue(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if(key ==NULL || name==NULL) return; // TODO: throw exception
+ RegSetValueEx(key, name, 0, REG_SZ,
+ reinterpret_cast(value.c_str()),
+ (DWORD)value.size() + 1);
+}
+
+void
+CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if(key ==NULL || name==NULL) return; // TODO: throw exception
+ RegSetValueEx(key, name, 0, REG_DWORD,
+ reinterpret_cast(&value),
+ sizeof(DWORD));
+}
+
+void
+CArchMiscWindows::setValueBinary(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if(key ==NULL || name==NULL) return; // TODO: throw exception
+ RegSetValueEx(key, name, 0, REG_BINARY,
+ reinterpret_cast(value.data()),
+ (DWORD)value.size());
+}
+
+std::string
+CArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type)
+{
+ // get the size of the string
+ DWORD actualType;
+ DWORD size = 0;
+ LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size);
+ if (result != ERROR_SUCCESS || actualType != type) {
+ return std::string();
+ }
+
+ // if zero size then return empty string
+ if (size == 0) {
+ return std::string();
+ }
+
+ // allocate space
+ char* buffer = new char[size];
+
+ // read it
+ result = RegQueryValueEx(key, name, 0, &actualType,
+ reinterpret_cast(buffer), &size);
+ if (result != ERROR_SUCCESS || actualType != type) {
+ delete[] buffer;
+ return std::string();
+ }
+
+ // clean up and return value
+ if (type == REG_SZ && buffer[size - 1] == '\0') {
+ // don't include terminating nul; std::string will add one.
+ --size;
+ }
+ std::string value(buffer, size);
+ delete[] buffer;
+ return value;
+}
+
+std::string
+CArchMiscWindows::readValueString(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_SZ);
+}
+
+std::string
+CArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_BINARY);
+}
+
+DWORD
+CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ DWORD value;
+ DWORD size = sizeof(value);
+ LONG result = RegQueryValueEx(key, name, 0, &type,
+ reinterpret_cast(&value), &size);
+ if (result != ERROR_SUCCESS || type != REG_DWORD) {
+ return 0;
+ }
+ return value;
+}
+
+void
+CArchMiscWindows::addDialog(HWND hwnd)
+{
+ s_dialogs->insert(hwnd);
+}
+
+void
+CArchMiscWindows::removeDialog(HWND hwnd)
+{
+ s_dialogs->erase(hwnd);
+}
+
+bool
+CArchMiscWindows::processDialog(MSG* msg)
+{
+ for (CDialogs::const_iterator index = s_dialogs->begin();
+ index != s_dialogs->end(); ++index) {
+ if (IsDialogMessage(*index, msg)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+CArchMiscWindows::addBusyState(DWORD busyModes)
+{
+ s_busyState |= busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+CArchMiscWindows::removeBusyState(DWORD busyModes)
+{
+ s_busyState &= ~busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+CArchMiscWindows::setThreadExecutionState(DWORD busyModes)
+{
+ // look up function dynamically so we work on older systems
+ if (s_stes == NULL) {
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel != NULL) {
+ s_stes = reinterpret_cast(GetProcAddress(kernel,
+ "SetThreadExecutionState"));
+ }
+ if (s_stes == NULL) {
+ s_stes = &CArchMiscWindows::dummySetThreadExecutionState;
+ }
+ }
+
+ // convert to STES form
+ EXECUTION_STATE state = 0;
+ if ((busyModes & kSYSTEM) != 0) {
+ state |= ES_SYSTEM_REQUIRED;
+ }
+ if ((busyModes & kDISPLAY) != 0) {
+ state |= ES_DISPLAY_REQUIRED;
+ }
+ if (state != 0) {
+ state |= ES_CONTINUOUS;
+ }
+
+ // do it
+ s_stes(state);
+}
+
+DWORD
+CArchMiscWindows::dummySetThreadExecutionState(DWORD)
+{
+ // do nothing
+ return 0;
+}
+
+void
+CArchMiscWindows::wakeupDisplay()
+{
+ // We can't use ::setThreadExecutionState here because it sets
+ // ES_CONTINUOUS, which we don't want.
+
+ if (s_stes == NULL) {
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel != NULL) {
+ s_stes = reinterpret_cast(GetProcAddress(kernel,
+ "SetThreadExecutionState"));
+ }
+ if (s_stes == NULL) {
+ s_stes = &CArchMiscWindows::dummySetThreadExecutionState;
+ }
+ }
+
+ s_stes(ES_DISPLAY_REQUIRED);
+
+ // restore the original execution states
+ setThreadExecutionState(s_busyState);
+}
+
+bool
+CArchMiscWindows::wasLaunchedAsService()
+{
+ CString name;
+ if (!getParentProcessName(name)) {
+ LOG((CLOG_ERR "cannot determine if process was launched as service"));
+ return false;
+ }
+
+ return (name == SERVICE_LAUNCHER);
+}
+
+bool
+CArchMiscWindows::getParentProcessName(CString &name)
+{
+ PROCESSENTRY32 parentEntry;
+ if (!getParentProcessEntry(parentEntry)){
+ LOG((CLOG_ERR "could not get entry for parent process"));
+ return false;
+ }
+
+ name = parentEntry.szExeFile;
+ return true;
+}
+
+BOOL WINAPI
+CArchMiscWindows::getSelfProcessEntry(PROCESSENTRY32& entry)
+{
+ // get entry from current PID
+ return getProcessEntry(entry, GetCurrentProcessId());
+}
+
+BOOL WINAPI
+CArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry)
+{
+ // get the current process, so we can get parent PID
+ PROCESSENTRY32 selfEntry;
+ if (!getSelfProcessEntry(selfEntry)) {
+ return FALSE;
+ }
+
+ // get entry from parent PID
+ return getProcessEntry(entry, selfEntry.th32ParentProcessID);
+}
+
+BOOL WINAPI
+CArchMiscWindows::getProcessEntry(PROCESSENTRY32& entry, DWORD processID)
+{
+ // first we need to take a snapshot of the running processes
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) {
+ LOG((CLOG_ERR "could not get process snapshot (error: %i)",
+ GetLastError()));
+ return FALSE;
+ }
+
+ entry.dwSize = sizeof(PROCESSENTRY32);
+
+ // get the first process, and if we can't do that then it's
+ // unlikely we can go any further
+ BOOL gotEntry = Process32First(snapshot, &entry);
+ if (!gotEntry) {
+ LOG((CLOG_ERR "could not get first process entry (error: %i)",
+ GetLastError()));
+ return FALSE;
+ }
+
+ while(gotEntry) {
+
+ if (entry.th32ProcessID == processID) {
+ // found current process
+ return TRUE;
+ }
+
+ // now move on to the next entry (when we reach end, loop will stop)
+ gotEntry = Process32Next(snapshot, &entry);
+ }
+
+ return FALSE;
+}
+
+HINSTANCE
+CArchMiscWindows::instanceWin32()
+{
+ assert(s_instanceWin32 != NULL);
+ return s_instanceWin32;
+}
+
+void
+CArchMiscWindows::setInstanceWin32(HINSTANCE instance)
+{
+ assert(instance != NULL);
+ s_instanceWin32 = instance;
+}
\ No newline at end of file
diff --git a/src/lib/arch/CArchMiscWindows.h b/src/lib/arch/CArchMiscWindows.h
new file mode 100644
index 00000000..3abad463
--- /dev/null
+++ b/src/lib/arch/CArchMiscWindows.h
@@ -0,0 +1,214 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CARCHMISCWINDOWS_H
+#define CARCHMISCWINDOWS_H
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "common.h"
+#include "stdstring.h"
+#include "stdset.h"
+#include
+#include
+#include "CString.h"
+
+//! Miscellaneous win32 functions.
+class CArchMiscWindows {
+public:
+ enum EValueType {
+ kUNKNOWN,
+ kNO_VALUE,
+ kUINT,
+ kSTRING,
+ kBINARY
+ };
+ enum EBusyModes {
+ kIDLE = 0x0000,
+ kSYSTEM = 0x0001,
+ kDISPLAY = 0x0002
+ };
+
+ typedef int (*RunFunc)(void);
+
+ //! Initialize
+ static void init();
+
+ //! Test if windows 95, et al.
+ /*!
+ Returns true iff the platform is win95/98/me.
+ */
+ static bool isWindows95Family();
+
+ //! Test if windows 95, et al.
+ /*!
+ Returns true iff the platform is win98 or win2k or higher (i.e.
+ not windows 95 or windows NT).
+ */
+ static bool isWindowsModern();
+
+ //! Set the application icons
+ /*!
+ Set the application icons.
+ */
+ static void setIcons(HICON largeIcon, HICON smallIcon);
+
+ //! Get the application icons
+ /*!
+ Get the application icons.
+ */
+ static void getIcons(HICON& largeIcon, HICON& smallIcon);
+
+ //! Run the daemon
+ /*!
+ Delegates to CArchDaemonWindows.
+ */
+ static int runDaemon(RunFunc runFunc);
+
+ //! Indicate daemon is in main loop
+ /*!
+ Delegates to CArchDaemonWindows.
+ */
+ static void daemonRunning(bool running);
+
+ //! Indicate failure of running daemon
+ /*!
+ Delegates to CArchDaemonWindows.
+ */
+ static void daemonFailed(int result);
+
+ //! Get daemon quit message
+ /*!
+ Delegates to CArchDaemonWindows.
+ */
+ static UINT getDaemonQuitMessage();
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* child);
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* const* keyPath);
+
+ //! Open/create and return a registry key, closing the parent key
+ static HKEY addKey(HKEY parent, const TCHAR* child);
+
+ //! Open/create and return a registry key, closing the parent key
+ static HKEY addKey(HKEY parent, const TCHAR* const* keyPath);
+
+ //! Close a key
+ static void closeKey(HKEY);
+
+ //! Delete a key (which should have no subkeys)
+ static void deleteKey(HKEY parent, const TCHAR* name);
+
+ //! Delete a value
+ static void deleteValue(HKEY parent, const TCHAR* name);
+
+ //! Test if a value exists
+ static bool hasValue(HKEY key, const TCHAR* name);
+
+ //! Get type of value
+ static EValueType typeOfValue(HKEY key, const TCHAR* name);
+
+ //! Set a string value in the registry
+ static void setValue(HKEY key, const TCHAR* name,
+ const std::string& value);
+
+ //! Set a DWORD value in the registry
+ static void setValue(HKEY key, const TCHAR* name, DWORD value);
+
+ //! Set a BINARY value in the registry
+ /*!
+ Sets the \p name value of \p key to \p value.data().
+ */
+ static void setValueBinary(HKEY key, const TCHAR* name,
+ const std::string& value);
+
+ //! Read a string value from the registry
+ static std::string readValueString(HKEY, const TCHAR* name);
+
+ //! Read a DWORD value from the registry
+ static DWORD readValueInt(HKEY, const TCHAR* name);
+
+ //! Read a BINARY value from the registry
+ static std::string readValueBinary(HKEY, const TCHAR* name);
+
+ //! Add a dialog
+ static void addDialog(HWND);
+
+ //! Remove a dialog
+ static void removeDialog(HWND);
+
+ //! Process dialog message
+ /*!
+ Checks if the message is destined for a dialog. If so the message
+ is passed to the dialog and returns true, otherwise returns false.
+ */
+ static bool processDialog(MSG*);
+
+ //! Disable power saving
+ static void addBusyState(DWORD busyModes);
+
+ //! Enable power saving
+ static void removeBusyState(DWORD busyModes);
+
+ //! Briefly interrupt power saving
+ static void wakeupDisplay();
+
+ //! Returns true if this process was launched via NT service host.
+ static bool wasLaunchedAsService();
+
+ //! Returns true if we got the parent process name.
+ static bool getParentProcessName(CString &name);
+
+ static HINSTANCE instanceWin32();
+
+ static void setInstanceWin32(HINSTANCE instance);
+
+ static BOOL WINAPI getProcessEntry(PROCESSENTRY32& entry, DWORD processID);
+ static BOOL WINAPI getSelfProcessEntry(PROCESSENTRY32& entry);
+ static BOOL WINAPI getParentProcessEntry(PROCESSENTRY32& entry);
+
+private:
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* child, bool create);
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* const* keyPath,
+ bool create);
+
+ //! Read a string value from the registry
+ static std::string readBinaryOrString(HKEY, const TCHAR* name, DWORD type);
+
+ //! Set thread busy state
+ static void setThreadExecutionState(DWORD);
+
+ static DWORD WINAPI dummySetThreadExecutionState(DWORD);
+
+private:
+ typedef std::set CDialogs;
+ typedef DWORD (WINAPI *STES_t)(DWORD);
+
+ static CDialogs* s_dialogs;
+ static DWORD s_busyState;
+ static STES_t s_stes;
+ static HICON s_largeIcon;
+ static HICON s_smallIcon;
+ static HINSTANCE s_instanceWin32;
+};
+
+#endif
diff --git a/src/lib/arch/CArchMultithreadPosix.cpp b/src/lib/arch/CArchMultithreadPosix.cpp
new file mode 100644
index 00000000..1c5f0789
--- /dev/null
+++ b/src/lib/arch/CArchMultithreadPosix.cpp
@@ -0,0 +1,809 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "CArchMultithreadPosix.h"
+#include "CArch.h"
+#include "XArch.h"
+#include
+#if TIME_WITH_SYS_TIME
+# include
+# include
+#else
+# if HAVE_SYS_TIME_H
+# include
+# else
+# include