diff --git a/CMakeLists.txt b/CMakeLists.txt index bc23d25a..d46e5c78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,35 +174,35 @@ if (UNIX) else() - # add include dir for bsd (posix uses /usr/include/) - set(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH}:/usr/local/include") - - set(XKBlib "X11/Xlib.h;X11/XKBlib.h") - check_symbol_exists("XRRNotifyEvent" "${XKBlib};X11/extensions/Xrandr.h" HAVE_X11_EXTENSIONS_XRANDR_H) - - check_include_files("${XKBlib};X11/extensions/dpms.h" HAVE_X11_EXTENSIONS_DPMS_H) - check_include_files("X11/extensions/Xinerama.h" HAVE_X11_EXTENSIONS_XINERAMA_H) - check_include_files("${XKBlib};X11/extensions/XKBstr.h" HAVE_X11_EXTENSIONS_XKBSTR_H) - check_include_files("X11/extensions/XKB.h" HAVE_XKB_EXTENSION) - check_include_files("X11/extensions/XTest.h" HAVE_X11_EXTENSIONS_XTEST_H) - check_include_files("${XKBlib}" HAVE_X11_XKBLIB_H) - check_include_files("X11/extensions/XInput2.h" HAVE_XI2) - - if (HAVE_X11_EXTENSIONS_DPMS_H) + # add include dir for bsd (posix uses /usr/include/) + set(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH}:/usr/local/include") + + set(XKBlib "X11/Xlib.h;X11/XKBlib.h") + check_symbol_exists("XRRNotifyEvent" "${XKBlib};X11/extensions/Xrandr.h" HAVE_X11_EXTENSIONS_XRANDR_H) + + check_include_files("${XKBlib};X11/extensions/dpms.h" HAVE_X11_EXTENSIONS_DPMS_H) + check_include_files("X11/extensions/Xinerama.h" HAVE_X11_EXTENSIONS_XINERAMA_H) + check_include_files("${XKBlib};X11/extensions/XKBstr.h" HAVE_X11_EXTENSIONS_XKBSTR_H) + check_include_files("X11/extensions/XKB.h" HAVE_XKB_EXTENSION) + check_include_files("X11/extensions/XTest.h" HAVE_X11_EXTENSIONS_XTEST_H) + check_include_files("${XKBlib}" HAVE_X11_XKBLIB_H) + check_include_files("X11/extensions/XInput2.h" HAVE_XI2) + + if (HAVE_X11_EXTENSIONS_DPMS_H) # Assume that function prototypes declared, when include exists. set(HAVE_DPMS_PROTOTYPES 1) endif() if (NOT HAVE_X11_XKBLIB_H) message(FATAL_ERROR "Missing header: " ${XKBlib}) - endif() - - check_library_exists("SM;ICE" IceConnectionNumber "" HAVE_ICE) - check_library_exists("Xext;X11" DPMSQueryExtension "" HAVE_Xext) - check_library_exists("Xtst;Xext;X11" XTestQueryExtension "" HAVE_Xtst) - check_library_exists("Xinerama" XineramaQueryExtension "" HAVE_Xinerama) - check_library_exists("Xi" XISelectEvents "" HAVE_Xi) - check_library_exists("Xrandr" XRRQueryExtension "" HAVE_Xrandr) + endif() + + check_library_exists("SM;ICE" IceConnectionNumber "" HAVE_ICE) + check_library_exists("Xext;X11" DPMSQueryExtension "" HAVE_Xext) + check_library_exists("Xtst;Xext;X11" XTestQueryExtension "" HAVE_Xtst) + check_library_exists("Xinerama" XineramaQueryExtension "" HAVE_Xinerama) + check_library_exists("Xi" XISelectEvents "" HAVE_Xi) + check_library_exists("Xrandr" XRRQueryExtension "" HAVE_Xrandr) if (HAVE_ICE) @@ -213,13 +213,13 @@ if (UNIX) endif() if (HAVE_Xtst) - - # Xtxt depends on X11. - set(HAVE_X11) - list(APPEND libs Xtst X11) - - else() - + + # Xtxt depends on X11. + set(HAVE_X11) + list(APPEND libs Xtst X11) + + else() + message(FATAL_ERROR "Missing library: Xtst") endif() @@ -229,14 +229,14 @@ if (UNIX) endif() if (HAVE_Xinerama) - list(APPEND libs Xinerama) - else (HAVE_Xinerama) - if (HAVE_X11_EXTENSIONS_XINERAMA_H) - set(HAVE_X11_EXTENSIONS_XINERAMA_H 0) - message(WARNING "Old Xinerama implementation detected, disabled") - endif() - endif() - + list(APPEND libs Xinerama) + else (HAVE_Xinerama) + if (HAVE_X11_EXTENSIONS_XINERAMA_H) + set(HAVE_X11_EXTENSIONS_XINERAMA_H 0) + message(WARNING "Old Xinerama implementation detected, disabled") + endif() + endif() + if (HAVE_Xrandr) list(APPEND libs Xrandr) endif() @@ -382,13 +382,13 @@ if (CONF_DOXYGEN) set(VERSION, "${VERSION}") # For doxygen.cfg, save the results based on a template (doxygen.cfg.in). - configure_file(${cmake_dir}/doxygen.cfg.in ${doc_dir}/doxygen.cfg) - -endif() - -if (${CMAKE_SYSTEM_NAME} MATCHES "IRIX") - set_target_properties(synergys PROPERTIES LINK_FLAGS "-all -woff 33 -woff 84 -woff 15") - set_target_properties(synergyc PROPERTIES LINK_FLAGS "-all -woff 33 -woff 84 -woff 15") - set_target_properties(synergyd PROPERTIES LINK_FLAGS "-all -woff 33 -woff 84 -woff 15") -endif() - + configure_file(${cmake_dir}/doxygen.cfg.in ${doc_dir}/doxygen.cfg) + +endif() + +if (${CMAKE_SYSTEM_NAME} MATCHES "IRIX") + set_target_properties(synergys PROPERTIES LINK_FLAGS "-all -woff 33 -woff 84 -woff 15") + set_target_properties(synergyc PROPERTIES LINK_FLAGS "-all -woff 33 -woff 84 -woff 15") + set_target_properties(synergyd PROPERTIES LINK_FLAGS "-all -woff 33 -woff 84 -woff 15") +endif() + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ee77129..930d4ca8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,11 +13,11 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -add_subdirectory(lib) -add_subdirectory(cmd) -if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "IRIX") - add_subdirectory(test) -endif() -add_subdirectory(plugin) -add_subdirectory(micro) + +add_subdirectory(lib) +add_subdirectory(cmd) +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "IRIX") + add_subdirectory(test) +endif() +add_subdirectory(plugin) +add_subdirectory(micro) diff --git a/src/cmd/synergyp/synergyp.cpp b/src/cmd/synergyp/synergyp.cpp index af1d1886..a6f6b1db 100644 --- a/src/cmd/synergyp/synergyp.cpp +++ b/src/cmd/synergyp/synergyp.cpp @@ -64,17 +64,17 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdSh } if (!server && !client) { - MessageBox(NULL, - "Either the --server argument or the --client argument must be provided.", - "Server or client?", MB_OK); + MessageBox(NULL, + "Either the --server argument or the --client argument must be provided.", + "Server or client?", MB_OK); return 1; } if (argc <= 2) { - MessageBox(NULL, - "No additional arguments were provided. Append the --help argument for help.\n\n" - "Hint: Create a shortcut and append the \"Target\" field with the arguments.", - "No additional arguments", MB_OK); + MessageBox(NULL, + "No additional arguments were provided. Append the --help argument for help.\n\n" + "Hint: Create a shortcut and append the \"Target\" field with the arguments.", + "No additional arguments", MB_OK); return 1; } diff --git a/src/gui/src/CryptoMode.h b/src/gui/src/CryptoMode.h index 5db9964c..bd5f9a6c 100644 --- a/src/gui/src/CryptoMode.h +++ b/src/gui/src/CryptoMode.h @@ -1,26 +1,26 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Bolton Software Ltd. - * - * 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 - -enum CryptoMode { - Disabled, - OFB, - CFB, - CTR, - GCM -}; +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * 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 + +enum CryptoMode { + Disabled, + OFB, + CFB, + CTR, + GCM +}; diff --git a/src/gui/src/Ipc.cpp b/src/gui/src/Ipc.cpp index 4f7d9316..4b7cec65 100644 --- a/src/gui/src/Ipc.cpp +++ b/src/gui/src/Ipc.cpp @@ -1,26 +1,26 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 . - */ - -// this class is a duplicate of /src/lib/ipc/Ipc.cpp - -#include "Ipc.h" - -const char* kIpcMsgHello = "IHEL%1i"; -const char* kIpcMsgLogLine = "ILOG%s"; -const char* kIpcMsgCommand = "ICMD%s%1i"; -const char* kIpcMsgShutdown = "ISDN"; + * 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 . + */ + +// this class is a duplicate of /src/lib/ipc/Ipc.cpp + +#include "Ipc.h" + +const char* kIpcMsgHello = "IHEL%1i"; +const char* kIpcMsgLogLine = "ILOG%s"; +const char* kIpcMsgCommand = "ICMD%s%1i"; +const char* kIpcMsgShutdown = "ISDN"; diff --git a/src/gui/src/Ipc.h b/src/gui/src/Ipc.h index 0bae0733..0af53b18 100644 --- a/src/gui/src/Ipc.h +++ b/src/gui/src/Ipc.h @@ -1,42 +1,42 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 . - */ - -// this class is a duplicate of /src/lib/ipc/Ipc.h - -#pragma once - -#define IPC_HOST "127.0.0.1" -#define IPC_PORT 24801 - -enum qIpcMessageType { - kIpcHello, - kIpcLogLine, - kIpcCommand, - kIpcShutdown, -}; - -enum qIpcClientType { - kIpcClientUnknown, - kIpcClientGui, - kIpcClientNode, -}; - -extern const char* kIpcMsgHello; -extern const char* kIpcMsgLogLine; -extern const char* kIpcMsgCommand; -extern const char* kIpcMsgShutdown; + * 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 . + */ + +// this class is a duplicate of /src/lib/ipc/Ipc.h + +#pragma once + +#define IPC_HOST "127.0.0.1" +#define IPC_PORT 24801 + +enum qIpcMessageType { + kIpcHello, + kIpcLogLine, + kIpcCommand, + kIpcShutdown, +}; + +enum qIpcClientType { + kIpcClientUnknown, + kIpcClientGui, + kIpcClientNode, +}; + +extern const char* kIpcMsgHello; +extern const char* kIpcMsgLogLine; +extern const char* kIpcMsgCommand; +extern const char* kIpcMsgShutdown; diff --git a/src/gui/src/IpcClient.cpp b/src/gui/src/IpcClient.cpp index 0eeb5d29..c899e677 100644 --- a/src/gui/src/IpcClient.cpp +++ b/src/gui/src/IpcClient.cpp @@ -1,147 +1,147 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 "IpcClient.h" -#include -#include -#include -#include -#include "IpcReader.h" -#include "Ipc.h" - -IpcClient::IpcClient() : -m_ReaderStarted(false), -m_Enabled(false) -{ - m_Socket = new QTcpSocket(this); - connect(m_Socket, SIGNAL(connected()), this, SLOT(connected())); - connect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError))); - - m_Reader = new IpcReader(m_Socket); - connect(m_Reader, SIGNAL(readLogLine(const QString&)), this, SLOT(handleReadLogLine(const QString&))); -} - -IpcClient::~IpcClient() -{ -} - -void IpcClient::connected() -{ - char typeBuf[1]; - typeBuf[0] = kIpcClientGui; - sendHello(); - - infoMessage("connection established"); -} - -void IpcClient::connectToHost() -{ - m_Enabled = true; - - infoMessage("connecting to service..."); - m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), IPC_PORT); - - if (!m_ReaderStarted) { - m_Reader->start(); - m_ReaderStarted = true; - } -} - -void IpcClient::disconnectFromHost() -{ - infoMessage("service disconnect"); - m_Reader->stop(); - m_Socket->close(); -} - -void IpcClient::error(QAbstractSocket::SocketError error) -{ - QString text; - switch (error) { - case 0: text = "connection refused"; break; - case 1: text = "remote host closed"; break; - default: text = QString("code=%1").arg(error); break; - } - - errorMessage(QString("ipc connection error, %1").arg(text)); - - QTimer::singleShot(1000, this, SLOT(retryConnect())); -} - -void IpcClient::retryConnect() -{ - if (m_Enabled) { - connectToHost(); - } -} - -void IpcClient::sendHello() -{ - QDataStream stream(m_Socket); - stream.writeRawData(kIpcMsgHello, 4); - - char typeBuf[1]; - typeBuf[0] = kIpcClientGui; - stream.writeRawData(typeBuf, 1); -} - -void IpcClient::sendCommand(const QString& command, bool elevate) -{ - QDataStream stream(m_Socket); - - stream.writeRawData(kIpcMsgCommand, 4); - - std::string stdStringCommand = command.toStdString(); - const char* charCommand = stdStringCommand.c_str(); - int length = strlen(charCommand); - - char lenBuf[4]; - intToBytes(length, lenBuf, 4); - stream.writeRawData(lenBuf, 4); - stream.writeRawData(charCommand, length); - - char elevateBuf[1]; - elevateBuf[0] = elevate ? 1 : 0; - stream.writeRawData(elevateBuf, 1); -} - -void IpcClient::handleReadLogLine(const QString& text) -{ - readLogLine(text); -} - -// TODO: qt must have a built in way of converting int to bytes. -void IpcClient::intToBytes(int value, char *buffer, int size) -{ - if (size == 1) { - buffer[0] = value & 0xff; - } - else if (size == 2) { - buffer[0] = (value >> 8) & 0xff; - buffer[1] = value & 0xff; - } - else if (size == 4) { - buffer[0] = (value >> 24) & 0xff; - buffer[1] = (value >> 16) & 0xff; - buffer[2] = (value >> 8) & 0xff; - buffer[3] = value & 0xff; - } - else { - // TODO: other sizes, if needed. - } -} + * 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 "IpcClient.h" +#include +#include +#include +#include +#include "IpcReader.h" +#include "Ipc.h" + +IpcClient::IpcClient() : +m_ReaderStarted(false), +m_Enabled(false) +{ + m_Socket = new QTcpSocket(this); + connect(m_Socket, SIGNAL(connected()), this, SLOT(connected())); + connect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError))); + + m_Reader = new IpcReader(m_Socket); + connect(m_Reader, SIGNAL(readLogLine(const QString&)), this, SLOT(handleReadLogLine(const QString&))); +} + +IpcClient::~IpcClient() +{ +} + +void IpcClient::connected() +{ + char typeBuf[1]; + typeBuf[0] = kIpcClientGui; + sendHello(); + + infoMessage("connection established"); +} + +void IpcClient::connectToHost() +{ + m_Enabled = true; + + infoMessage("connecting to service..."); + m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), IPC_PORT); + + if (!m_ReaderStarted) { + m_Reader->start(); + m_ReaderStarted = true; + } +} + +void IpcClient::disconnectFromHost() +{ + infoMessage("service disconnect"); + m_Reader->stop(); + m_Socket->close(); +} + +void IpcClient::error(QAbstractSocket::SocketError error) +{ + QString text; + switch (error) { + case 0: text = "connection refused"; break; + case 1: text = "remote host closed"; break; + default: text = QString("code=%1").arg(error); break; + } + + errorMessage(QString("ipc connection error, %1").arg(text)); + + QTimer::singleShot(1000, this, SLOT(retryConnect())); +} + +void IpcClient::retryConnect() +{ + if (m_Enabled) { + connectToHost(); + } +} + +void IpcClient::sendHello() +{ + QDataStream stream(m_Socket); + stream.writeRawData(kIpcMsgHello, 4); + + char typeBuf[1]; + typeBuf[0] = kIpcClientGui; + stream.writeRawData(typeBuf, 1); +} + +void IpcClient::sendCommand(const QString& command, bool elevate) +{ + QDataStream stream(m_Socket); + + stream.writeRawData(kIpcMsgCommand, 4); + + std::string stdStringCommand = command.toStdString(); + const char* charCommand = stdStringCommand.c_str(); + int length = strlen(charCommand); + + char lenBuf[4]; + intToBytes(length, lenBuf, 4); + stream.writeRawData(lenBuf, 4); + stream.writeRawData(charCommand, length); + + char elevateBuf[1]; + elevateBuf[0] = elevate ? 1 : 0; + stream.writeRawData(elevateBuf, 1); +} + +void IpcClient::handleReadLogLine(const QString& text) +{ + readLogLine(text); +} + +// TODO: qt must have a built in way of converting int to bytes. +void IpcClient::intToBytes(int value, char *buffer, int size) +{ + if (size == 1) { + buffer[0] = value & 0xff; + } + else if (size == 2) { + buffer[0] = (value >> 8) & 0xff; + buffer[1] = value & 0xff; + } + else if (size == 4) { + buffer[0] = (value >> 24) & 0xff; + buffer[1] = (value >> 16) & 0xff; + buffer[2] = (value >> 8) & 0xff; + buffer[3] = value & 0xff; + } + else { + // TODO: other sizes, if needed. + } +} diff --git a/src/gui/src/IpcClient.h b/src/gui/src/IpcClient.h index 6830483b..b417b356 100644 --- a/src/gui/src/IpcClient.h +++ b/src/gui/src/IpcClient.h @@ -1,61 +1,61 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 QTcpSocket; -class IpcReader; - -class IpcClient : public QObject -{ - Q_OBJECT - -public: - IpcClient(); - virtual ~IpcClient(); - - void sendHello(); - void sendCommand(const QString& command, bool elevate); - void connectToHost(); - void disconnectFromHost(); - -public slots: - void retryConnect(); - -private: - void intToBytes(int value, char* buffer, int size); - -private slots: - void connected(); - void error(QAbstractSocket::SocketError error); - void handleReadLogLine(const QString& text); - -signals: - void readLogLine(const QString& text); - void infoMessage(const QString& text); - void errorMessage(const QString& text); - -private: - QTcpSocket* m_Socket; - IpcReader* m_Reader; - bool m_ReaderStarted; - bool m_Enabled; -}; + * 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 QTcpSocket; +class IpcReader; + +class IpcClient : public QObject +{ + Q_OBJECT + +public: + IpcClient(); + virtual ~IpcClient(); + + void sendHello(); + void sendCommand(const QString& command, bool elevate); + void connectToHost(); + void disconnectFromHost(); + +public slots: + void retryConnect(); + +private: + void intToBytes(int value, char* buffer, int size); + +private slots: + void connected(); + void error(QAbstractSocket::SocketError error); + void handleReadLogLine(const QString& text); + +signals: + void readLogLine(const QString& text); + void infoMessage(const QString& text); + void errorMessage(const QString& text); + +private: + QTcpSocket* m_Socket; + IpcReader* m_Reader; + bool m_ReaderStarted; + bool m_Enabled; +}; diff --git a/src/gui/src/IpcReader.cpp b/src/gui/src/IpcReader.cpp index 288fb432..87bb8041 100644 --- a/src/gui/src/IpcReader.cpp +++ b/src/gui/src/IpcReader.cpp @@ -1,131 +1,131 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 "IpcReader.h" -#include -#include "Ipc.h" -#include -#include -#include - -IpcReader::IpcReader(QTcpSocket* socket) : -m_Socket(socket) -{ -} - -IpcReader::~IpcReader() -{ -} - -void IpcReader::start() -{ - connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read())); -} - -void IpcReader::stop() -{ - disconnect(m_Socket, SIGNAL(readyRead()), this, SLOT(read())); -} - -void IpcReader::read() -{ - QMutexLocker locker(&m_Mutex); - std::cout << "ready read" << std::endl; - - while (m_Socket->bytesAvailable()) { - std::cout << "bytes available" << std::endl; - - char codeBuf[5]; - readStream(codeBuf, 4); - codeBuf[4] = 0; - std::cout << "ipc read: " << codeBuf << std::endl; - - if (memcmp(codeBuf, kIpcMsgLogLine, 4) == 0) { - std::cout << "reading log line" << std::endl; - - char lenBuf[4]; - readStream(lenBuf, 4); - int len = bytesToInt(lenBuf, 4); - - char* data = new char[len]; - readStream(data, len); - QString line = QString::fromUtf8(data, len); - delete data; - - readLogLine(line); - } - else { - std::cerr << "aborting, message invalid" << std::endl; - return; - } - } - - std::cout << "read done" << std::endl; -} - -bool IpcReader::readStream(char* buffer, int length) -{ - std::cout << "reading stream" << std::endl; - - int read = 0; - while (read < length) { - int ask = length - read; - if (m_Socket->bytesAvailable() < ask) { - std::cout << "buffer too short, waiting" << std::endl; - m_Socket->waitForReadyRead(-1); - } - - int got = m_Socket->read(buffer, ask); - read += got; - - std::cout << "> ask=" << ask << " got=" << got - << " read=" << read << std::endl; - - if (got == -1) { - std::cout << "socket ended, aborting" << std::endl; - return false; - } - else if (length - read > 0) { - std::cout << "more remains, seek to " << got << std::endl; - buffer += got; - } - } - return true; -} - -int IpcReader::bytesToInt(const char *buffer, int size) -{ - if (size == 1) { - return (unsigned char)buffer[0]; - } - else if (size == 2) { - return - (((unsigned char)buffer[0]) << 8) + - (unsigned char)buffer[1]; - } - else if (size == 4) { - return - (((unsigned char)buffer[0]) << 24) + - (((unsigned char)buffer[1]) << 16) + - (((unsigned char)buffer[2]) << 8) + - (unsigned char)buffer[3]; - } - else { - return 0; - } -} + * 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 "IpcReader.h" +#include +#include "Ipc.h" +#include +#include +#include + +IpcReader::IpcReader(QTcpSocket* socket) : +m_Socket(socket) +{ +} + +IpcReader::~IpcReader() +{ +} + +void IpcReader::start() +{ + connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read())); +} + +void IpcReader::stop() +{ + disconnect(m_Socket, SIGNAL(readyRead()), this, SLOT(read())); +} + +void IpcReader::read() +{ + QMutexLocker locker(&m_Mutex); + std::cout << "ready read" << std::endl; + + while (m_Socket->bytesAvailable()) { + std::cout << "bytes available" << std::endl; + + char codeBuf[5]; + readStream(codeBuf, 4); + codeBuf[4] = 0; + std::cout << "ipc read: " << codeBuf << std::endl; + + if (memcmp(codeBuf, kIpcMsgLogLine, 4) == 0) { + std::cout << "reading log line" << std::endl; + + char lenBuf[4]; + readStream(lenBuf, 4); + int len = bytesToInt(lenBuf, 4); + + char* data = new char[len]; + readStream(data, len); + QString line = QString::fromUtf8(data, len); + delete data; + + readLogLine(line); + } + else { + std::cerr << "aborting, message invalid" << std::endl; + return; + } + } + + std::cout << "read done" << std::endl; +} + +bool IpcReader::readStream(char* buffer, int length) +{ + std::cout << "reading stream" << std::endl; + + int read = 0; + while (read < length) { + int ask = length - read; + if (m_Socket->bytesAvailable() < ask) { + std::cout << "buffer too short, waiting" << std::endl; + m_Socket->waitForReadyRead(-1); + } + + int got = m_Socket->read(buffer, ask); + read += got; + + std::cout << "> ask=" << ask << " got=" << got + << " read=" << read << std::endl; + + if (got == -1) { + std::cout << "socket ended, aborting" << std::endl; + return false; + } + else if (length - read > 0) { + std::cout << "more remains, seek to " << got << std::endl; + buffer += got; + } + } + return true; +} + +int IpcReader::bytesToInt(const char *buffer, int size) +{ + if (size == 1) { + return (unsigned char)buffer[0]; + } + else if (size == 2) { + return + (((unsigned char)buffer[0]) << 8) + + (unsigned char)buffer[1]; + } + else if (size == 4) { + return + (((unsigned char)buffer[0]) << 24) + + (((unsigned char)buffer[1]) << 16) + + (((unsigned char)buffer[2]) << 8) + + (unsigned char)buffer[3]; + } + else { + return 0; + } +} diff --git a/src/gui/src/IpcReader.h b/src/gui/src/IpcReader.h index 9c9f2942..067500a4 100644 --- a/src/gui/src/IpcReader.h +++ b/src/gui/src/IpcReader.h @@ -1,49 +1,49 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 QTcpSocket; - -class IpcReader : public QObject -{ - Q_OBJECT; - -public: - IpcReader(QTcpSocket* socket); - virtual ~IpcReader(); - void start(); - void stop(); - -signals: - void readLogLine(const QString& text); - -private: - bool readStream(char* buffer, int length); - int bytesToInt(const char* buffer, int size); - -private slots: - void read(); - -private: - QTcpSocket* m_Socket; - QMutex m_Mutex; -}; + * 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 QTcpSocket; + +class IpcReader : public QObject +{ + Q_OBJECT; + +public: + IpcReader(QTcpSocket* socket); + virtual ~IpcReader(); + void start(); + void stop(); + +signals: + void readLogLine(const QString& text); + +private: + bool readStream(char* buffer, int length); + int bytesToInt(const char* buffer, int size); + +private slots: + void read(); + +private: + QTcpSocket* m_Socket; + QMutex m_Mutex; +}; diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 6c27af9b..dc9e0e90 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -1,51 +1,51 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Bolton Software Ltd. - * - * 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 "QUtility.h" - -void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData) -{ - for (int i = 0; i < comboBox->count(); ++i) - { - if (comboBox->itemData(i) == itemData) - { - comboBox->setCurrentIndex(i); - return; - } - } -} - -QString hash(const QString& string) -{ - QByteArray data = string.toUtf8(); - QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5); - return hash.toHex(); -} - -QString getFirstMacAddress() -{ - QString mac; - foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) - { - mac = interface.hardwareAddress(); - if (mac.size() != 0) - { - break; - } - } - return mac; -} +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * 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 "QUtility.h" + +void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData) +{ + for (int i = 0; i < comboBox->count(); ++i) + { + if (comboBox->itemData(i) == itemData) + { + comboBox->setCurrentIndex(i); + return; + } + } +} + +QString hash(const QString& string) +{ + QByteArray data = string.toUtf8(); + QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5); + return hash.toHex(); +} + +QString getFirstMacAddress() +{ + QString mac; + foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) + { + mac = interface.hardwareAddress(); + if (mac.size() != 0) + { + break; + } + } + return mac; +} diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index fa435c82..77e9b916 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -1,27 +1,27 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Bolton Software Ltd. - * - * 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 -#include -#include - -void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData); -QString hash(const QString& string); -QString getFirstMacAddress(); +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * 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 +#include +#include + +void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData); +QString hash(const QString& string); +QString getFirstMacAddress(); diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index f2a438b9..dc2e19ca 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -1,334 +1,334 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Bolton Software Ltd. - * - * 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 "SetupWizard.h" -#include "MainWindow.h" -#include "QSynergyApplication.h" -#include "QUtility.h" - -#include -#include -#include -#include -#include - -//#define PREMIUM_AUTH_URL "http://localhost/synergy/premium/json/auth/" -#define PREMIUM_AUTH_URL "https://synergy-foss.org/premium/json/auth/" -#define PREMIUM_REGISTER_URL "https://synergy-foss.org/premium/register/?source=gui-wizard" - -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 - - 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))); - - m_Locale.fillLanguageComboBox(m_pComboLanguage); - setIndexFromItemData(m_pComboLanguage, m_MainWindow.appConfig().language()); - AppConfig& appConfig = m_MainWindow.appConfig(); - QString premiumEmail = appConfig.premiumEmail(); - if (!premiumEmail.isEmpty()) - { - m_pRadioButtonPremiumLogin->setChecked(true); - m_pLineEditPremiumEmail->setText(premiumEmail); - } -} - -SetupWizard::~SetupWizard() -{ -} - -bool SetupWizard::validateCurrentPage() -{ - QMessageBox message; - message.setWindowTitle(tr("Setup Synergy")); - message.setIcon(QMessageBox::Information); - - if (currentPage() == m_pNodePage) - { - bool result = m_pClientRadioButton->isChecked() || - m_pServerRadioButton->isChecked(); - - if (!result) - { - message.setText(tr("Please select an option.")); - message.exec(); - return false; - } - } - else if (currentPage() == m_pPremiumUserPage) - { - if (m_pRadioButtonPremiumLogin->isChecked()) - { - if (m_pLineEditPremiumEmail->text().isEmpty() || - m_pLineEditPremiumPassword->text().isEmpty()) - { - message.setText(tr("Please enter your email address and password.")); - message.exec(); - return false; - } - else if (!isPremiumLoginValid(message)) - { - return false; - } - else - { - m_pComboCryptoMode->setCurrentIndex(0); - m_pComboCryptoMode->setEnabled(true); - } - } - else if (m_pRadioButtonPremiumRegister->isChecked()) - { - const QUrl url(QString(PREMIUM_REGISTER_URL)); - QDesktopServices::openUrl(url); - m_pRadioButtonPremiumLogin->setChecked(true); - return false; - } - else if (m_pRadioButtonPremiumLater->isChecked()) - { - int size = m_pComboCryptoMode->count(); - m_pComboCryptoMode->setCurrentIndex(size - 1); - m_pComboCryptoMode->setEnabled(false); - } - else { - message.setText(tr("Please select an option.")); - message.exec(); - return false; - } - } - else if (currentPage() == m_pCryptoPage) - { - QString modeText = m_pComboCryptoMode->currentText(); - if (modeText.isEmpty()) - { - message.setText(tr("Encryption mode required.")); - message.exec(); - return false; - } - - if (parseCryptoMode(modeText) != Disabled) - { - if (m_pLineEditCryptoPassword1->text().isEmpty()) - { - message.setText(tr("Encryption password required.")); - message.exec(); - return false; - } - - if (m_pLineEditCryptoPassword1->text() != m_pLineEditCryptoPassword2->text()) - { - message.setText(tr("Encryption password and confirmation do not match.")); - message.exec(); - return false; - } - } - } - - return true; -} - -void SetupWizard::changeEvent(QEvent* event) -{ - if (event != 0) - { - switch (event->type()) - { - case QEvent::LanguageChange: - { - m_pComboLanguage->blockSignals(true); - retranslateUi(this); - m_pComboLanguage->blockSignals(false); - break; - } - - default: - QWizard::changeEvent(event); - } - } -} - -void SetupWizard::accept() -{ - AppConfig& appConfig = m_MainWindow.appConfig(); - - appConfig.setCryptoMode(parseCryptoMode(m_pComboCryptoMode->currentText())); - appConfig.setCryptoPass(m_pLineEditCryptoPassword1->text()); - appConfig.setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()); - appConfig.setPremiumEmail(m_pLineEditPremiumEmail->text()); - - if (!m_pRadioButtonPremiumLogin->isChecked()) - { - appConfig.setPremiumToken(""); - } - else - { - QString mac = getFirstMacAddress(); - QString hashSrc = m_pLineEditPremiumEmail->text() + mac; - QString hashResult = hash(hashSrc); - appConfig.setPremiumToken(hashResult); - } - - appConfig.setWizardHasRun(); - 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); - } - - QWizard::accept(); -} - -void SetupWizard::reject() -{ - QSynergyApplication::getInstance()->switchTranslator(m_MainWindow.appConfig().language()); - - if (m_StartMain) - { - m_MainWindow.start(true); - } - - QWizard::reject(); -} - -void SetupWizard::on_m_pComboCryptoMode_currentIndexChanged(int index) -{ - bool enabled = parseCryptoMode(m_pComboCryptoMode->currentText()) != Disabled; - m_pLineEditCryptoPassword1->setEnabled(enabled); - m_pLineEditCryptoPassword2->setEnabled(enabled); -} - -CryptoMode SetupWizard::parseCryptoMode(const QString& s) -{ - if (s.startsWith("OFB")) - { - return OFB; - } - else if (s.startsWith("CFB")) - { - return CFB; - } - else if (s.startsWith("CTR")) - { - return CTR; - } - else if (s.startsWith("GCM")) - { - return GCM; - } - - return Disabled; -} - -void SetupWizard::on_m_pComboLanguage_currentIndexChanged(int index) -{ - QString ietfCode = m_pComboLanguage->itemData(index).toString(); - QSynergyApplication::getInstance()->switchTranslator(ietfCode); -} - -void SetupWizard::on_m_pRadioButtonPremiumLogin_toggled(bool checked) -{ - m_pLineEditPremiumEmail->setEnabled(checked); - m_pLineEditPremiumPassword->setEnabled(checked); -} - -bool SetupWizard::isPremiumLoginValid(QMessageBox& message) -{ - QString email = m_pLineEditPremiumEmail->text(); - QString password = m_pLineEditPremiumPassword->text(); - - QString requestJson = "{\"email\":\"" + email + "\",\"password\":\"" + password + "\"}"; - QByteArray requestData(requestJson.toStdString().c_str()); - - QString version = m_MainWindow.versionChecker().getVersion(); - QString userAgent = "Synergy GUI " + version; - QByteArray userAgentData(userAgent.toStdString().c_str()); - - QNetworkRequest request(QUrl(PREMIUM_AUTH_URL)); - request.setRawHeader("User-Agent", userAgentData); - - QUrl params; - params.addEncodedQueryItem("json", requestData); - QNetworkReply* reply = m_Network.post(request, params.encodedQuery()); - - // use loop instead of waitForReadyRead (which doesnt seem to work). - QEventLoop loop; - connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - - if (reply->error() != QNetworkReply::NoError) { - message.setText(tr("Login failed, an error occurred.\n\nError: %1").arg(reply->errorString())); - message.exec(); - return false; - } - - QByteArray responseData = reply->readAll(); - QString responseJson(responseData); - - // this feels like a lot of work, but its cheaper than getting a json - // parsing library involved. - QRegExp regex(".*\"result\":\\s*([^,}\\s]+).*"); - if (regex.exactMatch(responseJson)) { - QString boolString = regex.cap(1); - if (boolString == "true") { - return true; - } - else if (boolString == "false") { - message.setText(tr("Login failed, invalid email or password.")); - message.exec(); - return false; - } - } - - message.setText(tr("Login failed, an error occurred.\n\nServer response:\n\n%1").arg(responseJson.trimmed())); - message.exec(); - return false; -} +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012 Bolton Software Ltd. + * + * 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 "SetupWizard.h" +#include "MainWindow.h" +#include "QSynergyApplication.h" +#include "QUtility.h" + +#include +#include +#include +#include +#include + +//#define PREMIUM_AUTH_URL "http://localhost/synergy/premium/json/auth/" +#define PREMIUM_AUTH_URL "https://synergy-foss.org/premium/json/auth/" +#define PREMIUM_REGISTER_URL "https://synergy-foss.org/premium/register/?source=gui-wizard" + +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 + + 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))); + + m_Locale.fillLanguageComboBox(m_pComboLanguage); + setIndexFromItemData(m_pComboLanguage, m_MainWindow.appConfig().language()); + AppConfig& appConfig = m_MainWindow.appConfig(); + QString premiumEmail = appConfig.premiumEmail(); + if (!premiumEmail.isEmpty()) + { + m_pRadioButtonPremiumLogin->setChecked(true); + m_pLineEditPremiumEmail->setText(premiumEmail); + } +} + +SetupWizard::~SetupWizard() +{ +} + +bool SetupWizard::validateCurrentPage() +{ + QMessageBox message; + message.setWindowTitle(tr("Setup Synergy")); + message.setIcon(QMessageBox::Information); + + if (currentPage() == m_pNodePage) + { + bool result = m_pClientRadioButton->isChecked() || + m_pServerRadioButton->isChecked(); + + if (!result) + { + message.setText(tr("Please select an option.")); + message.exec(); + return false; + } + } + else if (currentPage() == m_pPremiumUserPage) + { + if (m_pRadioButtonPremiumLogin->isChecked()) + { + if (m_pLineEditPremiumEmail->text().isEmpty() || + m_pLineEditPremiumPassword->text().isEmpty()) + { + message.setText(tr("Please enter your email address and password.")); + message.exec(); + return false; + } + else if (!isPremiumLoginValid(message)) + { + return false; + } + else + { + m_pComboCryptoMode->setCurrentIndex(0); + m_pComboCryptoMode->setEnabled(true); + } + } + else if (m_pRadioButtonPremiumRegister->isChecked()) + { + const QUrl url(QString(PREMIUM_REGISTER_URL)); + QDesktopServices::openUrl(url); + m_pRadioButtonPremiumLogin->setChecked(true); + return false; + } + else if (m_pRadioButtonPremiumLater->isChecked()) + { + int size = m_pComboCryptoMode->count(); + m_pComboCryptoMode->setCurrentIndex(size - 1); + m_pComboCryptoMode->setEnabled(false); + } + else { + message.setText(tr("Please select an option.")); + message.exec(); + return false; + } + } + else if (currentPage() == m_pCryptoPage) + { + QString modeText = m_pComboCryptoMode->currentText(); + if (modeText.isEmpty()) + { + message.setText(tr("Encryption mode required.")); + message.exec(); + return false; + } + + if (parseCryptoMode(modeText) != Disabled) + { + if (m_pLineEditCryptoPassword1->text().isEmpty()) + { + message.setText(tr("Encryption password required.")); + message.exec(); + return false; + } + + if (m_pLineEditCryptoPassword1->text() != m_pLineEditCryptoPassword2->text()) + { + message.setText(tr("Encryption password and confirmation do not match.")); + message.exec(); + return false; + } + } + } + + return true; +} + +void SetupWizard::changeEvent(QEvent* event) +{ + if (event != 0) + { + switch (event->type()) + { + case QEvent::LanguageChange: + { + m_pComboLanguage->blockSignals(true); + retranslateUi(this); + m_pComboLanguage->blockSignals(false); + break; + } + + default: + QWizard::changeEvent(event); + } + } +} + +void SetupWizard::accept() +{ + AppConfig& appConfig = m_MainWindow.appConfig(); + + appConfig.setCryptoMode(parseCryptoMode(m_pComboCryptoMode->currentText())); + appConfig.setCryptoPass(m_pLineEditCryptoPassword1->text()); + appConfig.setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()); + appConfig.setPremiumEmail(m_pLineEditPremiumEmail->text()); + + if (!m_pRadioButtonPremiumLogin->isChecked()) + { + appConfig.setPremiumToken(""); + } + else + { + QString mac = getFirstMacAddress(); + QString hashSrc = m_pLineEditPremiumEmail->text() + mac; + QString hashResult = hash(hashSrc); + appConfig.setPremiumToken(hashResult); + } + + appConfig.setWizardHasRun(); + 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); + } + + QWizard::accept(); +} + +void SetupWizard::reject() +{ + QSynergyApplication::getInstance()->switchTranslator(m_MainWindow.appConfig().language()); + + if (m_StartMain) + { + m_MainWindow.start(true); + } + + QWizard::reject(); +} + +void SetupWizard::on_m_pComboCryptoMode_currentIndexChanged(int index) +{ + bool enabled = parseCryptoMode(m_pComboCryptoMode->currentText()) != Disabled; + m_pLineEditCryptoPassword1->setEnabled(enabled); + m_pLineEditCryptoPassword2->setEnabled(enabled); +} + +CryptoMode SetupWizard::parseCryptoMode(const QString& s) +{ + if (s.startsWith("OFB")) + { + return OFB; + } + else if (s.startsWith("CFB")) + { + return CFB; + } + else if (s.startsWith("CTR")) + { + return CTR; + } + else if (s.startsWith("GCM")) + { + return GCM; + } + + return Disabled; +} + +void SetupWizard::on_m_pComboLanguage_currentIndexChanged(int index) +{ + QString ietfCode = m_pComboLanguage->itemData(index).toString(); + QSynergyApplication::getInstance()->switchTranslator(ietfCode); +} + +void SetupWizard::on_m_pRadioButtonPremiumLogin_toggled(bool checked) +{ + m_pLineEditPremiumEmail->setEnabled(checked); + m_pLineEditPremiumPassword->setEnabled(checked); +} + +bool SetupWizard::isPremiumLoginValid(QMessageBox& message) +{ + QString email = m_pLineEditPremiumEmail->text(); + QString password = m_pLineEditPremiumPassword->text(); + + QString requestJson = "{\"email\":\"" + email + "\",\"password\":\"" + password + "\"}"; + QByteArray requestData(requestJson.toStdString().c_str()); + + QString version = m_MainWindow.versionChecker().getVersion(); + QString userAgent = "Synergy GUI " + version; + QByteArray userAgentData(userAgent.toStdString().c_str()); + + QNetworkRequest request(QUrl(PREMIUM_AUTH_URL)); + request.setRawHeader("User-Agent", userAgentData); + + QUrl params; + params.addEncodedQueryItem("json", requestData); + QNetworkReply* reply = m_Network.post(request, params.encodedQuery()); + + // use loop instead of waitForReadyRead (which doesnt seem to work). + QEventLoop loop; + connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + + if (reply->error() != QNetworkReply::NoError) { + message.setText(tr("Login failed, an error occurred.\n\nError: %1").arg(reply->errorString())); + message.exec(); + return false; + } + + QByteArray responseData = reply->readAll(); + QString responseJson(responseData); + + // this feels like a lot of work, but its cheaper than getting a json + // parsing library involved. + QRegExp regex(".*\"result\":\\s*([^,}\\s]+).*"); + if (regex.exactMatch(responseJson)) { + QString boolString = regex.cap(1); + if (boolString == "true") { + return true; + } + else if (boolString == "false") { + message.setText(tr("Login failed, invalid email or password.")); + message.exec(); + return false; + } + } + + message.setText(tr("Login failed, an error occurred.\n\nServer response:\n\n%1").arg(responseJson.trimmed())); + message.exec(); + return false; +} diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 4c217cda..2d13484e 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -1,57 +1,57 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Bolton Software Ltd. - * - * 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 "ui_SetupWizardBase.h" -#include "CryptoMode.h" -#include "SynergyLocale.h" - -#include -#include - -class MainWindow; -class QMessageBox; - -class SetupWizard : public QWizard, public Ui::SetupWizardBase -{ - Q_OBJECT -public: - SetupWizard(MainWindow& mainWindow, bool startMain); - virtual ~SetupWizard(); - bool validateCurrentPage(); - -protected: - void changeEvent(QEvent* event); - void accept(); - void reject(); - -private: - CryptoMode parseCryptoMode(const QString& s); - bool isPremiumLoginValid(QMessageBox& message); - -private: - MainWindow& m_MainWindow; - bool m_StartMain; - SynergyLocale m_Locale; - QNetworkAccessManager m_Network; - -private slots: - void on_m_pComboCryptoMode_currentIndexChanged(int index); - void on_m_pComboLanguage_currentIndexChanged(int index); - void on_m_pRadioButtonPremiumLogin_toggled(bool checked); -}; +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012 Bolton Software Ltd. + * + * 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 "ui_SetupWizardBase.h" +#include "CryptoMode.h" +#include "SynergyLocale.h" + +#include +#include + +class MainWindow; +class QMessageBox; + +class SetupWizard : public QWizard, public Ui::SetupWizardBase +{ + Q_OBJECT +public: + SetupWizard(MainWindow& mainWindow, bool startMain); + virtual ~SetupWizard(); + bool validateCurrentPage(); + +protected: + void changeEvent(QEvent* event); + void accept(); + void reject(); + +private: + CryptoMode parseCryptoMode(const QString& s); + bool isPremiumLoginValid(QMessageBox& message); + +private: + MainWindow& m_MainWindow; + bool m_StartMain; + SynergyLocale m_Locale; + QNetworkAccessManager m_Network; + +private slots: + void on_m_pComboCryptoMode_currentIndexChanged(int index); + void on_m_pComboLanguage_currentIndexChanged(int index); + void on_m_pRadioButtonPremiumLogin_toggled(bool checked); +}; diff --git a/src/gui/src/SynergyLocale.cpp b/src/gui/src/SynergyLocale.cpp index 6c6598bb..db4e941a 100644 --- a/src/gui/src/SynergyLocale.cpp +++ b/src/gui/src/SynergyLocale.cpp @@ -1,68 +1,68 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Bolton Software Ltd. - * - * 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 "SynergyLocale.h" - -#include -#include -#include - -SynergyLocale::SynergyLocale() -{ - loadLanguages(); -} - -void SynergyLocale::loadLanguages() -{ - QResource resource(":/res/lang/Languages.xml"); - QByteArray bytes(reinterpret_cast(resource.data()), resource.size()); - QXmlStreamReader xml(bytes); - - while (!xml.atEnd()) - { - QXmlStreamReader::TokenType token = xml.readNext(); - if (xml.hasError()) - { - qCritical() << xml.errorString(); - throw std::exception(); - } - - if (xml.name() == "language" && token == QXmlStreamReader::StartElement) - { - QXmlStreamAttributes attributes = xml.attributes(); - addLanguage( - attributes.value("ietfCode").toString(), - attributes.value("name").toString()); - } - } -} - -void SynergyLocale::addLanguage(const QString& ietfCode, const QString& name) -{ - m_Languages.push_back(SynergyLocale::Language(ietfCode, name)); -} - -void SynergyLocale::fillLanguageComboBox(QComboBox* comboBox) -{ - comboBox->blockSignals(true); - QVector::iterator it; - for (it = m_Languages.begin(); it != m_Languages.end(); ++it) - { - comboBox->addItem((*it).m_Name, (*it).m_IetfCode); - } - comboBox->blockSignals(false); -} +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * 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 "SynergyLocale.h" + +#include +#include +#include + +SynergyLocale::SynergyLocale() +{ + loadLanguages(); +} + +void SynergyLocale::loadLanguages() +{ + QResource resource(":/res/lang/Languages.xml"); + QByteArray bytes(reinterpret_cast(resource.data()), resource.size()); + QXmlStreamReader xml(bytes); + + while (!xml.atEnd()) + { + QXmlStreamReader::TokenType token = xml.readNext(); + if (xml.hasError()) + { + qCritical() << xml.errorString(); + throw std::exception(); + } + + if (xml.name() == "language" && token == QXmlStreamReader::StartElement) + { + QXmlStreamAttributes attributes = xml.attributes(); + addLanguage( + attributes.value("ietfCode").toString(), + attributes.value("name").toString()); + } + } +} + +void SynergyLocale::addLanguage(const QString& ietfCode, const QString& name) +{ + m_Languages.push_back(SynergyLocale::Language(ietfCode, name)); +} + +void SynergyLocale::fillLanguageComboBox(QComboBox* comboBox) +{ + comboBox->blockSignals(true); + QVector::iterator it; + for (it = m_Languages.begin(); it != m_Languages.end(); ++it) + { + comboBox->addItem((*it).m_Name, (*it).m_IetfCode); + } + comboBox->blockSignals(false); +} diff --git a/src/gui/src/SynergyLocale.h b/src/gui/src/SynergyLocale.h index ccbbe817..ae816305 100644 --- a/src/gui/src/SynergyLocale.h +++ b/src/gui/src/SynergyLocale.h @@ -1,48 +1,48 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Bolton Software Ltd. - * - * 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 -#include - -class SynergyLocale -{ - class Language - { - public: - Language() { } - Language(const QString& IetfCode, const QString& name) - : m_IetfCode(IetfCode), m_Name(name) { } - - public: - QString m_IetfCode; - QString m_Name; - }; - -public: - SynergyLocale(); - void fillLanguageComboBox(QComboBox* comboBox); - -private: - void loadLanguages(); - void addLanguage(const QString& IetfCode, const QString& name); - -private: - QVector m_Languages; -}; +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * 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 +#include + +class SynergyLocale +{ + class Language + { + public: + Language() { } + Language(const QString& IetfCode, const QString& name) + : m_IetfCode(IetfCode), m_Name(name) { } + + public: + QString m_IetfCode; + QString m_Name; + }; + +public: + SynergyLocale(); + void fillLanguageComboBox(QComboBox* comboBox); + +private: + void loadLanguages(); + void addLanguage(const QString& IetfCode, const QString& name); + +private: + QVector m_Languages; +}; diff --git a/src/gui/src/TcpSocketReader.cpp b/src/gui/src/TcpSocketReader.cpp index f5d7750c..ea381085 100644 --- a/src/gui/src/TcpSocketReader.cpp +++ b/src/gui/src/TcpSocketReader.cpp @@ -1,30 +1,30 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 "TcpSocketReader.h" -#include - -IpcReader::IpcReader(QTcpSocket& socket) : -m_Socket(socket) -{ - connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read())); -} - -IpcReader::~IpcReader() -{ -} + * 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 "TcpSocketReader.h" +#include + +IpcReader::IpcReader(QTcpSocket& socket) : +m_Socket(socket) +{ + connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read())); +} + +IpcReader::~IpcReader() +{ +} diff --git a/src/gui/src/VersionChecker.cpp b/src/gui/src/VersionChecker.cpp index cd811d3f..b784adf2 100644 --- a/src/gui/src/VersionChecker.cpp +++ b/src/gui/src/VersionChecker.cpp @@ -1,103 +1,103 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Bolton Software Ltd. - * 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-foss.org/version/" - -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"); -} +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012 Bolton Software Ltd. + * 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-foss.org/version/" + +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 index a35eb738..941ee760 100644 --- a/src/gui/src/VersionChecker.h +++ b/src/gui/src/VersionChecker.h @@ -1,44 +1,44 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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; -}; + * 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/lib/arch/CArchNetworkBSD.cpp b/src/lib/arch/CArchNetworkBSD.cpp index a9d1c978..f0c74d68 100644 --- a/src/lib/arch/CArchNetworkBSD.cpp +++ b/src/lib/arch/CArchNetworkBSD.cpp @@ -83,26 +83,26 @@ inet_aton(const char* cp, struct in_addr* inp) // // CArchNetworkBSD // - -CArchNetworkBSD::CArchNetworkBSD() -{ -} - -CArchNetworkBSD::~CArchNetworkBSD() + +CArchNetworkBSD::CArchNetworkBSD() +{ +} + +CArchNetworkBSD::~CArchNetworkBSD() +{ + ARCH->closeMutex(m_mutex); +} + +void +CArchNetworkBSD::init() +{ + // create mutex to make some calls thread safe + m_mutex = ARCH->newMutex(); +} + +CArchSocket +CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) { - ARCH->closeMutex(m_mutex); -} - -void -CArchNetworkBSD::init() -{ - // create mutex to make some calls thread safe - m_mutex = ARCH->newMutex(); -} - -CArchSocket -CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) -{ // create socket int fd = socket(s_family[family], s_type[type], 0); if (fd == -1) { diff --git a/src/lib/arch/CArchNetworkBSD.h b/src/lib/arch/CArchNetworkBSD.h index 9ae4a25c..f524915f 100644 --- a/src/lib/arch/CArchNetworkBSD.h +++ b/src/lib/arch/CArchNetworkBSD.h @@ -57,13 +57,13 @@ public: //! Berkeley (BSD) sockets implementation of IArchNetwork class CArchNetworkBSD : public IArchNetwork { public: - CArchNetworkBSD(); - virtual ~CArchNetworkBSD(); - - virtual void init(); - - // IArchNetwork overrides - virtual CArchSocket newSocket(EAddressFamily, ESocketType); + CArchNetworkBSD(); + virtual ~CArchNetworkBSD(); + + virtual void init(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); virtual CArchSocket copySocket(CArchSocket s); virtual void closeSocket(CArchSocket s); virtual void closeSocketForRead(CArchSocket s); virtual void closeSocketForWrite(CArchSocket s); diff --git a/src/lib/base/CEventQueue.cpp b/src/lib/base/CEventQueue.cpp index 0de4575d..0502ec27 100644 --- a/src/lib/base/CEventQueue.cpp +++ b/src/lib/base/CEventQueue.cpp @@ -60,7 +60,7 @@ interrupt(CArch::ESignal, void* data) CEventQueue::CEventQueue() : m_systemTarget(0), - m_nextType(CEvent::kLast), + m_nextType(CEvent::kLast), m_typesForCClient(NULL), m_typesForIStream(NULL), m_typesForCIpcClient(NULL), diff --git a/src/lib/base/CLog.cpp b/src/lib/base/CLog.cpp index d58587c3..e2974ea5 100644 --- a/src/lib/base/CLog.cpp +++ b/src/lib/base/CLog.cpp @@ -178,7 +178,7 @@ CLog::print(const char* file, int line, const char* fmt, ...) // do not prefix time and file for kPRINT (CLOG_PRINT) if (priority != kPRINT) { - char message[2048]; + char message[kLogMessageLength]; #ifndef NDEBUG struct tm *tm; diff --git a/src/lib/base/CLog.h b/src/lib/base/CLog.h index 6f5adcdd..1eb8c82b 100644 --- a/src/lib/base/CLog.h +++ b/src/lib/base/CLog.h @@ -137,6 +137,8 @@ private: int m_maxPriority; }; +const UInt16 kLogMessageLength = 2048; + /*! \def LOG(arg) Write to the log. Because macros cannot accept variable arguments, this diff --git a/src/lib/client/CClient.cpp b/src/lib/client/CClient.cpp index fdf34721..72f31fb5 100644 --- a/src/lib/client/CClient.cpp +++ b/src/lib/client/CClient.cpp @@ -33,14 +33,20 @@ #include "CArch.h" #include "IPlatformScreen.h" #include "CCryptoStream.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CFileChunker.h" #include #include #include +#include // // CClient // +const size_t CClient::m_chunkSize = 1024 * 512; // 512kb + CClient::CClient(IEventQueue* events, const CString& name, const CNetworkAddress& address, ISocketFactory* socketFactory, @@ -438,6 +444,17 @@ CClient::sendConnectionFailedEvent(const char* msg) m_events->addEvent(event); } +void +CClient::sendFileChunk(const void* data) +{ + CFileChunker::CFileChunk* fileChunk = reinterpret_cast(const_cast(data)); + LOG((CLOG_DEBUG1 "sendFileChunk")); + assert(m_server != NULL); + + // relay + m_server->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize); +} + void CClient::setupConnecting() { @@ -737,6 +754,7 @@ CClient::handleGameDeviceFeedback(const CEvent& event, void*) void CClient::handleFileChunkSending(const CEvent& event, void*) { + sendFileChunk(event.getData()); } void @@ -763,3 +781,24 @@ CClient::isReceivedFileSizeValid() { return m_expectedFileSize == m_receivedFileData.size(); } + +void +CClient::sendFileToServer(const char* filename) +{ + CThread* thread = new CThread( + new TMethodJob( + this, &CClient::sendFileThread, + reinterpret_cast(const_cast(filename)))); +} + +void +CClient::sendFileThread(void* filename) +{ + try { + char* name = reinterpret_cast(filename); + CFileChunker::sendFileChunks(name, m_events, this); + } + catch (std::runtime_error error) { + LOG((CLOG_ERR "failed sending file chunks: %s", error.what())); + } +} diff --git a/src/lib/client/CClient.h b/src/lib/client/CClient.h index c4db7e9c..030e64b7 100644 --- a/src/lib/client/CClient.h +++ b/src/lib/client/CClient.h @@ -100,9 +100,9 @@ public: //! Received a chunk of file data void fileChunkReceived(CString data); - - //! Return true if recieved file size is valid - bool isReceivedFileSizeValid(); + + //! Create a new thread and use it to send file to Server + void sendFileToServer(const char* filename); //@} //! @name accessors @@ -128,6 +128,9 @@ public: */ CNetworkAddress getServerAddress() const; + //! Return true if recieved file size is valid + bool isReceivedFileSizeValid(); + //@} // IScreen overrides @@ -167,6 +170,8 @@ private: void sendClipboard(ClipboardID); void sendEvent(CEvent::Type, void*); void sendConnectionFailedEvent(const char* msg); + void sendFileChunk(const void* data); + void sendFileThread(void*); void setupConnecting(); void setupConnection(); void setupScreen(); @@ -188,7 +193,7 @@ private: void handleGameDeviceTimingResp(const CEvent& event, void*); void handleGameDeviceFeedback(const CEvent& event, void*); void handleFileChunkSending(const CEvent&, void*); - + public: bool m_mock; @@ -214,6 +219,7 @@ private: CCryptoOptions m_crypto; std::size_t m_expectedFileSize; CString m_receivedFileData; + static const size_t m_chunkSize; }; #endif diff --git a/src/lib/client/CServerProxy.cpp b/src/lib/client/CServerProxy.cpp index dff281ce..c8475cb6 100644 --- a/src/lib/client/CServerProxy.cpp +++ b/src/lib/client/CServerProxy.cpp @@ -935,7 +935,8 @@ CServerProxy::infoAcknowledgment() m_ignoreMouse = false; } -void CServerProxy::fileChunkReceived() +void +CServerProxy::fileChunkReceived() { // parse UInt8 mark; @@ -943,20 +944,42 @@ void CServerProxy::fileChunkReceived() CProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content); switch (mark) { - case '0': - LOG((CLOG_DEBUG2 "recv file data: file size = %s", content)); + case kFileStart: + LOG((CLOG_DEBUG2 "recv file data from server: size=%s", content.c_str())); m_client->clearReceivedFileData(); m_client->setExpectedFileSize(content); break; - case '1': - LOG((CLOG_DEBUG2 "recv file data: chunck size = %i", content.size())); + case kFileChunk: + LOG((CLOG_DEBUG2 "recv file data from server: size=%i", content.size())); m_client->fileChunkReceived(content); break; - case '2': + case kFileEnd: LOG((CLOG_DEBUG2 "file data transfer finished")); m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), m_client)); break; } } + +void +CServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize) +{ + CString chunk(data, dataSize); + + switch (mark) { + case kFileStart: + LOG((CLOG_DEBUG2 "file sending start: size=%s", data)); + break; + + case kFileChunk: + LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size())); + break; + + case kFileEnd: + LOG((CLOG_DEBUG2 "file sending finished")); + break; + } + + CProtocolUtil::writef(m_stream, kMsgDFileTransfer, mark, &chunk); +} diff --git a/src/lib/client/CServerProxy.h b/src/lib/client/CServerProxy.h index a1afa4e4..74255fa8 100644 --- a/src/lib/client/CServerProxy.h +++ b/src/lib/client/CServerProxy.h @@ -56,6 +56,14 @@ public: //@} + //! @file transfer + //@{ + + //! sending file chunk to server + void fileChunkSending(UInt8 mark, char* data, size_t dataSize); + + //@} + #ifdef TEST_ENV void handleDataForTest() { handleData(CEvent(), NULL); } #endif diff --git a/src/lib/platform/CMSWindowsDebugOutputter.h b/src/lib/platform/CMSWindowsDebugOutputter.h index aaf08117..f89c470e 100644 --- a/src/lib/platform/CMSWindowsDebugOutputter.h +++ b/src/lib/platform/CMSWindowsDebugOutputter.h @@ -1,39 +1,39 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 "ILogOutputter.h" - -//! Write log to debugger -/*! -This outputter writes output to the debugger. In Visual Studio, this -can be seen in the Output window. -*/ -class CMSWindowsDebugOutputter : public ILogOutputter { -public: - CMSWindowsDebugOutputter(); - virtual ~CMSWindowsDebugOutputter(); - - // ILogOutputter overrides - virtual void open(const char* title); - virtual void close(); - virtual void show(bool showIfEmpty); - virtual bool write(ELevel level, const char* message); - virtual void flush(); -}; + * 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 "ILogOutputter.h" + +//! Write log to debugger +/*! +This outputter writes output to the debugger. In Visual Studio, this +can be seen in the Output window. +*/ +class CMSWindowsDebugOutputter : public ILogOutputter { +public: + CMSWindowsDebugOutputter(); + virtual ~CMSWindowsDebugOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual void show(bool showIfEmpty); + virtual bool write(ELevel level, const char* message); + virtual void flush(); +}; diff --git a/src/lib/platform/CMSWindowsRelauncher.cpp b/src/lib/platform/CMSWindowsRelauncher.cpp index 84fc181d..d7b563b0 100644 --- a/src/lib/platform/CMSWindowsRelauncher.cpp +++ b/src/lib/platform/CMSWindowsRelauncher.cpp @@ -203,7 +203,7 @@ CMSWindowsRelauncher::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTE LOG((CLOG_ERR "could not open token, process handle: %d (error: %i)", process, GetLastError())); return NULL; } - + LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken)); HANDLE newToken; @@ -216,7 +216,7 @@ CMSWindowsRelauncher::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTE sourceToken, GetLastError())); return NULL; } - + LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); return newToken; } @@ -265,7 +265,7 @@ CMSWindowsRelauncher::getSessionToken(DWORD sessionId, LPSECURITY_ATTRIBUTES sec LOG((CLOG_ERR "could not duplicate token (error: %i)", GetLastError())); return 0; } - + LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); return newToken; } @@ -284,10 +284,10 @@ CMSWindowsRelauncher::mainLoop(void*) DWORD sessionId = -1; bool launched = false; - - SECURITY_ATTRIBUTES saAttr; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; + + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) { @@ -308,27 +308,27 @@ CMSWindowsRelauncher::mainLoop(void*) sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS"); } - DWORD newSessionId = getSessionId(); - - bool running = false; - if (launched) { - - DWORD exitCode; - GetExitCodeProcess(pi.hProcess, &exitCode); - running = (exitCode == STILL_ACTIVE); - - if (!running) { - failures++; - LOG((CLOG_INFO "detected application not running, pid=%d, failures=%d", pi.dwProcessId, failures)); - - // increasing backoff period, maximum of 10 seconds. - int timeout = (failures * 2) < 10 ? (failures * 2) : 10; - LOG((CLOG_DEBUG "waiting, backoff period is %d seconds", timeout)); - ARCH->sleep(timeout); - - // double check, in case process started after we waited. - GetExitCodeProcess(pi.hProcess, &exitCode); - running = (exitCode == STILL_ACTIVE); + DWORD newSessionId = getSessionId(); + + bool running = false; + if (launched) { + + DWORD exitCode; + GetExitCodeProcess(pi.hProcess, &exitCode); + running = (exitCode == STILL_ACTIVE); + + if (!running) { + failures++; + LOG((CLOG_INFO "detected application not running, pid=%d, failures=%d", pi.dwProcessId, failures)); + + // increasing backoff period, maximum of 10 seconds. + int timeout = (failures * 2) < 10 ? (failures * 2) : 10; + LOG((CLOG_DEBUG "waiting, backoff period is %d seconds", timeout)); + ARCH->sleep(timeout); + + // double check, in case process started after we waited. + GetExitCodeProcess(pi.hProcess, &exitCode); + running = (exitCode == STILL_ACTIVE); } else { // reset failures when running. @@ -383,7 +383,7 @@ CMSWindowsRelauncher::mainLoop(void*) si.cb = sizeof(STARTUPINFO); si.lpDesktop = "winsta0\\Default"; // TODO: maybe this should be \winlogon if we have logonui.exe? si.hStdError = m_stdOutWrite; - si.hStdOutput = m_stdOutWrite; + si.hStdOutput = m_stdOutWrite; si.dwFlags |= STARTF_USESTDHANDLES; LPVOID environment; @@ -502,45 +502,45 @@ CMSWindowsRelauncher::outputLoop(void*) } } -} - -void -CMSWindowsRelauncher::shutdownProcess(HANDLE handle, DWORD pid, int timeout) -{ - DWORD exitCode; - GetExitCodeProcess(handle, &exitCode); - if (exitCode != STILL_ACTIVE) - return; - - CIpcShutdownMessage shutdown; - m_ipcServer.send(shutdown, kIpcClientNode); - - // wait for process to exit gracefully. - double start = ARCH->time(); - while (true) - { - GetExitCodeProcess(handle, &exitCode); - if (exitCode != STILL_ACTIVE) { - // yay, we got a graceful shutdown. there should be no hook in use errors! - LOG((CLOG_INFO "process %d was shutdown gracefully", pid)); - break; - } - else { - - double elapsed = (ARCH->time() - start); - if (elapsed > timeout) { - // if timeout reached, kill forcefully. - // calling TerminateProcess on synergy is very bad! - // it causes the hook DLL to stay loaded in some apps, - // making it impossible to start synergy again. - LOG((CLOG_WARN "shutdown timed out after %d secs, forcefully terminating", (int)elapsed)); - TerminateProcess(handle, kExitSuccess); - break; - } - - ARCH->sleep(1); - } - } +} + +void +CMSWindowsRelauncher::shutdownProcess(HANDLE handle, DWORD pid, int timeout) +{ + DWORD exitCode; + GetExitCodeProcess(handle, &exitCode); + if (exitCode != STILL_ACTIVE) + return; + + CIpcShutdownMessage shutdown; + m_ipcServer.send(shutdown, kIpcClientNode); + + // wait for process to exit gracefully. + double start = ARCH->time(); + while (true) + { + GetExitCodeProcess(handle, &exitCode); + if (exitCode != STILL_ACTIVE) { + // yay, we got a graceful shutdown. there should be no hook in use errors! + LOG((CLOG_INFO "process %d was shutdown gracefully", pid)); + break; + } + else { + + double elapsed = (ARCH->time() - start); + if (elapsed > timeout) { + // if timeout reached, kill forcefully. + // calling TerminateProcess on synergy is very bad! + // it causes the hook DLL to stay loaded in some apps, + // making it impossible to start synergy again. + LOG((CLOG_WARN "shutdown timed out after %d secs, forcefully terminating", (int)elapsed)); + TerminateProcess(handle, kExitSuccess); + break; + } + + ARCH->sleep(1); + } + } } void @@ -597,4 +597,4 @@ CMSWindowsRelauncher::shutdownExistingProcesses() } CloseHandle(snapshot); -} +} diff --git a/src/lib/platform/CXWindowsUtil.cpp b/src/lib/platform/CXWindowsUtil.cpp index 3b8244bf..ca0178f4 100644 --- a/src/lib/platform/CXWindowsUtil.cpp +++ b/src/lib/platform/CXWindowsUtil.cpp @@ -1477,16 +1477,16 @@ CXWindowsUtil::mapKeySymToKeyID(KeySym k) case XK_ISO_Left_Tab: return kKeyLeftTab; - case XK_ISO_Level3_Shift: - return kKeyAltGr; - -#ifdef XK_ISO_Level5_Shift - case XK_ISO_Level5_Shift: - return XK_ISO_Level5_Shift; //FIXME: there is no "usual" key for this... -#endif - - case XK_ISO_Next_Group: - return kKeyNextGroup; + case XK_ISO_Level3_Shift: + return kKeyAltGr; + +#ifdef XK_ISO_Level5_Shift + case XK_ISO_Level5_Shift: + return XK_ISO_Level5_Shift; //FIXME: there is no "usual" key for this... +#endif + + case XK_ISO_Next_Group: + return kKeyNextGroup; case XK_ISO_Prev_Group: return kKeyPrevGroup; @@ -1582,16 +1582,16 @@ CXWindowsUtil::getModifierBitForKeySym(KeySym keysym) return kKeyModifierBitSuper; case XK_Mode_switch: - case XK_ISO_Level3_Shift: - return kKeyModifierBitAltGr; - -#ifdef XK_ISO_Level5_Shift - case XK_ISO_Level5_Shift: - return kKeyModifierBitLevel5Lock; -#endif - - case XK_Caps_Lock: - return kKeyModifierBitCapsLock; + case XK_ISO_Level3_Shift: + return kKeyModifierBitAltGr; + +#ifdef XK_ISO_Level5_Shift + case XK_ISO_Level5_Shift: + return kKeyModifierBitLevel5Lock; +#endif + + case XK_Caps_Lock: + return kKeyModifierBitCapsLock; case XK_Num_Lock: return kKeyModifierBitNumLock; diff --git a/src/lib/platform/HookDLL.cpp b/src/lib/platform/HookDLL.cpp index 3818cfdc..c151133e 100644 --- a/src/lib/platform/HookDLL.cpp +++ b/src/lib/platform/HookDLL.cpp @@ -14,329 +14,329 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - */ - -/*-------------------------------------------------------------------------------------------------------- - Original comment: - - APIHIJACK.CPP - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000. - http://msdn.microsoft.com/library/periodic/period00/hood0200.htm - Adapted by Wade Brainerd, wadeb@wadeb.com ---------------------------------------------------------------------------------------------------------*/ - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include "HookDLL.h" - -#include -std::stringstream _hookDllLogStream; -#define LOG(s) \ - _hookDllLogStream.str(""); \ - _hookDllLogStream << "Synergy HookDLL: " << s << std::endl; \ - OutputDebugString( _hookDllLogStream.str().c_str() ); - -//=========================================================================== -// Called from the DLPD_IAT_STUB stubs. Increments "count" field of the stub - -void __cdecl DefaultHook( PVOID dummy ) -{ - // asm only supported on 32-bit -#ifdef _M_IX86 - __asm pushad // Save all general purpose registers - - // Get return address, then subtract 5 (size of a CALL X instruction) - // The result points at a DLPD_IAT_STUB - - // pointer math! &dummy-1 really subtracts sizeof(PVOID) - PDWORD pRetAddr = (PDWORD)(&dummy - 1); - - DLPD_IAT_STUB * pDLPDStub = (DLPD_IAT_STUB *)(*pRetAddr - 5); - - pDLPDStub->count++; - - #if 0 - // Remove the above conditional to get a cheezy API trace from - // the loader process. It's slow! - if ( !IMAGE_SNAP_BY_ORDINAL( pDLPDStub->pszNameOrOrdinal) ) - { - LOG( "Called hooked function: " ); - LOG( (PSTR)pDLPDStub->pszNameOrOrdinal ); - } - #endif - - __asm popad // Restore all general purpose registers -#endif -} - -// This function must be __cdecl!!! -void __cdecl DelayLoadProfileDLL_UpdateCount( PVOID dummy ); - -PIMAGE_IMPORT_DESCRIPTOR g_pFirstImportDesc; - -//=========================================================================== -// Given an HMODULE, returns a pointer to the PE header - -PIMAGE_NT_HEADERS PEHeaderFromHModule(HMODULE hModule) -{ - PIMAGE_NT_HEADERS pNTHeader = 0; - - __try - { - if ( PIMAGE_DOS_HEADER(hModule)->e_magic != IMAGE_DOS_SIGNATURE ) - __leave; - - pNTHeader = PIMAGE_NT_HEADERS(PBYTE(hModule) - + PIMAGE_DOS_HEADER(hModule)->e_lfanew); - - if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE ) - pNTHeader = 0; - } - __except( EXCEPTION_EXECUTE_HANDLER ) - { - } - - return pNTHeader; -} - -//=========================================================================== -// Builds stubs for and redirects the IAT for one DLL (pImportDesc) - -bool RedirectIAT( SDLLHook* DLLHook, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, PVOID pBaseLoadAddr ) -{ - PIMAGE_THUNK_DATA pIAT; // Ptr to import address table - PIMAGE_THUNK_DATA pINT; // Ptr to import names table - PIMAGE_THUNK_DATA pIteratingIAT; - - // Figure out which OS platform we're on - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof(osvi); - GetVersionEx( &osvi ); - - // If no import names table, we can't redirect this, so bail - if ( pImportDesc->OriginalFirstThunk == 0 ) - { - LOG( "no IAT available." ); - return false; - } - - pIAT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->FirstThunk ); - pINT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->OriginalFirstThunk ); - - // Count how many entries there are in this IAT. Array is 0 terminated - pIteratingIAT = pIAT; - unsigned cFuncs = 0; - while ( pIteratingIAT->u1.Function ) - { - cFuncs++; - pIteratingIAT++; - } - - if ( cFuncs == 0 ) // If no imported functions, we're done! - { - LOG( "no imported functions" ); - return false; - } - - // These next few lines ensure that we'll be able to modify the IAT, - // which is often in a read-only section in the EXE. - DWORD flOldProtect, flNewProtect, flDontCare; - MEMORY_BASIC_INFORMATION mbi; - - // Get the current protection attributes - VirtualQuery( pIAT, &mbi, sizeof(mbi) ); - - // remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag - flNewProtect = mbi.Protect; - flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); - flNewProtect |= (PAGE_READWRITE); - - if ( !VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, - flNewProtect, &flOldProtect) ) - { - LOG( "could not remove ReadOnly and ExecuteRead attributes, or add ReadWrite flag" ); - return false; - } - - // If the Default hook is enabled, build an array of redirection stubs in the processes memory. - DLPD_IAT_STUB * pStubs = 0; - if ( DLLHook->UseDefault ) - { - // Allocate memory for the redirection stubs. Make one extra stub at the - // end to be a sentinel - pStubs = new DLPD_IAT_STUB[ cFuncs + 1]; - if ( !pStubs ) - { - LOG( "could not allocate memory for redirection stubs" ); - return false; - } - } - - // Scan through the IAT, completing the stubs and redirecting the IAT - // entries to point to the stubs - pIteratingIAT = pIAT; - - while ( pIteratingIAT->u1.Function ) - { - void* HookFn = 0; // Set to either the SFunctionHook or pStubs. - - if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) ) // import by name - { - PIMAGE_IMPORT_BY_NAME pImportName = MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr, pINT->u1.AddressOfData ); - - LOG( "checking function with name: " << pImportName->Name ); - - // Iterate through the hook functions, searching for this import. - SFunctionHook* FHook = DLLHook->Functions; - while ( FHook->Name ) - { - if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 ) - { - // Save the old function in the SFunctionHook structure and get the new one. - FHook->OrigFn = (void*)pIteratingIAT->u1.Function; - HookFn = FHook->HookFn; - - LOG( "hooked function: " << pImportName->Name ); - - break; - } - - FHook++; - } - - // If the default function is enabled, store the name for the user. - if ( DLLHook->UseDefault ) - pStubs->pszNameOrOrdinal = (DWORD)&pImportName->Name; - } - else // added comparison for ordinal - { - LOG( "checking function at ordinal: " << pINT->u1.Ordinal ); - - SFunctionHook* FHook = DLLHook->Functions; - while ( FHook->Name ) - { - if ( (DWORD)FHook->Name == pINT->u1.Ordinal ) - { - // Save the old function in the SFunctionHook structure and get the new one. - FHook->OrigFn = (void*)pIteratingIAT->u1.Function; - HookFn = FHook->HookFn; - - LOG( "hooked ordinal: " << pINT->u1.Ordinal ); - - break; - } - FHook++; - } - // If the default function is enabled, store the ordinal for the user. - if ( DLLHook->UseDefault ) - pStubs->pszNameOrOrdinal = (DWORD)pINT->u1.Ordinal; - } - - // If the default function is enabled, fill in the fields to the stub code. - if ( DLLHook->UseDefault ) - { - pStubs->data_call = (DWORD)(PDWORD)DLLHook->DefaultFn - - (DWORD)(PDWORD)&pStubs->instr_JMP; - pStubs->data_JMP = *(PDWORD)pIteratingIAT - (DWORD)(PDWORD)&pStubs->count; - - // If it wasn't manually hooked, use the Stub function. - if ( !HookFn ) - HookFn = (void*)pStubs; - } - - // Replace the IAT function pointer if we have a hook. - if ( HookFn ) - { - // Cheez-o hack to see if what we're importing is code or data. - // If it's code, we shouldn't be able to write to it - if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) ) - { - pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn; - } - else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) - { - // Special hack for Win9X, which builds stubs for imported - // functions in system DLLs (Loaded above 2GB). These stubs are - // writeable, so we have to explicitly check for this case - if ( pIteratingIAT->u1.Function > (DWORD)(PDWORD)0x80000000 ) - pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn; - } - } - - if ( DLLHook->UseDefault ) - pStubs++; // Advance to next stub - - pIteratingIAT++; // Advance to next IAT entry - pINT++; // Advance to next INT entry - } - - if ( DLLHook->UseDefault ) - pStubs->pszNameOrOrdinal = 0; // Final stub is a sentinel - - // Put the page attributes back the way they were. - VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare); - - return true; -} - -//=========================================================================== -// Top level routine to find the EXE's imports, and redirect them -bool HookAPICalls( SDLLHook* hook ) -{ - if ( !hook ) - { - LOG("no hook"); - return false; - } - - HMODULE hModEXE = GetModuleHandle( 0 ); - - PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule( hModEXE ); - - if ( !pExeNTHdr ) - { - LOG("no PE header"); - return false; - } - - DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory - [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; - if ( !importRVA ) - { - LOG("no virtual address for image directory entry import"); - return false; - } - - // Convert imports RVA to a usable pointer - PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR, - hModEXE, importRVA ); - - // Save off imports address in a global for later use - g_pFirstImportDesc = pImportDesc; - - // Iterate through each import descriptor, and redirect if appropriate - while ( pImportDesc->FirstThunk ) - { - PSTR pszImportModuleName = MakePtr( PSTR, hModEXE, pImportDesc->Name); - - if ( lstrcmpi( pszImportModuleName, hook->Name ) == 0 ) - { - LOG( "found " << hook->Name << " in process" ); - - if ( RedirectIAT( hook, pImportDesc, (PVOID)hModEXE ) ) - { - LOG( "redirected IAT ok" ); - return true; - } - else - { - LOG( "failed to redirect IAT" ); - } - } - - pImportDesc++; // Advance to next import descriptor - } - - return false; -} - + */ + +/*-------------------------------------------------------------------------------------------------------- + Original comment: + + APIHIJACK.CPP - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000. + http://msdn.microsoft.com/library/periodic/period00/hood0200.htm + Adapted by Wade Brainerd, wadeb@wadeb.com +--------------------------------------------------------------------------------------------------------*/ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "HookDLL.h" + +#include +std::stringstream _hookDllLogStream; +#define LOG(s) \ + _hookDllLogStream.str(""); \ + _hookDllLogStream << "Synergy HookDLL: " << s << std::endl; \ + OutputDebugString( _hookDllLogStream.str().c_str() ); + +//=========================================================================== +// Called from the DLPD_IAT_STUB stubs. Increments "count" field of the stub + +void __cdecl DefaultHook( PVOID dummy ) +{ + // asm only supported on 32-bit +#ifdef _M_IX86 + __asm pushad // Save all general purpose registers + + // Get return address, then subtract 5 (size of a CALL X instruction) + // The result points at a DLPD_IAT_STUB + + // pointer math! &dummy-1 really subtracts sizeof(PVOID) + PDWORD pRetAddr = (PDWORD)(&dummy - 1); + + DLPD_IAT_STUB * pDLPDStub = (DLPD_IAT_STUB *)(*pRetAddr - 5); + + pDLPDStub->count++; + + #if 0 + // Remove the above conditional to get a cheezy API trace from + // the loader process. It's slow! + if ( !IMAGE_SNAP_BY_ORDINAL( pDLPDStub->pszNameOrOrdinal) ) + { + LOG( "Called hooked function: " ); + LOG( (PSTR)pDLPDStub->pszNameOrOrdinal ); + } + #endif + + __asm popad // Restore all general purpose registers +#endif +} + +// This function must be __cdecl!!! +void __cdecl DelayLoadProfileDLL_UpdateCount( PVOID dummy ); + +PIMAGE_IMPORT_DESCRIPTOR g_pFirstImportDesc; + +//=========================================================================== +// Given an HMODULE, returns a pointer to the PE header + +PIMAGE_NT_HEADERS PEHeaderFromHModule(HMODULE hModule) +{ + PIMAGE_NT_HEADERS pNTHeader = 0; + + __try + { + if ( PIMAGE_DOS_HEADER(hModule)->e_magic != IMAGE_DOS_SIGNATURE ) + __leave; + + pNTHeader = PIMAGE_NT_HEADERS(PBYTE(hModule) + + PIMAGE_DOS_HEADER(hModule)->e_lfanew); + + if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE ) + pNTHeader = 0; + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + } + + return pNTHeader; +} + +//=========================================================================== +// Builds stubs for and redirects the IAT for one DLL (pImportDesc) + +bool RedirectIAT( SDLLHook* DLLHook, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, PVOID pBaseLoadAddr ) +{ + PIMAGE_THUNK_DATA pIAT; // Ptr to import address table + PIMAGE_THUNK_DATA pINT; // Ptr to import names table + PIMAGE_THUNK_DATA pIteratingIAT; + + // Figure out which OS platform we're on + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof(osvi); + GetVersionEx( &osvi ); + + // If no import names table, we can't redirect this, so bail + if ( pImportDesc->OriginalFirstThunk == 0 ) + { + LOG( "no IAT available." ); + return false; + } + + pIAT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->FirstThunk ); + pINT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->OriginalFirstThunk ); + + // Count how many entries there are in this IAT. Array is 0 terminated + pIteratingIAT = pIAT; + unsigned cFuncs = 0; + while ( pIteratingIAT->u1.Function ) + { + cFuncs++; + pIteratingIAT++; + } + + if ( cFuncs == 0 ) // If no imported functions, we're done! + { + LOG( "no imported functions" ); + return false; + } + + // These next few lines ensure that we'll be able to modify the IAT, + // which is often in a read-only section in the EXE. + DWORD flOldProtect, flNewProtect, flDontCare; + MEMORY_BASIC_INFORMATION mbi; + + // Get the current protection attributes + VirtualQuery( pIAT, &mbi, sizeof(mbi) ); + + // remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag + flNewProtect = mbi.Protect; + flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); + flNewProtect |= (PAGE_READWRITE); + + if ( !VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, + flNewProtect, &flOldProtect) ) + { + LOG( "could not remove ReadOnly and ExecuteRead attributes, or add ReadWrite flag" ); + return false; + } + + // If the Default hook is enabled, build an array of redirection stubs in the processes memory. + DLPD_IAT_STUB * pStubs = 0; + if ( DLLHook->UseDefault ) + { + // Allocate memory for the redirection stubs. Make one extra stub at the + // end to be a sentinel + pStubs = new DLPD_IAT_STUB[ cFuncs + 1]; + if ( !pStubs ) + { + LOG( "could not allocate memory for redirection stubs" ); + return false; + } + } + + // Scan through the IAT, completing the stubs and redirecting the IAT + // entries to point to the stubs + pIteratingIAT = pIAT; + + while ( pIteratingIAT->u1.Function ) + { + void* HookFn = 0; // Set to either the SFunctionHook or pStubs. + + if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) ) // import by name + { + PIMAGE_IMPORT_BY_NAME pImportName = MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr, pINT->u1.AddressOfData ); + + LOG( "checking function with name: " << pImportName->Name ); + + // Iterate through the hook functions, searching for this import. + SFunctionHook* FHook = DLLHook->Functions; + while ( FHook->Name ) + { + if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 ) + { + // Save the old function in the SFunctionHook structure and get the new one. + FHook->OrigFn = (void*)pIteratingIAT->u1.Function; + HookFn = FHook->HookFn; + + LOG( "hooked function: " << pImportName->Name ); + + break; + } + + FHook++; + } + + // If the default function is enabled, store the name for the user. + if ( DLLHook->UseDefault ) + pStubs->pszNameOrOrdinal = (DWORD)&pImportName->Name; + } + else // added comparison for ordinal + { + LOG( "checking function at ordinal: " << pINT->u1.Ordinal ); + + SFunctionHook* FHook = DLLHook->Functions; + while ( FHook->Name ) + { + if ( (DWORD)FHook->Name == pINT->u1.Ordinal ) + { + // Save the old function in the SFunctionHook structure and get the new one. + FHook->OrigFn = (void*)pIteratingIAT->u1.Function; + HookFn = FHook->HookFn; + + LOG( "hooked ordinal: " << pINT->u1.Ordinal ); + + break; + } + FHook++; + } + // If the default function is enabled, store the ordinal for the user. + if ( DLLHook->UseDefault ) + pStubs->pszNameOrOrdinal = (DWORD)pINT->u1.Ordinal; + } + + // If the default function is enabled, fill in the fields to the stub code. + if ( DLLHook->UseDefault ) + { + pStubs->data_call = (DWORD)(PDWORD)DLLHook->DefaultFn + - (DWORD)(PDWORD)&pStubs->instr_JMP; + pStubs->data_JMP = *(PDWORD)pIteratingIAT - (DWORD)(PDWORD)&pStubs->count; + + // If it wasn't manually hooked, use the Stub function. + if ( !HookFn ) + HookFn = (void*)pStubs; + } + + // Replace the IAT function pointer if we have a hook. + if ( HookFn ) + { + // Cheez-o hack to see if what we're importing is code or data. + // If it's code, we shouldn't be able to write to it + if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) ) + { + pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn; + } + else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) + { + // Special hack for Win9X, which builds stubs for imported + // functions in system DLLs (Loaded above 2GB). These stubs are + // writeable, so we have to explicitly check for this case + if ( pIteratingIAT->u1.Function > (DWORD)(PDWORD)0x80000000 ) + pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn; + } + } + + if ( DLLHook->UseDefault ) + pStubs++; // Advance to next stub + + pIteratingIAT++; // Advance to next IAT entry + pINT++; // Advance to next INT entry + } + + if ( DLLHook->UseDefault ) + pStubs->pszNameOrOrdinal = 0; // Final stub is a sentinel + + // Put the page attributes back the way they were. + VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare); + + return true; +} + +//=========================================================================== +// Top level routine to find the EXE's imports, and redirect them +bool HookAPICalls( SDLLHook* hook ) +{ + if ( !hook ) + { + LOG("no hook"); + return false; + } + + HMODULE hModEXE = GetModuleHandle( 0 ); + + PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule( hModEXE ); + + if ( !pExeNTHdr ) + { + LOG("no PE header"); + return false; + } + + DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory + [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + if ( !importRVA ) + { + LOG("no virtual address for image directory entry import"); + return false; + } + + // Convert imports RVA to a usable pointer + PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR, + hModEXE, importRVA ); + + // Save off imports address in a global for later use + g_pFirstImportDesc = pImportDesc; + + // Iterate through each import descriptor, and redirect if appropriate + while ( pImportDesc->FirstThunk ) + { + PSTR pszImportModuleName = MakePtr( PSTR, hModEXE, pImportDesc->Name); + + if ( lstrcmpi( pszImportModuleName, hook->Name ) == 0 ) + { + LOG( "found " << hook->Name << " in process" ); + + if ( RedirectIAT( hook, pImportDesc, (PVOID)hModEXE ) ) + { + LOG( "redirected IAT ok" ); + return true; + } + else + { + LOG( "failed to redirect IAT" ); + } + } + + pImportDesc++; // Advance to next import descriptor + } + + return false; +} + diff --git a/src/lib/platform/HookDLL.h b/src/lib/platform/HookDLL.h index ded57198..2d3e843d 100644 --- a/src/lib/platform/HookDLL.h +++ b/src/lib/platform/HookDLL.h @@ -14,68 +14,68 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - */ - -/*-------------------------------------------------------------------------------------------------------- - Original comment: - - APIHIJACK.H - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000. - http://msdn.microsoft.com/library/periodic/period00/hood0200.htm - Adapted by Wade Brainerd, wadeb@wadeb.com ---------------------------------------------------------------------------------------------------------*/ - -#pragma once - -#define WIN32_LEAN_AND_MEAN -#include - -#pragma warning(disable:4200) - -// Macro for convenient pointer addition. -// Essentially treats the last two parameters as DWORDs. The first -// parameter is used to typecast the result to the appropriate pointer type. -#define MakePtr(cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue)) - -// Default Hook Stub Structure: Contains data about the original function, Name/Ordinal, Address -// and a Count field. This is actually a block of assembly code. -#pragma pack( push, 1 ) -struct DLPD_IAT_STUB -{ - BYTE instr_CALL; - DWORD data_call; - BYTE instr_JMP; - DWORD data_JMP; - DWORD count; - DWORD pszNameOrOrdinal; - - DLPD_IAT_STUB() : instr_CALL( 0xE8 ), instr_JMP( 0xE9 ), count( 0 ) {} -}; -#pragma pack( pop ) - -// Example DefaultHook procedure, called from the DLPD_IAT_STUB stubs. -// Increments "count" field of the stub. -// See the implementation for more information. -void __cdecl DefaultHook( PVOID dummy ); - -struct SFunctionHook -{ - char* Name; // Function name, e.g. "DirectDrawCreateEx". - void* HookFn; // Address of your function. - void* OrigFn; // Stored by HookAPICalls, the address of the original function. -}; - -struct SDLLHook -{ - // Name of the DLL, e.g. "DDRAW.DLL" - char* Name; - - // Set true to call the default for all non-hooked functions before they are executed. - bool UseDefault; - void* DefaultFn; - - // Function hook array. Terminated with a NULL Name field. - SFunctionHook Functions[]; -}; - -// Hook functions one or more DLLs. -bool HookAPICalls( SDLLHook* hook ); + */ + +/*-------------------------------------------------------------------------------------------------------- + Original comment: + + APIHIJACK.H - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000. + http://msdn.microsoft.com/library/periodic/period00/hood0200.htm + Adapted by Wade Brainerd, wadeb@wadeb.com +--------------------------------------------------------------------------------------------------------*/ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +#pragma warning(disable:4200) + +// Macro for convenient pointer addition. +// Essentially treats the last two parameters as DWORDs. The first +// parameter is used to typecast the result to the appropriate pointer type. +#define MakePtr(cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue)) + +// Default Hook Stub Structure: Contains data about the original function, Name/Ordinal, Address +// and a Count field. This is actually a block of assembly code. +#pragma pack( push, 1 ) +struct DLPD_IAT_STUB +{ + BYTE instr_CALL; + DWORD data_call; + BYTE instr_JMP; + DWORD data_JMP; + DWORD count; + DWORD pszNameOrOrdinal; + + DLPD_IAT_STUB() : instr_CALL( 0xE8 ), instr_JMP( 0xE9 ), count( 0 ) {} +}; +#pragma pack( pop ) + +// Example DefaultHook procedure, called from the DLPD_IAT_STUB stubs. +// Increments "count" field of the stub. +// See the implementation for more information. +void __cdecl DefaultHook( PVOID dummy ); + +struct SFunctionHook +{ + char* Name; // Function name, e.g. "DirectDrawCreateEx". + void* HookFn; // Address of your function. + void* OrigFn; // Stored by HookAPICalls, the address of the original function. +}; + +struct SDLLHook +{ + // Name of the DLL, e.g. "DDRAW.DLL" + char* Name; + + // Set true to call the default for all non-hooked functions before they are executed. + bool UseDefault; + void* DefaultFn; + + // Function hook array. Terminated with a NULL Name field. + SFunctionHook Functions[]; +}; + +// Hook functions one or more DLLs. +bool HookAPICalls( SDLLHook* hook ); diff --git a/src/lib/platform/XInput13.h b/src/lib/platform/XInput13.h index ee434893..a1d5a180 100644 --- a/src/lib/platform/XInput13.h +++ b/src/lib/platform/XInput13.h @@ -1,229 +1,229 @@ -/*************************************************************************** -* * -* XInput.h -- This module defines XBOX controller APIs * -* and constansts for the Windows platform. * -* * +/*************************************************************************** +* * +* XInput.h -- This module defines XBOX controller APIs * +* and constansts for the Windows platform. * +* * * Copyright (C) 2012 Bolton Software Ltd. -* Copyright (c) Microsoft Corp. All rights reserved. * -* * -***************************************************************************/ -#ifndef _XINPUT_H_ -#define _XINPUT_H_ - -#include - -// Current name of the DLL shipped in the same SDK as this header. -// The name reflects the current version -#ifndef XINPUT_USE_9_1_0 -#define XINPUT_DLL_A "xinput1_3.dll" -#define XINPUT_DLL_W L"xinput1_3.dll" -#else -#define XINPUT_DLL_A "xinput9_1_0.dll" -#define XINPUT_DLL_W L"xinput9_1_0.dll" -#endif -#ifdef UNICODE - #define XINPUT_DLL XINPUT_DLL_W -#else - #define XINPUT_DLL XINPUT_DLL_A -#endif - -// -// Device types available in XINPUT_CAPABILITIES -// -#define XINPUT_DEVTYPE_GAMEPAD 0x01 - -// -// Device subtypes available in XINPUT_CAPABILITIES -// -#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01 - -#ifndef XINPUT_USE_9_1_0 - -#define XINPUT_DEVSUBTYPE_WHEEL 0x02 -#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 -#define XINPUT_DEVSUBTYPE_FLIGHT_SICK 0x04 -#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 -#define XINPUT_DEVSUBTYPE_GUITAR 0x06 -#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 - -#endif // !XINPUT_USE_9_1_0 - - - -// -// Flags for XINPUT_CAPABILITIES -// -#define XINPUT_CAPS_VOICE_SUPPORTED 0x0004 - -// -// Constants for gamepad buttons -// -#define XINPUT_GAMEPAD_DPAD_UP 0x0001 -#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002 -#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004 -#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008 -#define XINPUT_GAMEPAD_START 0x0010 -#define XINPUT_GAMEPAD_BACK 0x0020 -#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040 -#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080 -#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 -#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200 -#define XINPUT_GAMEPAD_A 0x1000 -#define XINPUT_GAMEPAD_B 0x2000 -#define XINPUT_GAMEPAD_X 0x4000 -#define XINPUT_GAMEPAD_Y 0x8000 - - -// -// Gamepad thresholds -// -#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849 -#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689 -#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30 - -// -// Flags to pass to XInputGetCapabilities -// -#define XINPUT_FLAG_GAMEPAD 0x00000001 - - -#ifndef XINPUT_USE_9_1_0 - -// -// Devices that support batteries -// -#define BATTERY_DEVTYPE_GAMEPAD 0x00 -#define BATTERY_DEVTYPE_HEADSET 0x01 - -// -// Flags for battery status level -// -#define BATTERY_TYPE_DISCONNECTED 0x00 // This device is not connected -#define BATTERY_TYPE_WIRED 0x01 // Wired device, no battery -#define BATTERY_TYPE_ALKALINE 0x02 // Alkaline battery source -#define BATTERY_TYPE_NIMH 0x03 // Nickel Metal Hydride battery source -#define BATTERY_TYPE_UNKNOWN 0xFF // Cannot determine the battery type - -// These are only valid for wireless, connected devices, with known battery types -// The amount of use time remaining depends on the type of device. -#define BATTERY_LEVEL_EMPTY 0x00 -#define BATTERY_LEVEL_LOW 0x01 -#define BATTERY_LEVEL_MEDIUM 0x02 -#define BATTERY_LEVEL_FULL 0x03 - -// User index definitions -#define XUSER_MAX_COUNT 4 - -#define XUSER_INDEX_ANY 0x000000FF - - -// -// Codes returned for the gamepad keystroke -// - -#define VK_PAD_A 0x5800 -#define VK_PAD_B 0x5801 -#define VK_PAD_X 0x5802 -#define VK_PAD_Y 0x5803 -#define VK_PAD_RSHOULDER 0x5804 -#define VK_PAD_LSHOULDER 0x5805 -#define VK_PAD_LTRIGGER 0x5806 -#define VK_PAD_RTRIGGER 0x5807 - -#define VK_PAD_DPAD_UP 0x5810 -#define VK_PAD_DPAD_DOWN 0x5811 -#define VK_PAD_DPAD_LEFT 0x5812 -#define VK_PAD_DPAD_RIGHT 0x5813 -#define VK_PAD_START 0x5814 -#define VK_PAD_BACK 0x5815 -#define VK_PAD_LTHUMB_PRESS 0x5816 -#define VK_PAD_RTHUMB_PRESS 0x5817 - -#define VK_PAD_LTHUMB_UP 0x5820 -#define VK_PAD_LTHUMB_DOWN 0x5821 -#define VK_PAD_LTHUMB_RIGHT 0x5822 -#define VK_PAD_LTHUMB_LEFT 0x5823 -#define VK_PAD_LTHUMB_UPLEFT 0x5824 -#define VK_PAD_LTHUMB_UPRIGHT 0x5825 -#define VK_PAD_LTHUMB_DOWNRIGHT 0x5826 -#define VK_PAD_LTHUMB_DOWNLEFT 0x5827 - -#define VK_PAD_RTHUMB_UP 0x5830 -#define VK_PAD_RTHUMB_DOWN 0x5831 -#define VK_PAD_RTHUMB_RIGHT 0x5832 -#define VK_PAD_RTHUMB_LEFT 0x5833 -#define VK_PAD_RTHUMB_UPLEFT 0x5834 -#define VK_PAD_RTHUMB_UPRIGHT 0x5835 -#define VK_PAD_RTHUMB_DOWNRIGHT 0x5836 -#define VK_PAD_RTHUMB_DOWNLEFT 0x5837 - -// -// Flags used in XINPUT_KEYSTROKE -// -#define XINPUT_KEYSTROKE_KEYDOWN 0x0001 -#define XINPUT_KEYSTROKE_KEYUP 0x0002 -#define XINPUT_KEYSTROKE_REPEAT 0x0004 - -#endif //!XINPUT_USE_9_1_0 - -// -// Structures used by XInput APIs -// -typedef struct _XINPUT_GAMEPAD -{ - WORD wButtons; - BYTE bLeftTrigger; - BYTE bRightTrigger; - SHORT sThumbLX; - SHORT sThumbLY; - SHORT sThumbRX; - SHORT sThumbRY; -} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD; - -typedef struct _XINPUT_STATE -{ - DWORD dwPacketNumber; - XINPUT_GAMEPAD Gamepad; -} XINPUT_STATE, *PXINPUT_STATE; - -typedef struct _XINPUT_VIBRATION -{ - WORD wLeftMotorSpeed; - WORD wRightMotorSpeed; -} XINPUT_VIBRATION, *PXINPUT_VIBRATION; - -typedef struct _XINPUT_CAPABILITIES -{ - BYTE Type; - BYTE SubType; - WORD Flags; - XINPUT_GAMEPAD Gamepad; - XINPUT_VIBRATION Vibration; -} XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES; - -#ifndef XINPUT_USE_9_1_0 - -typedef struct _XINPUT_BATTERY_INFORMATION -{ - BYTE BatteryType; - BYTE BatteryLevel; -} XINPUT_BATTERY_INFORMATION, *PXINPUT_BATTERY_INFORMATION; - -typedef struct _XINPUT_KEYSTROKE -{ - WORD VirtualKey; - WCHAR Unicode; - WORD Flags; - BYTE UserIndex; - BYTE HidCode; -} XINPUT_KEYSTROKE, *PXINPUT_KEYSTROKE; - -#endif // !XINPUT_USE_9_1_0 - -// -// XInput APIs -// - -// now defined in proxy class. - -#endif //_XINPUT_H_ +* Copyright (c) Microsoft Corp. All rights reserved. * +* * +***************************************************************************/ +#ifndef _XINPUT_H_ +#define _XINPUT_H_ + +#include + +// Current name of the DLL shipped in the same SDK as this header. +// The name reflects the current version +#ifndef XINPUT_USE_9_1_0 +#define XINPUT_DLL_A "xinput1_3.dll" +#define XINPUT_DLL_W L"xinput1_3.dll" +#else +#define XINPUT_DLL_A "xinput9_1_0.dll" +#define XINPUT_DLL_W L"xinput9_1_0.dll" +#endif +#ifdef UNICODE + #define XINPUT_DLL XINPUT_DLL_W +#else + #define XINPUT_DLL XINPUT_DLL_A +#endif + +// +// Device types available in XINPUT_CAPABILITIES +// +#define XINPUT_DEVTYPE_GAMEPAD 0x01 + +// +// Device subtypes available in XINPUT_CAPABILITIES +// +#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01 + +#ifndef XINPUT_USE_9_1_0 + +#define XINPUT_DEVSUBTYPE_WHEEL 0x02 +#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 +#define XINPUT_DEVSUBTYPE_FLIGHT_SICK 0x04 +#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 +#define XINPUT_DEVSUBTYPE_GUITAR 0x06 +#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 + +#endif // !XINPUT_USE_9_1_0 + + + +// +// Flags for XINPUT_CAPABILITIES +// +#define XINPUT_CAPS_VOICE_SUPPORTED 0x0004 + +// +// Constants for gamepad buttons +// +#define XINPUT_GAMEPAD_DPAD_UP 0x0001 +#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002 +#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004 +#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008 +#define XINPUT_GAMEPAD_START 0x0010 +#define XINPUT_GAMEPAD_BACK 0x0020 +#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040 +#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080 +#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 +#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200 +#define XINPUT_GAMEPAD_A 0x1000 +#define XINPUT_GAMEPAD_B 0x2000 +#define XINPUT_GAMEPAD_X 0x4000 +#define XINPUT_GAMEPAD_Y 0x8000 + + +// +// Gamepad thresholds +// +#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849 +#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689 +#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30 + +// +// Flags to pass to XInputGetCapabilities +// +#define XINPUT_FLAG_GAMEPAD 0x00000001 + + +#ifndef XINPUT_USE_9_1_0 + +// +// Devices that support batteries +// +#define BATTERY_DEVTYPE_GAMEPAD 0x00 +#define BATTERY_DEVTYPE_HEADSET 0x01 + +// +// Flags for battery status level +// +#define BATTERY_TYPE_DISCONNECTED 0x00 // This device is not connected +#define BATTERY_TYPE_WIRED 0x01 // Wired device, no battery +#define BATTERY_TYPE_ALKALINE 0x02 // Alkaline battery source +#define BATTERY_TYPE_NIMH 0x03 // Nickel Metal Hydride battery source +#define BATTERY_TYPE_UNKNOWN 0xFF // Cannot determine the battery type + +// These are only valid for wireless, connected devices, with known battery types +// The amount of use time remaining depends on the type of device. +#define BATTERY_LEVEL_EMPTY 0x00 +#define BATTERY_LEVEL_LOW 0x01 +#define BATTERY_LEVEL_MEDIUM 0x02 +#define BATTERY_LEVEL_FULL 0x03 + +// User index definitions +#define XUSER_MAX_COUNT 4 + +#define XUSER_INDEX_ANY 0x000000FF + + +// +// Codes returned for the gamepad keystroke +// + +#define VK_PAD_A 0x5800 +#define VK_PAD_B 0x5801 +#define VK_PAD_X 0x5802 +#define VK_PAD_Y 0x5803 +#define VK_PAD_RSHOULDER 0x5804 +#define VK_PAD_LSHOULDER 0x5805 +#define VK_PAD_LTRIGGER 0x5806 +#define VK_PAD_RTRIGGER 0x5807 + +#define VK_PAD_DPAD_UP 0x5810 +#define VK_PAD_DPAD_DOWN 0x5811 +#define VK_PAD_DPAD_LEFT 0x5812 +#define VK_PAD_DPAD_RIGHT 0x5813 +#define VK_PAD_START 0x5814 +#define VK_PAD_BACK 0x5815 +#define VK_PAD_LTHUMB_PRESS 0x5816 +#define VK_PAD_RTHUMB_PRESS 0x5817 + +#define VK_PAD_LTHUMB_UP 0x5820 +#define VK_PAD_LTHUMB_DOWN 0x5821 +#define VK_PAD_LTHUMB_RIGHT 0x5822 +#define VK_PAD_LTHUMB_LEFT 0x5823 +#define VK_PAD_LTHUMB_UPLEFT 0x5824 +#define VK_PAD_LTHUMB_UPRIGHT 0x5825 +#define VK_PAD_LTHUMB_DOWNRIGHT 0x5826 +#define VK_PAD_LTHUMB_DOWNLEFT 0x5827 + +#define VK_PAD_RTHUMB_UP 0x5830 +#define VK_PAD_RTHUMB_DOWN 0x5831 +#define VK_PAD_RTHUMB_RIGHT 0x5832 +#define VK_PAD_RTHUMB_LEFT 0x5833 +#define VK_PAD_RTHUMB_UPLEFT 0x5834 +#define VK_PAD_RTHUMB_UPRIGHT 0x5835 +#define VK_PAD_RTHUMB_DOWNRIGHT 0x5836 +#define VK_PAD_RTHUMB_DOWNLEFT 0x5837 + +// +// Flags used in XINPUT_KEYSTROKE +// +#define XINPUT_KEYSTROKE_KEYDOWN 0x0001 +#define XINPUT_KEYSTROKE_KEYUP 0x0002 +#define XINPUT_KEYSTROKE_REPEAT 0x0004 + +#endif //!XINPUT_USE_9_1_0 + +// +// Structures used by XInput APIs +// +typedef struct _XINPUT_GAMEPAD +{ + WORD wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; +} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD; + +typedef struct _XINPUT_STATE +{ + DWORD dwPacketNumber; + XINPUT_GAMEPAD Gamepad; +} XINPUT_STATE, *PXINPUT_STATE; + +typedef struct _XINPUT_VIBRATION +{ + WORD wLeftMotorSpeed; + WORD wRightMotorSpeed; +} XINPUT_VIBRATION, *PXINPUT_VIBRATION; + +typedef struct _XINPUT_CAPABILITIES +{ + BYTE Type; + BYTE SubType; + WORD Flags; + XINPUT_GAMEPAD Gamepad; + XINPUT_VIBRATION Vibration; +} XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES; + +#ifndef XINPUT_USE_9_1_0 + +typedef struct _XINPUT_BATTERY_INFORMATION +{ + BYTE BatteryType; + BYTE BatteryLevel; +} XINPUT_BATTERY_INFORMATION, *PXINPUT_BATTERY_INFORMATION; + +typedef struct _XINPUT_KEYSTROKE +{ + WORD VirtualKey; + WCHAR Unicode; + WORD Flags; + BYTE UserIndex; + BYTE HidCode; +} XINPUT_KEYSTROKE, *PXINPUT_KEYSTROKE; + +#endif // !XINPUT_USE_9_1_0 + +// +// XInput APIs +// + +// now defined in proxy class. + +#endif //_XINPUT_H_ diff --git a/src/lib/platform/XInputHook.cpp b/src/lib/platform/XInputHook.cpp index f606d4ac..d80079d3 100644 --- a/src/lib/platform/XInputHook.cpp +++ b/src/lib/platform/XInputHook.cpp @@ -1,296 +1,296 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * Copyright (C) 2011 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ + * Copyright (C) 2011 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -#define WIN32_LEAN_AND_MEAN -#define SYNERGY_EXPORT_XINPUT_HOOKS -#define REQUIRED_XINPUT_DLL "xinput1_3.dll" -#define HOOK_TIMEOUT 10000 // 10 sec - -#include -#include -#include "XInputHook.h" -#include "HookDLL.h" -#include - -HINSTANCE dll; -char name[256]; - -#pragma data_seg(".SHARED") - -HHOOK s_hook = NULL; - -// @todo use a struct for multiple gamepad support -WORD s_buttons = 0; -SHORT s_leftStickX = 0; -SHORT s_leftStickY = 0; -SHORT s_rightStickX = 0; -SHORT s_rightStickY = 0; -BYTE s_leftTrigger = 0; -BYTE s_rightTrigger = 0; -BOOL s_timingReqQueued = FALSE; -BOOL s_timingRespQueued = FALSE; -DWORD s_lastFakeMillis = 0; -WORD s_fakeFreqMillis = 0; -DWORD s_packetNumber = 0; -WORD s_leftMotor = 0; -WORD s_rightMotor = 0; -BOOL s_feedbackQueued = FALSE; - -#pragma data_seg() - -#pragma comment(linker, "/SECTION:.SHARED,RWS") - -#include -std::stringstream _xInputHookLogStream; -#define LOG(s) \ - _xInputHookLogStream.str(""); \ - _xInputHookLogStream << "Synergy XInputHook: " << s << endl; \ - OutputDebugString( _xInputHookLogStream.str().c_str()) - -using namespace std; - -SDLLHook s_xInputHook = -{ - XINPUT_DLL, - false, NULL, - { - { (char*)(0x80000002), HookXInputGetState }, - { (char*)(0x80000003), HookXInputSetState }, - { (char*)(0x80000004), HookXInputGetCapabilities }, - } -}; - -BOOL APIENTRY -DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) -{ - if (reason == DLL_PROCESS_ATTACH) - { - dll = module; - - // disable unwanted thread notifications to reduce overhead - DisableThreadLibraryCalls(dll); - - GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name)); - - // don't hook synergys (this needs to detect real input) - if (string(name).find("synergy") == string::npos) - { - LOG("checking '" << name << "' for " << XINPUT_DLL); - HookAPICalls(&s_xInputHook); - } - } - - return TRUE; -} - -void -SetXInputButtons(DWORD userIndex, WORD buttons) -{ - s_buttons = buttons; - - s_packetNumber++; - - LOG("SetXInputButtons: idx=" << userIndex << ", btns=" << buttons); -} - -void -SetXInputSticks(DWORD userIndex, SHORT lx, SHORT ly, SHORT rx, SHORT ry) -{ - s_leftStickX = lx; - s_leftStickY = ly; - s_rightStickX = rx; - s_rightStickY = ry; - - s_packetNumber++; - - LOG("SetXInputSticks:" << - " l=" << s_leftStickX << "," << s_leftStickY << - " r=" << s_rightStickX << "," << s_rightStickY); -} - -void -SetXInputTriggers(DWORD userIndex, BYTE left, BYTE right) -{ - s_leftTrigger = left; - s_rightTrigger = right; - - s_packetNumber++; - - LOG("SetXInputTriggers: " << - "l=" << (int)left << " r=" << (int)right); -} - -void -QueueXInputTimingReq() -{ - s_timingReqQueued = TRUE; -} - -BOOL -DequeueXInputTimingResp() -{ - BOOL result = s_timingRespQueued; - s_timingRespQueued = FALSE; - return result; -} - -BOOL -DequeueXInputFeedback(WORD* leftMotor, WORD* rightMotor) -{ - if (s_feedbackQueued) - { - *leftMotor = s_leftMotor; - *rightMotor = s_rightMotor; - s_feedbackQueued = FALSE; - return TRUE; - } - return FALSE; -} - -WORD -GetXInputFakeFreqMillis() -{ - return s_fakeFreqMillis; -} - -DWORD WINAPI -HookXInputGetState(DWORD userIndex, XINPUT_STATE* state) -{ - // @todo multiple device support - if (userIndex != 0) - { - return ERROR_DEVICE_NOT_CONNECTED; - } - - DWORD now = GetTickCount(); - s_fakeFreqMillis = (WORD)(now - s_lastFakeMillis); - s_lastFakeMillis = now; - - state->dwPacketNumber = s_packetNumber; - state->Gamepad.wButtons = s_buttons; - state->Gamepad.bLeftTrigger = s_leftTrigger; - state->Gamepad.bRightTrigger = s_rightTrigger; - state->Gamepad.sThumbLX = s_leftStickX; - state->Gamepad.sThumbLY = s_leftStickY; - state->Gamepad.sThumbRX = s_rightStickX; - state->Gamepad.sThumbRY = s_rightStickY; - - LOG("HookXInputGetState" - << ", idx=" << userIndex - << ", pkt=" << state->dwPacketNumber - << ", btn=" << state->Gamepad.wButtons - << ", t1=" << (int)state->Gamepad.bLeftTrigger - << ", t2=" << (int)state->Gamepad.bRightTrigger - << ", s1=" << state->Gamepad.sThumbLX << "," << state->Gamepad.sThumbLY - << ", s2=" << state->Gamepad.sThumbRX << "," << state->Gamepad.sThumbRY); - - if (s_timingReqQueued) - { - s_timingRespQueued = TRUE; - s_timingReqQueued = FALSE; - LOG("timing response queued"); - } - - return ERROR_SUCCESS; -} - -DWORD WINAPI -HookXInputSetState(DWORD userIndex, XINPUT_VIBRATION* vibration) -{ - // @todo multiple device support - if (userIndex != 0) - { - return ERROR_DEVICE_NOT_CONNECTED; - } - - // only change values and queue feedback change if - // feedback has actually changed. - if ((s_leftMotor != vibration->wLeftMotorSpeed) || - (s_rightMotor != vibration->wRightMotorSpeed)) - { - s_leftMotor = vibration->wLeftMotorSpeed; - s_rightMotor = vibration->wRightMotorSpeed; - s_feedbackQueued = TRUE; - - LOG("HookXInputSetState" - ", idx=" << userIndex << - ", lm=" << s_leftMotor << - ", rm=" << s_rightMotor); - } - - return ERROR_SUCCESS; -} - -DWORD WINAPI -HookXInputGetCapabilities(DWORD userIndex, DWORD flags, XINPUT_CAPABILITIES* capabilities) -{ - // @todo multiple device support - if (userIndex != 0) - { - return ERROR_DEVICE_NOT_CONNECTED; - } - - LOG("HookXInputGetCapabilities" - ", idx=" << userIndex << - ", flags=" << flags); - - capabilities->Type = 1; - capabilities->SubType = 1; - capabilities->Flags = 4; - capabilities->Gamepad.bLeftTrigger = 1; - capabilities->Gamepad.bRightTrigger = 1; - capabilities->Gamepad.sThumbLX = 1; - capabilities->Gamepad.sThumbLY = 1; - capabilities->Gamepad.sThumbRX = 1; - capabilities->Gamepad.sThumbRY = 1; - capabilities->Gamepad.wButtons = 62463; - capabilities->Vibration.wLeftMotorSpeed = 1; - capabilities->Vibration.wRightMotorSpeed = 1; - - return ERROR_SUCCESS; -} - -synxinhk_API LRESULT CALLBACK -HookProc(int code, WPARAM wParam, LPARAM lParam) -{ - return CallNextHookEx(s_hook, code, wParam, lParam); -} - -synxinhk_API BOOL -InstallXInputHook() -{ - if (_stricmp(XINPUT_DLL, REQUIRED_XINPUT_DLL) != 0) - { - LOG("DLL not supported: " << XINPUT_DLL); - return FALSE; - } - - LOG("installing hook"); - s_hook = SetWindowsHookEx(WH_CBT, HookProc, dll, 0); - LOG("hook installed"); - - return TRUE; -} - -synxinhk_API void -RemoveXInputHook() -{ - LOG("removing hook"); - UnhookWindowsHookEx(s_hook); - LOG("hook removed"); -} +#define WIN32_LEAN_AND_MEAN +#define SYNERGY_EXPORT_XINPUT_HOOKS +#define REQUIRED_XINPUT_DLL "xinput1_3.dll" +#define HOOK_TIMEOUT 10000 // 10 sec + +#include +#include +#include "XInputHook.h" +#include "HookDLL.h" +#include + +HINSTANCE dll; +char name[256]; + +#pragma data_seg(".SHARED") + +HHOOK s_hook = NULL; + +// @todo use a struct for multiple gamepad support +WORD s_buttons = 0; +SHORT s_leftStickX = 0; +SHORT s_leftStickY = 0; +SHORT s_rightStickX = 0; +SHORT s_rightStickY = 0; +BYTE s_leftTrigger = 0; +BYTE s_rightTrigger = 0; +BOOL s_timingReqQueued = FALSE; +BOOL s_timingRespQueued = FALSE; +DWORD s_lastFakeMillis = 0; +WORD s_fakeFreqMillis = 0; +DWORD s_packetNumber = 0; +WORD s_leftMotor = 0; +WORD s_rightMotor = 0; +BOOL s_feedbackQueued = FALSE; + +#pragma data_seg() + +#pragma comment(linker, "/SECTION:.SHARED,RWS") + +#include +std::stringstream _xInputHookLogStream; +#define LOG(s) \ + _xInputHookLogStream.str(""); \ + _xInputHookLogStream << "Synergy XInputHook: " << s << endl; \ + OutputDebugString( _xInputHookLogStream.str().c_str()) + +using namespace std; + +SDLLHook s_xInputHook = +{ + XINPUT_DLL, + false, NULL, + { + { (char*)(0x80000002), HookXInputGetState }, + { (char*)(0x80000003), HookXInputSetState }, + { (char*)(0x80000004), HookXInputGetCapabilities }, + } +}; + +BOOL APIENTRY +DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) +{ + if (reason == DLL_PROCESS_ATTACH) + { + dll = module; + + // disable unwanted thread notifications to reduce overhead + DisableThreadLibraryCalls(dll); + + GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name)); + + // don't hook synergys (this needs to detect real input) + if (string(name).find("synergy") == string::npos) + { + LOG("checking '" << name << "' for " << XINPUT_DLL); + HookAPICalls(&s_xInputHook); + } + } + + return TRUE; +} + +void +SetXInputButtons(DWORD userIndex, WORD buttons) +{ + s_buttons = buttons; + + s_packetNumber++; + + LOG("SetXInputButtons: idx=" << userIndex << ", btns=" << buttons); +} + +void +SetXInputSticks(DWORD userIndex, SHORT lx, SHORT ly, SHORT rx, SHORT ry) +{ + s_leftStickX = lx; + s_leftStickY = ly; + s_rightStickX = rx; + s_rightStickY = ry; + + s_packetNumber++; + + LOG("SetXInputSticks:" << + " l=" << s_leftStickX << "," << s_leftStickY << + " r=" << s_rightStickX << "," << s_rightStickY); +} + +void +SetXInputTriggers(DWORD userIndex, BYTE left, BYTE right) +{ + s_leftTrigger = left; + s_rightTrigger = right; + + s_packetNumber++; + + LOG("SetXInputTriggers: " << + "l=" << (int)left << " r=" << (int)right); +} + +void +QueueXInputTimingReq() +{ + s_timingReqQueued = TRUE; +} + +BOOL +DequeueXInputTimingResp() +{ + BOOL result = s_timingRespQueued; + s_timingRespQueued = FALSE; + return result; +} + +BOOL +DequeueXInputFeedback(WORD* leftMotor, WORD* rightMotor) +{ + if (s_feedbackQueued) + { + *leftMotor = s_leftMotor; + *rightMotor = s_rightMotor; + s_feedbackQueued = FALSE; + return TRUE; + } + return FALSE; +} + +WORD +GetXInputFakeFreqMillis() +{ + return s_fakeFreqMillis; +} + +DWORD WINAPI +HookXInputGetState(DWORD userIndex, XINPUT_STATE* state) +{ + // @todo multiple device support + if (userIndex != 0) + { + return ERROR_DEVICE_NOT_CONNECTED; + } + + DWORD now = GetTickCount(); + s_fakeFreqMillis = (WORD)(now - s_lastFakeMillis); + s_lastFakeMillis = now; + + state->dwPacketNumber = s_packetNumber; + state->Gamepad.wButtons = s_buttons; + state->Gamepad.bLeftTrigger = s_leftTrigger; + state->Gamepad.bRightTrigger = s_rightTrigger; + state->Gamepad.sThumbLX = s_leftStickX; + state->Gamepad.sThumbLY = s_leftStickY; + state->Gamepad.sThumbRX = s_rightStickX; + state->Gamepad.sThumbRY = s_rightStickY; + + LOG("HookXInputGetState" + << ", idx=" << userIndex + << ", pkt=" << state->dwPacketNumber + << ", btn=" << state->Gamepad.wButtons + << ", t1=" << (int)state->Gamepad.bLeftTrigger + << ", t2=" << (int)state->Gamepad.bRightTrigger + << ", s1=" << state->Gamepad.sThumbLX << "," << state->Gamepad.sThumbLY + << ", s2=" << state->Gamepad.sThumbRX << "," << state->Gamepad.sThumbRY); + + if (s_timingReqQueued) + { + s_timingRespQueued = TRUE; + s_timingReqQueued = FALSE; + LOG("timing response queued"); + } + + return ERROR_SUCCESS; +} + +DWORD WINAPI +HookXInputSetState(DWORD userIndex, XINPUT_VIBRATION* vibration) +{ + // @todo multiple device support + if (userIndex != 0) + { + return ERROR_DEVICE_NOT_CONNECTED; + } + + // only change values and queue feedback change if + // feedback has actually changed. + if ((s_leftMotor != vibration->wLeftMotorSpeed) || + (s_rightMotor != vibration->wRightMotorSpeed)) + { + s_leftMotor = vibration->wLeftMotorSpeed; + s_rightMotor = vibration->wRightMotorSpeed; + s_feedbackQueued = TRUE; + + LOG("HookXInputSetState" + ", idx=" << userIndex << + ", lm=" << s_leftMotor << + ", rm=" << s_rightMotor); + } + + return ERROR_SUCCESS; +} + +DWORD WINAPI +HookXInputGetCapabilities(DWORD userIndex, DWORD flags, XINPUT_CAPABILITIES* capabilities) +{ + // @todo multiple device support + if (userIndex != 0) + { + return ERROR_DEVICE_NOT_CONNECTED; + } + + LOG("HookXInputGetCapabilities" + ", idx=" << userIndex << + ", flags=" << flags); + + capabilities->Type = 1; + capabilities->SubType = 1; + capabilities->Flags = 4; + capabilities->Gamepad.bLeftTrigger = 1; + capabilities->Gamepad.bRightTrigger = 1; + capabilities->Gamepad.sThumbLX = 1; + capabilities->Gamepad.sThumbLY = 1; + capabilities->Gamepad.sThumbRX = 1; + capabilities->Gamepad.sThumbRY = 1; + capabilities->Gamepad.wButtons = 62463; + capabilities->Vibration.wLeftMotorSpeed = 1; + capabilities->Vibration.wRightMotorSpeed = 1; + + return ERROR_SUCCESS; +} + +synxinhk_API LRESULT CALLBACK +HookProc(int code, WPARAM wParam, LPARAM lParam) +{ + return CallNextHookEx(s_hook, code, wParam, lParam); +} + +synxinhk_API BOOL +InstallXInputHook() +{ + if (_stricmp(XINPUT_DLL, REQUIRED_XINPUT_DLL) != 0) + { + LOG("DLL not supported: " << XINPUT_DLL); + return FALSE; + } + + LOG("installing hook"); + s_hook = SetWindowsHookEx(WH_CBT, HookProc, dll, 0); + LOG("hook installed"); + + return TRUE; +} + +synxinhk_API void +RemoveXInputHook() +{ + LOG("removing hook"); + UnhookWindowsHookEx(s_hook); + LOG("hook removed"); +} diff --git a/src/lib/platform/XInputHook.h b/src/lib/platform/XInputHook.h index eb98c29e..08f3f66d 100644 --- a/src/lib/platform/XInputHook.h +++ b/src/lib/platform/XInputHook.h @@ -1,44 +1,44 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * Copyright (C) 2011 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#ifdef synxinhk_EXPORTS -#define synxinhk_API __declspec(dllexport) -#else -#define synxinhk_API __declspec(dllimport) -#endif - -synxinhk_API LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam); -synxinhk_API BOOL InstallXInputHook(); -synxinhk_API void RemoveXInputHook(); -synxinhk_API void SetXInputButtons(DWORD userIndex, WORD buttons); -synxinhk_API void SetXInputSticks(DWORD userIndex, SHORT lx, SHORT ly, SHORT rx, SHORT ry); -synxinhk_API void SetXInputTriggers(DWORD userIndex, BYTE left, BYTE right); -synxinhk_API void QueueXInputTimingReq(); -synxinhk_API BOOL DequeueXInputTimingResp(); -synxinhk_API WORD GetXInputFakeFreqMillis(); -synxinhk_API BOOL DequeueXInputFeedback(WORD* leftMotor, WORD* rightMotor); - -#ifdef SYNERGY_EXPORT_XINPUT_HOOKS - -synxinhk_API DWORD WINAPI HookXInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState); -synxinhk_API DWORD WINAPI HookXInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); -synxinhk_API DWORD WINAPI HookXInputGetCapabilities(DWORD userIndex, DWORD flags, XINPUT_CAPABILITIES* capabilities); - -#endif + * Copyright (C) 2011 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifdef synxinhk_EXPORTS +#define synxinhk_API __declspec(dllexport) +#else +#define synxinhk_API __declspec(dllimport) +#endif + +synxinhk_API LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam); +synxinhk_API BOOL InstallXInputHook(); +synxinhk_API void RemoveXInputHook(); +synxinhk_API void SetXInputButtons(DWORD userIndex, WORD buttons); +synxinhk_API void SetXInputSticks(DWORD userIndex, SHORT lx, SHORT ly, SHORT rx, SHORT ry); +synxinhk_API void SetXInputTriggers(DWORD userIndex, BYTE left, BYTE right); +synxinhk_API void QueueXInputTimingReq(); +synxinhk_API BOOL DequeueXInputTimingResp(); +synxinhk_API WORD GetXInputFakeFreqMillis(); +synxinhk_API BOOL DequeueXInputFeedback(WORD* leftMotor, WORD* rightMotor); + +#ifdef SYNERGY_EXPORT_XINPUT_HOOKS + +synxinhk_API DWORD WINAPI HookXInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState); +synxinhk_API DWORD WINAPI HookXInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); +synxinhk_API DWORD WINAPI HookXInputGetCapabilities(DWORD userIndex, DWORD flags, XINPUT_CAPABILITIES* capabilities); + +#endif diff --git a/src/lib/platform/XInputProxy13.cpp b/src/lib/platform/XInputProxy13.cpp index ab1cb79d..f3bac29f 100644 --- a/src/lib/platform/XInputProxy13.cpp +++ b/src/lib/platform/XInputProxy13.cpp @@ -18,7 +18,7 @@ #define SYNERGY_EXPORT_XINPUT_HOOKS #define WIN32_LEAN_AND_MEAN - + #include #include "XInputProxy13.h" #include "XInputHook.h" @@ -31,43 +31,43 @@ #pragma comment(linker, "/EXPORT:XInputGetBatteryInformation=_XInputGetBatteryInformation@12,@7") #pragma comment(linker, "/EXPORT:XInputGetKeystroke=_XInputGetKeystroke@12,@8") -sxinpx13_API DWORD WINAPI -XInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState) -{ - return HookXInputGetState(dwUserIndex, pState); -} - -sxinpx13_API DWORD WINAPI -XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) -{ - return HookXInputSetState(dwUserIndex, pVibration); -} - -sxinpx13_API DWORD WINAPI -XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities) -{ - return HookXInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities); -} - -sxinpx13_API void WINAPI -XInputEnable(BOOL enable) -{ -} - -sxinpx13_API DWORD WINAPI -XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid) -{ - return ERROR_DEVICE_NOT_CONNECTED; -} - -sxinpx13_API DWORD WINAPI -XInputGetBatteryInformation(DWORD dwUserIndex, BYTE devType, XINPUT_BATTERY_INFORMATION* pBatteryInformation) -{ - return ERROR_DEVICE_NOT_CONNECTED; -} - sxinpx13_API DWORD WINAPI -XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, PXINPUT_KEYSTROKE pKeystroke) -{ +XInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState) +{ + return HookXInputGetState(dwUserIndex, pState); +} + +sxinpx13_API DWORD WINAPI +XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) +{ + return HookXInputSetState(dwUserIndex, pVibration); +} + +sxinpx13_API DWORD WINAPI +XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities) +{ + return HookXInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities); +} + +sxinpx13_API void WINAPI +XInputEnable(BOOL enable) +{ +} + +sxinpx13_API DWORD WINAPI +XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid) +{ + return ERROR_DEVICE_NOT_CONNECTED; +} + +sxinpx13_API DWORD WINAPI +XInputGetBatteryInformation(DWORD dwUserIndex, BYTE devType, XINPUT_BATTERY_INFORMATION* pBatteryInformation) +{ + return ERROR_DEVICE_NOT_CONNECTED; +} + +sxinpx13_API DWORD WINAPI +XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, PXINPUT_KEYSTROKE pKeystroke) +{ return ERROR_DEVICE_NOT_CONNECTED; } diff --git a/src/lib/platform/XInputProxy13.h b/src/lib/platform/XInputProxy13.h index 7e585635..1d0cdb94 100644 --- a/src/lib/platform/XInputProxy13.h +++ b/src/lib/platform/XInputProxy13.h @@ -14,67 +14,67 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - */ - -#pragma once - -#ifdef sxinpx13_EXPORTS -#define sxinpx13_API __declspec(dllexport) -#else -#define sxinpx13_API __declspec(dllimport) -#endif - -#include "XInput13.h" - -#ifdef __cplusplus -extern "C" { -#endif - -sxinpx13_API DWORD WINAPI XInputGetState -( - __in DWORD dwUserIndex, // Index of the gamer associated with the device - __out XINPUT_STATE* pState // Receives the current state -); - -sxinpx13_API DWORD WINAPI XInputSetState -( - __in DWORD dwUserIndex, // Index of the gamer associated with the device - __in XINPUT_VIBRATION* pVibration // The vibration information to send to the controller -); - -sxinpx13_API DWORD WINAPI XInputGetCapabilities -( - __in DWORD dwUserIndex, // Index of the gamer associated with the device - __in DWORD dwFlags, // Input flags that identify the device type - __out XINPUT_CAPABILITIES* pCapabilities // Receives the capabilities -); - -sxinpx13_API void WINAPI XInputEnable -( - __in BOOL enable // [in] Indicates whether xinput is enabled or disabled. -); - -sxinpx13_API DWORD WINAPI XInputGetDSoundAudioDeviceGuids -( - __in DWORD dwUserIndex, // Index of the gamer associated with the device - __out GUID* pDSoundRenderGuid, // DSound device ID for render - __out GUID* pDSoundCaptureGuid // DSound device ID for capture -); - -sxinpx13_API DWORD WINAPI XInputGetBatteryInformation -( - __in DWORD dwUserIndex, // Index of the gamer associated with the device - __in BYTE devType, // Which device on this user index - __out XINPUT_BATTERY_INFORMATION* pBatteryInformation // Contains the level and types of batteries -); - -sxinpx13_API DWORD WINAPI XInputGetKeystroke -( - __in DWORD dwUserIndex, // Index of the gamer associated with the device - __reserved DWORD dwReserved, // Reserved for future use - __out PXINPUT_KEYSTROKE pKeystroke // Pointer to an XINPUT_KEYSTROKE structure that receives an input event. -); - -#ifdef __cplusplus -} + */ + +#pragma once + +#ifdef sxinpx13_EXPORTS +#define sxinpx13_API __declspec(dllexport) +#else +#define sxinpx13_API __declspec(dllimport) +#endif + +#include "XInput13.h" + +#ifdef __cplusplus +extern "C" { +#endif + +sxinpx13_API DWORD WINAPI XInputGetState +( + __in DWORD dwUserIndex, // Index of the gamer associated with the device + __out XINPUT_STATE* pState // Receives the current state +); + +sxinpx13_API DWORD WINAPI XInputSetState +( + __in DWORD dwUserIndex, // Index of the gamer associated with the device + __in XINPUT_VIBRATION* pVibration // The vibration information to send to the controller +); + +sxinpx13_API DWORD WINAPI XInputGetCapabilities +( + __in DWORD dwUserIndex, // Index of the gamer associated with the device + __in DWORD dwFlags, // Input flags that identify the device type + __out XINPUT_CAPABILITIES* pCapabilities // Receives the capabilities +); + +sxinpx13_API void WINAPI XInputEnable +( + __in BOOL enable // [in] Indicates whether xinput is enabled or disabled. +); + +sxinpx13_API DWORD WINAPI XInputGetDSoundAudioDeviceGuids +( + __in DWORD dwUserIndex, // Index of the gamer associated with the device + __out GUID* pDSoundRenderGuid, // DSound device ID for render + __out GUID* pDSoundCaptureGuid // DSound device ID for capture +); + +sxinpx13_API DWORD WINAPI XInputGetBatteryInformation +( + __in DWORD dwUserIndex, // Index of the gamer associated with the device + __in BYTE devType, // Which device on this user index + __out XINPUT_BATTERY_INFORMATION* pBatteryInformation // Contains the level and types of batteries +); + +sxinpx13_API DWORD WINAPI XInputGetKeystroke +( + __in DWORD dwUserIndex, // Index of the gamer associated with the device + __reserved DWORD dwReserved, // Reserved for future use + __out PXINPUT_KEYSTROKE pKeystroke // Pointer to an XINPUT_KEYSTROKE structure that receives an input event. +); + +#ifdef __cplusplus +} #endif \ No newline at end of file diff --git a/src/lib/server/CBaseClientProxy.h b/src/lib/server/CBaseClientProxy.h index 03502fb5..6f72b44d 100644 --- a/src/lib/server/CBaseClientProxy.h +++ b/src/lib/server/CBaseClientProxy.h @@ -83,7 +83,7 @@ public: virtual void screensaver(bool activate) = 0; virtual void resetOptions() = 0; virtual void setOptions(const COptionsList& options) = 0; - virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0; + virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0; virtual CString getName() const; private: diff --git a/src/lib/server/CClientProxy.h b/src/lib/server/CClientProxy.h index 4cebabc2..d2b952e2 100644 --- a/src/lib/server/CClientProxy.h +++ b/src/lib/server/CClientProxy.h @@ -89,7 +89,7 @@ public: virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) = 0; virtual void gameDeviceTimingReq() = 0; virtual void cryptoIv(const UInt8* iv) = 0; - virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0; + virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0; private: synergy::IStream* m_stream; diff --git a/src/lib/server/CClientProxy1_0.cpp b/src/lib/server/CClientProxy1_0.cpp index 319fd6ce..1dd3cc8f 100644 --- a/src/lib/server/CClientProxy1_0.cpp +++ b/src/lib/server/CClientProxy1_0.cpp @@ -394,7 +394,7 @@ CClientProxy1_0::cryptoIv(const UInt8* iv) } void -CClientProxy1_0::fileChunkSending(UInt8 mark, const UInt8* iv) +CClientProxy1_0::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { // ignore -- not supported in protocol 1.0 LOG((CLOG_DEBUG "fileChunkSending not supported")); diff --git a/src/lib/server/CClientProxy1_0.h b/src/lib/server/CClientProxy1_0.h index 62e46a46..bed12e9d 100644 --- a/src/lib/server/CClientProxy1_0.h +++ b/src/lib/server/CClientProxy1_0.h @@ -64,7 +64,7 @@ public: virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2); virtual void gameDeviceTimingReq(); virtual void cryptoIv(const UInt8* iv); - virtual void fileChunkSending(UInt8 mark, const UInt8* data); + virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); protected: virtual bool parseHandshakeMessage(const UInt8* code); diff --git a/src/lib/server/CClientProxy1_3.h b/src/lib/server/CClientProxy1_3.h index 82c87c9f..251cac36 100644 --- a/src/lib/server/CClientProxy1_3.h +++ b/src/lib/server/CClientProxy1_3.h @@ -42,7 +42,6 @@ protected: private: void handleKeepAlive(const CEvent&, void*); - private: double m_keepAliveRate; CEventQueueTimer* m_keepAliveTimer; diff --git a/src/lib/server/CClientProxy1_4.h b/src/lib/server/CClientProxy1_4.h index 587b6470..bdd33622 100644 --- a/src/lib/server/CClientProxy1_4.h +++ b/src/lib/server/CClientProxy1_4.h @@ -29,6 +29,14 @@ public: CClientProxy1_4(const CString& name, synergy::IStream* adoptedStream, CServer* server, IEventQueue* events); ~CClientProxy1_4(); + //! @name accessors + //@{ + + //! get server pointer + CServer* getServer() { return m_server; } + + //@} + // IClient overrides virtual void gameDeviceButtons(GameDeviceID id, GameDeviceButton buttons); virtual void gameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2); diff --git a/src/lib/server/CClientProxy1_5.cpp b/src/lib/server/CClientProxy1_5.cpp index 0f1fdeb1..0fdd7d8a 100644 --- a/src/lib/server/CClientProxy1_5.cpp +++ b/src/lib/server/CClientProxy1_5.cpp @@ -19,13 +19,15 @@ #include "CProtocolUtil.h" #include "CLog.h" #include "IStream.h" +#include "CServer.h" // // CClientProxy1_5 // CClientProxy1_5::CClientProxy1_5(const CString& name, synergy::IStream* stream, CServer* server, IEventQueue* events) : - CClientProxy1_4(name, stream, server, events) + CClientProxy1_4(name, stream, server, events), + m_events(events) { } @@ -34,23 +36,64 @@ CClientProxy1_5::~CClientProxy1_5() } void -CClientProxy1_5::fileChunkSending(UInt8 mark, const UInt8* data) +CClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { - CString chunk(reinterpret_cast(data)); + CString chunk(data, dataSize); switch (mark) { - case '0': - LOG((CLOG_DEBUG2 "file sending start: file size = %s", data)); + case kFileStart: + LOG((CLOG_DEBUG2 "file sending start: size=%s", data)); break; - case '1': - LOG((CLOG_DEBUG2 "file chunk sending: %s", data)); + case kFileChunk: + LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size())); break; - case '2': + case kFileEnd: LOG((CLOG_DEBUG2 "file sending finished")); break; } CProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk); } + +bool +CClientProxy1_5::parseMessage(const UInt8* code) +{ + if (memcmp(code, kMsgDFileTransfer, 4) == 0) { + fileChunkReceived(); + } + else { + return CClientProxy1_4::parseMessage(code); + } + + return true; +} + +void +CClientProxy1_5::fileChunkReceived() +{ + // parse + UInt8 mark; + CString content; + CProtocolUtil::readf(getStream(), kMsgDFileTransfer + 4, &mark, &content); + + CServer* server = getServer(); + switch (mark) { + case kFileStart: + LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str())); + server->clearReceivedFileData(); + server->setExpectedFileSize(content); + break; + + case kFileChunk: + LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size())); + server->fileChunkReceived(content); + break; + + case kFileEnd: + LOG((CLOG_DEBUG2 "file data transfer finished")); + m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), server)); + break; + } +} diff --git a/src/lib/server/CClientProxy1_5.h b/src/lib/server/CClientProxy1_5.h index e88d91c3..65ae94d2 100644 --- a/src/lib/server/CClientProxy1_5.h +++ b/src/lib/server/CClientProxy1_5.h @@ -28,5 +28,10 @@ public: CClientProxy1_5(const CString& name, synergy::IStream* adoptedStream, CServer* server, IEventQueue* events); ~CClientProxy1_5(); - virtual void fileChunkSending(UInt8 mark, const UInt8* data); + virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); + virtual bool parseMessage(const UInt8* code); + void fileChunkReceived(); + +private: + IEventQueue* m_events; }; diff --git a/src/lib/server/CPrimaryClient.cpp b/src/lib/server/CPrimaryClient.cpp index c010d48d..a76f77c0 100644 --- a/src/lib/server/CPrimaryClient.cpp +++ b/src/lib/server/CPrimaryClient.cpp @@ -274,7 +274,7 @@ CPrimaryClient::screensaver(bool) } void -CPrimaryClient::fileChunkSending(UInt8 mark, const UInt8* data) +CPrimaryClient::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { // ignore } diff --git a/src/lib/server/CPrimaryClient.h b/src/lib/server/CPrimaryClient.h index a2a0f6b2..663362b0 100644 --- a/src/lib/server/CPrimaryClient.h +++ b/src/lib/server/CPrimaryClient.h @@ -148,7 +148,7 @@ public: virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); - virtual void fileChunkSending(UInt8 mark, const UInt8* data); + virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); private: CScreen* m_screen; diff --git a/src/lib/server/CServer.cpp b/src/lib/server/CServer.cpp index 320a69a3..b2a49753 100644 --- a/src/lib/server/CServer.cpp +++ b/src/lib/server/CServer.cpp @@ -33,14 +33,22 @@ #include "TMethodEventJob.h" #include "CArch.h" #include "CKeyState.h" +#include "CScreen.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CFileChunker.h" #include #include -#include "CScreen.h" +#include +#include +#include // // CServer // +const size_t CServer::m_chunkSize = 1024 * 512; // 512kb + CServer::CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events) : m_events(events), m_mock(false), @@ -177,6 +185,10 @@ CServer::CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen this, new TMethodEventJob(this, &CServer::handleFileChunkSendingEvent)); + m_events->adoptHandler(m_events->forIScreen().fileRecieveComplete(), + this, + new TMethodEventJob(this, + &CServer::handleFileRecieveCompleteEvent)); // add connection addClient(m_primaryClient); @@ -1516,8 +1528,13 @@ CServer::handleFakeInputEndEvent(const CEvent&, void*) void CServer::handleFileChunkSendingEvent(const CEvent& event, void*) { - UInt8* data = reinterpret_cast(event.getData()); - onFileChunkSending(data); + onFileChunkSending(event.getData()); +} + +void +CServer::handleFileRecieveCompleteEvent(const CEvent& event, void*) +{ + onFileRecieveComplete(); } void @@ -1981,13 +1998,32 @@ CServer::onGameDeviceTimingReq() } void -CServer::onFileChunkSending(const UInt8* data) +CServer::onFileChunkSending(const void* data) { + CFileChunker::CFileChunk* fileChunk = reinterpret_cast(const_cast(data)); + LOG((CLOG_DEBUG1 "onFileChunkSending")); assert(m_active != NULL); // relay - m_active->fileChunkSending(data[0], &data[1]); + m_active->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize); +} + +void +CServer::onFileRecieveComplete() +{ + if (isReceivedFileSizeValid()) { + if (!m_fileTransferDes.empty()) { + std::fstream file; + file.open(m_fileTransferDes.c_str(), std::ios::out | std::ios::binary); + if (!file.is_open()) { + // TODO: file open failed + } + + file.write(m_receivedFileData.c_str(), m_receivedFileData.size()); + file.close(); + } + } } bool @@ -2260,3 +2296,49 @@ CServer::CKeyboardBroadcastInfo::alloc(State state, const CString& screens) strcpy(info->m_screens, screens.c_str()); return info; } + +void +CServer::clearReceivedFileData() +{ + m_receivedFileData.clear(); +} + +void +CServer::setExpectedFileSize(CString data) +{ + std::istringstream iss(data); + iss >> m_expectedFileSize; +} + +void +CServer::fileChunkReceived(CString data) +{ + m_receivedFileData += data; +} + +bool +CServer::isReceivedFileSizeValid() +{ + return m_expectedFileSize == m_receivedFileData.size(); +} + +void +CServer::sendFileToClient(const char* filename) +{ + CThread* thread = new CThread( + new TMethodJob( + this, &CServer::sendFileThread, + reinterpret_cast(const_cast(filename)))); +} + +void +CServer::sendFileThread(void* filename) +{ + try { + char* name = reinterpret_cast(filename); + CFileChunker::sendFileChunks(name, m_events, this); + } + catch (std::runtime_error error) { + LOG((CLOG_ERR "failed sending file chunks: %s", error.what())); + } +} diff --git a/src/lib/server/CServer.h b/src/lib/server/CServer.h index 91258924..c848f317 100644 --- a/src/lib/server/CServer.h +++ b/src/lib/server/CServer.h @@ -144,6 +144,21 @@ public: //! Notify of game device feedback void gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2); + //! Clears the file buffer + void clearReceivedFileData(); + + //! Set the expected size of receiving file + void setExpectedFileSize(CString data); + + //! Set + void setFileTransferDes(CString& des) { m_fileTransferDes = des; } + + //! Received a chunk of file data + void fileChunkReceived(CString data); + + //! Create a new thread and use it to send file to client + void sendFileToClient(const char* filename); + //@} //! @name accessors //@{ @@ -159,6 +174,9 @@ public: Set the \c list to the names of the currently connected clients. */ void getClients(std::vector& list) const; + + //! Return true if recieved file size is valid + bool isReceivedFileSizeValid(); //@} @@ -299,6 +317,7 @@ private: void handleFakeInputBeginEvent(const CEvent&, void*); void handleFakeInputEndEvent(const CEvent&, void*); void handleFileChunkSendingEvent(const CEvent&, void*); + void handleFileRecieveCompleteEvent(const CEvent&, void*); // event processing void onClipboardChanged(CBaseClientProxy* sender, @@ -318,7 +337,8 @@ private: void onGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2); void onGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2); void onGameDeviceTimingReq(); - void onFileChunkSending(const UInt8* data); + void onFileChunkSending(const void* data); + void onFileRecieveComplete(); // add client to list and attach event handlers for client bool addClient(CBaseClientProxy*); @@ -343,6 +363,9 @@ private: // force the cursor off of \p client void forceLeaveClient(CBaseClientProxy* client); + // thread funciton for sending file + void sendFileThread(void*); + public: bool m_mock; @@ -438,6 +461,13 @@ private: CScreen* m_screen; IEventQueue* m_events; + + // file transfer + size_t m_expectedFileSize; + CString m_receivedFileData; + static const size_t m_chunkSize; + CString m_fileTransferSrc; + CString m_fileTransferDes; }; #endif diff --git a/src/lib/synergy/CApp.cpp b/src/lib/synergy/CApp.cpp index a8f3cc8e..b86d276a 100644 --- a/src/lib/synergy/CApp.cpp +++ b/src/lib/synergy/CApp.cpp @@ -170,6 +170,14 @@ CApp::parseArg(const int& argc, const char* const* argv, int& i) else if (isArg(i, argc, argv, NULL, "--crypto-mode")) { argsBase().m_crypto.setMode(argv[++i]); } + + else if (isArg(i, argc, argv, NULL, "--filetransfer-src")) { + m_fileTransferSrc = argv[++i]; + } + + else if (isArg(i, argc, argv, NULL, "--filetransfer-des")) { + m_fileTransferDes = argv[++i]; + } else { // option not supported here diff --git a/src/lib/synergy/CApp.h b/src/lib/synergy/CApp.h index e335ecf3..330fb025 100644 --- a/src/lib/synergy/CApp.h +++ b/src/lib/synergy/CApp.h @@ -101,6 +101,9 @@ public: void setSocketMultiplexer(CSocketMultiplexer* sm) { m_socketMultiplexer = sm; } CSocketMultiplexer* getSocketMultiplexer() const { return m_socketMultiplexer; } + CString& getFileTransferSrc() { return m_fileTransferSrc; } + CString& getFileTransferDes() { return m_fileTransferDes; } + private: void handleIpcMessage(const CEvent&, void*); @@ -122,6 +125,8 @@ private: CIpcClient* m_ipcClient; IEventQueue* m_events; CSocketMultiplexer* m_socketMultiplexer; + CString m_fileTransferSrc; + CString m_fileTransferDes; }; #define BYE "\nTry `%s --help' for more information." diff --git a/src/lib/synergy/CClientApp.cpp b/src/lib/synergy/CClientApp.cpp index e0107dc0..31a19cdd 100644 --- a/src/lib/synergy/CClientApp.cpp +++ b/src/lib/synergy/CClientApp.cpp @@ -349,6 +349,14 @@ CClientApp::handleClientConnected(const CEvent&, void*) LOG((CLOG_NOTE "connected to server")); resetRestartTimeout(); updateStatus(); + + /* + // TODO: remove testing code for relase + CString fileFullDir = getFileTransferSrc(); + if (!fileFullDir.empty()) { + s_client->sendFileToServer(getFileTransferSrc().c_str()); + } + */ } @@ -473,6 +481,7 @@ CClientApp::startClient() #endif s_client->connect(); + updateStatus(); return true; } diff --git a/src/lib/synergy/CFileChunker.cpp b/src/lib/synergy/CFileChunker.cpp new file mode 100644 index 00000000..e65ea7b9 --- /dev/null +++ b/src/lib/synergy/CFileChunker.cpp @@ -0,0 +1,100 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * 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 "CFileChunker.h" +#include "BasicTypes.h" +#include "ProtocolTypes.h" +#include "CEvent.h" +#include "IEventQueue.h" +#include "CEventTypes.h" +#include "CLOG.h" +#include +#include + +using namespace std; + +const size_t CFileChunker::m_chunkSize = 512 * 1024; // 512kb + +void +CFileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarget) +{ + std::fstream file(reinterpret_cast(filename), std::ios::in | std::ios::binary); + + if (!file.is_open()) { + throw runtime_error("failed to open file"); + } + + // check file size + file.seekg (0, std::ios::end); + size_t size = (size_t)file.tellg(); + + // send first message (file size) + CString fileSize = intToString(size); + UInt32 sizeLength = fileSize.size(); + CFileChunk* sizeMessage = new CFileChunk(sizeLength + 2); + char* chunkData = sizeMessage->m_chunk; + + chunkData[0] = kFileStart; + memcpy(&chunkData[1], fileSize.c_str(), sizeLength); + chunkData[sizeLength + 1] = '\0'; + events->addEvent(CEvent(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage)); + + // send chunk messages with a fixed chunk size + size_t sentLength = 0; + size_t chunkSize = m_chunkSize; + file.seekg (0, std::ios::beg); + while (true) { + // make sure we don't read too much from the mock data. + if (sentLength + chunkSize > size) { + chunkSize = size - sentLength; + } + + // for fileChunk->m_chunk, the first byte is the chunk mark, last is \0 + CFileChunk* fileChunk = new CFileChunk(chunkSize + 2); + char* chunkData = fileChunk->m_chunk; + + chunkData[0] = kFileChunk; + file.read(&chunkData[1], chunkSize); + chunkData[chunkSize + 1] = '\0'; + events->addEvent(CEvent(events->forIScreen().fileChunkSending(), eventTarget, fileChunk)); + + sentLength += chunkSize; + file.seekg (sentLength, std::ios::beg); + + if (sentLength == size) { + break; + } + } + + // send last message + CFileChunk* transferFinished = new CFileChunk(2); + chunkData = transferFinished->m_chunk; + + chunkData[0] = kFileEnd; + chunkData[1] = '\0'; + events->addEvent(CEvent(events->forIScreen().fileChunkSending(), eventTarget, transferFinished)); + + file.close(); +} + +CString +CFileChunker::intToString(size_t i) +{ + stringstream ss; + ss << i; + return ss.str(); +} diff --git a/src/lib/synergy/CFileChunker.h b/src/lib/synergy/CFileChunker.h new file mode 100644 index 00000000..0e49c778 --- /dev/null +++ b/src/lib/synergy/CFileChunker.h @@ -0,0 +1,46 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2013 Bolton Software Ltd. + * + * 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 "CString.h" + +class IEventQueue; + +class CFileChunker { +public: + //! FileChunk data + class CFileChunk { + public: + CFileChunk(size_t chunkSize) : m_dataSize(chunkSize - 2) + { + m_chunk = new char[chunkSize]; + } + + ~CFileChunk() { delete[] m_chunk; } + + public: + const size_t m_dataSize; + char* m_chunk; + }; + + static void sendFileChunks(char* filename, IEventQueue* events, void* eventTarget); + static CString intToString(size_t i); + +private: + static const size_t m_chunkSize; +}; diff --git a/src/lib/synergy/CMakeLists.txt b/src/lib/synergy/CMakeLists.txt index 84da6be3..d7b0eba5 100644 --- a/src/lib/synergy/CMakeLists.txt +++ b/src/lib/synergy/CMakeLists.txt @@ -49,6 +49,7 @@ set(inc CArgsBase.h IAppUtil.h CEventGameDevice.h + CFileChunker.h ) set(src @@ -74,23 +75,24 @@ set(src XSynergy.cpp CDaemonApp.cpp CAppUtil.cpp - CArgsBase.cpp - CEventGameDevice.cpp - CGameDevice.cpp -) - -if (WIN32) + CArgsBase.cpp + CEventGameDevice.cpp + CGameDevice.cpp + CFileChunker.cpp +) + +if (WIN32) list(APPEND inc CAppUtilWindows.h CGameDevice.h ) - list(APPEND src - ${inc} - CAppUtilWindows.cpp - ) -elseif(UNIX) - list(APPEND src + list(APPEND src + ${inc} + CAppUtilWindows.cpp + ) +elseif(UNIX) + list(APPEND src CAppUtilUnix.cpp ) endif() diff --git a/src/lib/synergy/CProtocolUtil.cpp b/src/lib/synergy/CProtocolUtil.cpp index 3445bebc..dd171230 100644 --- a/src/lib/synergy/CProtocolUtil.cpp +++ b/src/lib/synergy/CProtocolUtil.cpp @@ -228,7 +228,12 @@ CProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) } throw; } - LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); + + // don't cause buffer overrun, using +100 chars in case + // someone modifies this log message in future. + if (len + 100 < kLogMessageLength) { + LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); + } // save the data CString* dst = va_arg(args, CString*); diff --git a/src/lib/synergy/CServerApp.cpp b/src/lib/synergy/CServerApp.cpp index 239dd946..9421bad2 100644 --- a/src/lib/synergy/CServerApp.cpp +++ b/src/lib/synergy/CServerApp.cpp @@ -688,7 +688,7 @@ CServer* CServerApp::openServer(CConfig& config, CPrimaryClient* primaryClient) { CServer* server = new CServer(config, primaryClient, s_serverScreen, m_events); - + server->setFileTransferDes(getFileTransferDes()); try { m_events->adoptHandler( m_events->forCServer().disconnected(), server, diff --git a/src/lib/synergy/IPrimaryScreen.h b/src/lib/synergy/IPrimaryScreen.h index 586897ae..a8aa830e 100644 --- a/src/lib/synergy/IPrimaryScreen.h +++ b/src/lib/synergy/IPrimaryScreen.h @@ -31,7 +31,7 @@ This interface defines the methods common to all platform dependent primary screen implementations. */ -class IPrimaryScreen : public IInterface { +class IPrimaryScreen : public IInterface { public: //! Button event data class CButtonInfo { diff --git a/src/lib/synergy/IScreen.h b/src/lib/synergy/IScreen.h index 361b3400..30a4c3bb 100644 --- a/src/lib/synergy/IScreen.h +++ b/src/lib/synergy/IScreen.h @@ -30,7 +30,7 @@ class IClipboard; /*! This interface defines the methods common to all screens. */ -class IScreen : public IInterface { +class IScreen : public IInterface { public: struct CClipboardInfo { public: diff --git a/src/lib/synergy/ProtocolTypes.h b/src/lib/synergy/ProtocolTypes.h index bff4b701..85ebe960 100644 --- a/src/lib/synergy/ProtocolTypes.h +++ b/src/lib/synergy/ProtocolTypes.h @@ -68,6 +68,13 @@ enum EDirectionMask { kBottomMask = 1 << kBottom }; +// file transfer constants +enum EFileTransfer { + kFileStart = 1, + kFileChunk = 2, + kFileEnd = 3 +}; + // // message codes (trailing NUL is not part of code). in comments, $n diff --git a/src/micro/uSynergy.h b/src/micro/uSynergy.h index 18a17162..ef1efaa6 100644 --- a/src/micro/uSynergy.h +++ b/src/micro/uSynergy.h @@ -1,420 +1,420 @@ -/* -uSynergy client -- Interface for the embedded Synergy client library - version 1.0.0, July 7th, 2012 - +/* +uSynergy client -- Interface for the embedded Synergy client library + version 1.0.0, July 7th, 2012 + Copyright (C) 2012 Bolton Software Ltd. -Copyright (c) 2012 Alex Evans - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ -#include - -#ifdef __cplusplus -extern "C" { -#endif - - - -//--------------------------------------------------------------------------------------------------------------------- -// Configuration -//--------------------------------------------------------------------------------------------------------------------- - - - -/** -@brief Determine endianness -**/ -#if defined(USYNERGY_LITTLE_ENDIAN) && defined(USYNERGY_BIG_ENDIAN) - /* Ambiguous: both endians specified */ - #error "Can't define both USYNERGY_LITTLE_ENDIAN and USYNERGY_BIG_ENDIAN" -#elif !defined(USYNERGY_LITTLE_ENDIAN) && !defined(USYNERGY_BIG_ENDIAN) - /* Attempt to auto detect */ - #if defined(__LITTLE_ENDIAN__) || defined(LITTLE_ENDIAN) || (_BYTE_ORDER == _LITTLE_ENDIAN) - #define USYNERGY_LITTLE_ENDIAN - #elif defined(__BIG_ENDIAN__) || defined(BIG_ENDIAN) || (_BYTE_ORDER == _BIG_ENDIAN) - #define USYNERGY_BIG_ENDIAN - #else - #error "Can't detect endian-nes, please defined either USYNERGY_LITTLE_ENDIAN or USYNERGY_BIG_ENDIAN"; - #endif -#else - /* User-specified endian-nes, nothing to do for us */ -#endif - - - -//--------------------------------------------------------------------------------------------------------------------- -// Types and Constants -//--------------------------------------------------------------------------------------------------------------------- - - - -/** -@brief Boolean type -**/ -typedef int uSynergyBool; -#define USYNERGY_FALSE 0 /* False value */ -#define USYNERGY_TRUE 1 /* True value */ - - -/** -@brief User context type - -The uSynergyCookie type is an opaque type that is used by uSynergy to communicate to the client. It is passed along to -callback functions as context. -**/ -typedef struct { int ignored; } * uSynergyCookie; - - - -/** -@brief Clipboard types -**/ -enum uSynergyClipboardFormat -{ - USYNERGY_CLIPBOARD_FORMAT_TEXT = 0, /* Text format, UTF-8, newline is LF */ - USYNERGY_CLIPBOARD_FORMAT_BITMAP = 1, /* Bitmap format, BMP 24/32bpp, BI_RGB */ - USYNERGY_CLIPBOARD_FORMAT_HTML = 2, /* HTML format, HTML fragment, UTF-8, newline is LF */ -}; - - - -/** -@brief Constants and limits -**/ -#define USYNERGY_NUM_JOYSTICKS 4 /* Maximum number of supported joysticks */ - -#define USYNERGY_PROTOCOL_MAJOR 1 /* Major protocol version */ -#define USYNERGY_PROTOCOL_MINOR 4 /* Minor protocol version */ - -#define USYNERGY_IDLE_TIMEOUT 2000 /* Timeout in milliseconds before reconnecting */ - -#define USYNERGY_TRACE_BUFFER_SIZE 1024 /* Maximum length of traced message */ -#define USYNERGY_REPLY_BUFFER_SIZE 1024 /* Maximum size of a reply packet */ -#define USYNERGY_RECEIVE_BUFFER_SIZE 4096 /* Maximum size of an incoming packet */ - - - -/** -@brief Keyboard constants -**/ -#define USYNERGY_MODIFIER_SHIFT 0x0001 /* Shift key modifier */ -#define USYNERGY_MODIFIER_CTRL 0x0002 /* Ctrl key modifier */ -#define USYNERGY_MODIFIER_ALT 0x0004 /* Alt key modifier */ +Copyright (c) 2012 Alex Evans + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +//--------------------------------------------------------------------------------------------------------------------- +// Configuration +//--------------------------------------------------------------------------------------------------------------------- + + + +/** +@brief Determine endianness +**/ +#if defined(USYNERGY_LITTLE_ENDIAN) && defined(USYNERGY_BIG_ENDIAN) + /* Ambiguous: both endians specified */ + #error "Can't define both USYNERGY_LITTLE_ENDIAN and USYNERGY_BIG_ENDIAN" +#elif !defined(USYNERGY_LITTLE_ENDIAN) && !defined(USYNERGY_BIG_ENDIAN) + /* Attempt to auto detect */ + #if defined(__LITTLE_ENDIAN__) || defined(LITTLE_ENDIAN) || (_BYTE_ORDER == _LITTLE_ENDIAN) + #define USYNERGY_LITTLE_ENDIAN + #elif defined(__BIG_ENDIAN__) || defined(BIG_ENDIAN) || (_BYTE_ORDER == _BIG_ENDIAN) + #define USYNERGY_BIG_ENDIAN + #else + #error "Can't detect endian-nes, please defined either USYNERGY_LITTLE_ENDIAN or USYNERGY_BIG_ENDIAN"; + #endif +#else + /* User-specified endian-nes, nothing to do for us */ +#endif + + + +//--------------------------------------------------------------------------------------------------------------------- +// Types and Constants +//--------------------------------------------------------------------------------------------------------------------- + + + +/** +@brief Boolean type +**/ +typedef int uSynergyBool; +#define USYNERGY_FALSE 0 /* False value */ +#define USYNERGY_TRUE 1 /* True value */ + + +/** +@brief User context type + +The uSynergyCookie type is an opaque type that is used by uSynergy to communicate to the client. It is passed along to +callback functions as context. +**/ +typedef struct { int ignored; } * uSynergyCookie; + + + +/** +@brief Clipboard types +**/ +enum uSynergyClipboardFormat +{ + USYNERGY_CLIPBOARD_FORMAT_TEXT = 0, /* Text format, UTF-8, newline is LF */ + USYNERGY_CLIPBOARD_FORMAT_BITMAP = 1, /* Bitmap format, BMP 24/32bpp, BI_RGB */ + USYNERGY_CLIPBOARD_FORMAT_HTML = 2, /* HTML format, HTML fragment, UTF-8, newline is LF */ +}; + + + +/** +@brief Constants and limits +**/ +#define USYNERGY_NUM_JOYSTICKS 4 /* Maximum number of supported joysticks */ + +#define USYNERGY_PROTOCOL_MAJOR 1 /* Major protocol version */ +#define USYNERGY_PROTOCOL_MINOR 4 /* Minor protocol version */ + +#define USYNERGY_IDLE_TIMEOUT 2000 /* Timeout in milliseconds before reconnecting */ + +#define USYNERGY_TRACE_BUFFER_SIZE 1024 /* Maximum length of traced message */ +#define USYNERGY_REPLY_BUFFER_SIZE 1024 /* Maximum size of a reply packet */ +#define USYNERGY_RECEIVE_BUFFER_SIZE 4096 /* Maximum size of an incoming packet */ + + + +/** +@brief Keyboard constants +**/ +#define USYNERGY_MODIFIER_SHIFT 0x0001 /* Shift key modifier */ +#define USYNERGY_MODIFIER_CTRL 0x0002 /* Ctrl key modifier */ +#define USYNERGY_MODIFIER_ALT 0x0004 /* Alt key modifier */ #define USYNERGY_MODIFIER_META 0x0008 /* Meta key modifier */ -#define USYNERGY_MODIFIER_WIN 0x0010 /* Windows key modifier */ +#define USYNERGY_MODIFIER_WIN 0x0010 /* Windows key modifier */ #define USYNERGY_MODIFIER_ALT_GR 0x0020 /* AltGr key modifier */ #define USYNERGY_MODIFIER_LEVEL5LOCK 0x0040 /* Level5Lock key modifier */ #define USYNERGY_MODIFIER_CAPSLOCK 0x1000 /* CapsLock key modifier */ #define USYNERGY_MODIFIER_NUMLOCK 0x2000 /* NumLock key modifier */ #define USYNERGY_MODIFIER_SCROLLOCK 0x4000 /* ScrollLock key modifier */ - - - -//--------------------------------------------------------------------------------------------------------------------- -// Functions and Callbacks -//--------------------------------------------------------------------------------------------------------------------- - - - -/** -@brief Connect function - -This function is called when uSynergy needs to connect to the host. It doesn't imply a network implementation or -destination address, that must all be handled on the user side. The function should return USYNERGY_TRUE if a -connection was established or USYNERGY_FALSE if it could not connect. - -When network errors occur (e.g. uSynergySend or uSynergyReceive fail) then the connect call will be called again -so the implementation of the function must close any old connections and clean up resources before retrying. - -@param cookie Cookie supplied in the Synergy context -**/ -typedef uSynergyBool (*uSynergyConnectFunc)(uSynergyCookie cookie); - - - -/** -@brief Send function - -This function is called when uSynergy needs to send something over the default connection. It should return -USYNERGY_TRUE if sending succeeded and USYNERGY_FALSE otherwise. This function should block until the send -operation is completed. - -@param cookie Cookie supplied in the Synergy context -@param buffer Address of buffer to send -@param length Length of buffer to send -**/ -typedef uSynergyBool (*uSynergySendFunc)(uSynergyCookie cookie, const uint8_t *buffer, int length); - - - -/** -@brief Receive function - -This function is called when uSynergy needs to receive data from the default connection. It should return -USYNERGY_TRUE if receiving data succeeded and USYNERGY_FALSE otherwise. This function should block until data -has been received and wait for data to become available. If @a outLength is set to 0 upon completion it is -assumed that the connection is alive, but still in a connecting state and needs time to settle. - -@param cookie Cookie supplied in the Synergy context -@param buffer Address of buffer to receive data into -@param maxLength Maximum amount of bytes to write into the receive buffer -@param outLength Address of integer that receives the actual amount of bytes written into @a buffer -**/ -typedef uSynergyBool (*uSynergyReceiveFunc)(uSynergyCookie cookie, uint8_t *buffer, int maxLength, int* outLength); - - - -/** -@brief Thread sleep function - -This function is called when uSynergy wants to suspend operation for a while before retrying an operation. It -is mostly used when a socket times out or disconnect occurs to prevent uSynergy from continuously hammering a -network connection in case the network is down. - -@param cookie Cookie supplied in the Synergy context -@param timeMs Time to sleep the current thread (in milliseconds) -**/ -typedef void (*uSynergySleepFunc)(uSynergyCookie cookie, int timeMs); - - - -/** -@brief Get time function - -This function is called when uSynergy needs to know the current time. This is used to determine when timeouts -have occured. The time base should be a cyclic millisecond time value. - -@returns Time value in milliseconds -**/ -typedef uint32_t (*uSynergyGetTimeFunc)(); - - - -/** -@brief Trace function - -This function is called when uSynergy wants to trace something. It is optional to show these messages, but they -are often useful when debugging. uSynergy only traces major events like connecting and disconnecting. Usually -only a single trace is shown when the connection is established and no more trace are called. - -@param cookie Cookie supplied in the Synergy context -@param text Text to be traced -**/ -typedef void (*uSynergyTraceFunc)(uSynergyCookie cookie, const char *text); - - - -/** -@brief Screen active callback - -This callback is called when Synergy makes the screen active or inactive. This -callback is usually sent when the mouse enters or leaves the screen. - -@param cookie Cookie supplied in the Synergy context -@param active Activation flag, 1 if the screen has become active, 0 if the screen has become inactive -**/ -typedef void (*uSynergyScreenActiveCallback)(uSynergyCookie cookie, uSynergyBool active); - - - -/** -@brief Mouse callback - -This callback is called when a mouse events happens. The mouse X and Y position, -wheel and button state is communicated in the message. It's up to the user to -interpret if this is a mouse up, down, double-click or other message. - -@param cookie Cookie supplied in the Synergy context -@param x Mouse X position -@param y Mouse Y position -@param wheelX Mouse wheel X position -@param wheelY Mouse wheel Y position -@param buttonLeft Left button pressed status, 0 for released, 1 for pressed -@param buttonMiddle Middle button pressed status, 0 for released, 1 for pressed -@param buttonRight Right button pressed status, 0 for released, 1 for pressed -**/ -typedef void (*uSynergyMouseCallback)(uSynergyCookie cookie, uint16_t x, uint16_t y, int16_t wheelX, int16_t wheelY, uSynergyBool buttonLeft, uSynergyBool buttonRight, uSynergyBool buttonMiddle); - - - -/** -@brief Key event callback - -This callback is called when a key is pressed or released. - -@param cookie Cookie supplied in the Synergy context -@param key Key code of key that was pressed or released -@param modifiers Status of modifier keys (alt, shift, etc.) -@param down Down or up status, 1 is key is pressed down, 0 if key is released (up) -@param repeat Repeat flag, 1 if the key is down because the key is repeating, 0 if the key is initially pressed by the user -**/ -typedef void (*uSynergyKeyboardCallback)(uSynergyCookie cookie, uint16_t key, uint16_t modifiers, uSynergyBool down, uSynergyBool repeat); - - - -/** -@brief Joystick event callback - -This callback is called when a joystick stick or button changes. It is possible that multiple callbacks are -fired when different sticks or buttons change as these are individual messages in the packet stream. Each -callback will contain all the valid state for the different axes and buttons. The last callback received will -represent the most current joystick state. - -@param cookie Cookie supplied in the Synergy context -@param joyNum Joystick number, always in the range [0 ... USYNERGY_NUM_JOYSTICKS> -@param buttons Button pressed mask -@param leftStickX Left stick X position, in range [-127 ... 127] -@param leftStickY Left stick Y position, in range [-127 ... 127] -@param rightStickX Right stick X position, in range [-127 ... 127] -@param rightStickY Right stick Y position, in range [-127 ... 127] -**/ -typedef void (*uSynergyJoystickCallback)(uSynergyCookie cookie, uint8_t joyNum, uint16_t buttons, int8_t leftStickX, int8_t leftStickY, int8_t rightStickX, int8_t rightStickY); - - - -/** -@brief Clipboard event callback - -This callback is called when something is placed on the clipboard. Multiple callbacks may be fired for -multiple clipboard formats if they are supported. The data provided is read-only and may not be modified -by the application. - -@param cookie Cookie supplied in the Synergy context -@param format Clipboard format -@param data Memory area containing the clipboard raw data -@param size Size of clipboard data -**/ -typedef void (*uSynergyClipboardCallback)(uSynergyCookie cookie, enum uSynergyClipboardFormat format, const uint8_t *data, uint32_t size); - - - -//--------------------------------------------------------------------------------------------------------------------- -// Context -//--------------------------------------------------------------------------------------------------------------------- - - - -/** -@brief uSynergy context -**/ -typedef struct -{ - /* Mandatory configuration data, filled in by client */ - uSynergyConnectFunc m_connectFunc; /* Connect function */ - uSynergySendFunc m_sendFunc; /* Send data function */ - uSynergyReceiveFunc m_receiveFunc; /* Receive data function */ - uSynergySleepFunc m_sleepFunc; /* Thread sleep function */ - uSynergyGetTimeFunc m_getTimeFunc; /* Get current time function */ - const char* m_clientName; /* Name of Synergy Screen / Client */ - uint16_t m_clientWidth; /* Width of screen */ - uint16_t m_clientHeight; /* Height of screen */ - - /* Optional configuration data, filled in by client */ - uSynergyCookie m_cookie; /* Cookie pointer passed to callback functions (can be NULL) */ - uSynergyTraceFunc m_traceFunc; /* Function for tracing status (can be NULL) */ - uSynergyScreenActiveCallback m_screenActiveCallback; /* Callback for entering and leaving screen */ - uSynergyMouseCallback m_mouseCallback; /* Callback for mouse events */ - uSynergyKeyboardCallback m_keyboardCallback; /* Callback for keyboard events */ - uSynergyJoystickCallback m_joystickCallback; /* Callback for joystick events */ - uSynergyClipboardCallback m_clipboardCallback; /* Callback for clipboard events */ - - /* State data, used internall by client, initialized by uSynergyInit() */ - uSynergyBool m_connected; /* Is our socket connected? */ - uSynergyBool m_hasReceivedHello; /* Have we received a 'Hello' from the server? */ - uSynergyBool m_isCaptured; /* Is Synergy active (i.e. this client is receiving input messages?) */ - uint32_t m_lastMessageTime; /* Time at which last message was received */ - uint32_t m_sequenceNumber; /* Packet sequence number */ - uint8_t m_receiveBuffer[USYNERGY_RECEIVE_BUFFER_SIZE]; /* Receive buffer */ - int m_receiveOfs; /* Receive buffer offset */ - uint8_t m_replyBuffer[USYNERGY_REPLY_BUFFER_SIZE]; /* Reply buffer */ - uint8_t* m_replyCur; /* Write offset into reply buffer */ - uint16_t m_mouseX; /* Mouse X position */ - uint16_t m_mouseY; /* Mouse Y position */ - int16_t m_mouseWheelX; /* Mouse wheel X position */ - int16_t m_mouseWheelY; /* Mouse wheel Y position */ - uSynergyBool m_mouseButtonLeft; /* Mouse left button */ - uSynergyBool m_mouseButtonRight; /* Mouse right button */ - uSynergyBool m_mouseButtonMiddle; /* Mouse middle button */ - int8_t m_joystickSticks[USYNERGY_NUM_JOYSTICKS][4]; /* Joystick stick position in 2 axes for 2 sticks */ - uint16_t m_joystickButtons[USYNERGY_NUM_JOYSTICKS]; /* Joystick button state */ -} uSynergyContext; - - - -//--------------------------------------------------------------------------------------------------------------------- -// Interface -//--------------------------------------------------------------------------------------------------------------------- - - - -/** -@brief Initialize uSynergy context - -This function initializes @a context for use. Call this function directly after -creating the context, before filling in any configuration data in it. Not calling -this function will cause undefined behavior. - -@param context Context to be initialized -**/ -extern void uSynergyInit(uSynergyContext *context); - - - -/** -@brief Update uSynergy - -This function updates uSynergy and does the bulk of the work. It does connection management, -receiving data, reconnecting after errors or timeouts and so on. It assumes that networking -operations are blocking and it can suspend the current thread if it needs to wait. It is -best practice to call uSynergyUpdate from a background thread so it is responsive. - -Because uSynergy relies mostly on blocking calls it will mostly stay in thread sleep state -waiting for system mutexes and won't eat much memory. - -uSynergyUpdate doesn't do any memory allocations or have any side effects beyond those of -the callbacks it calls. - -@param context Context to be updated -**/ -extern void uSynergyUpdate(uSynergyContext *context); - - - -/** -@brief Send clipboard data - -This function sets new clipboard data and sends it to the server. Use this function if -your client cuts or copies data onto the clipboard that it needs to share with the -server. - -Currently there is only support for plaintext, but HTML and image data could be -supported with some effort. - -@param context Context to send clipboard data to -@param text Text to set to the clipboard -**/ -extern void uSynergySendClipboard(uSynergyContext *context, const char *text); - - - -#ifdef __cplusplus -}; -#endif + + + +//--------------------------------------------------------------------------------------------------------------------- +// Functions and Callbacks +//--------------------------------------------------------------------------------------------------------------------- + + + +/** +@brief Connect function + +This function is called when uSynergy needs to connect to the host. It doesn't imply a network implementation or +destination address, that must all be handled on the user side. The function should return USYNERGY_TRUE if a +connection was established or USYNERGY_FALSE if it could not connect. + +When network errors occur (e.g. uSynergySend or uSynergyReceive fail) then the connect call will be called again +so the implementation of the function must close any old connections and clean up resources before retrying. + +@param cookie Cookie supplied in the Synergy context +**/ +typedef uSynergyBool (*uSynergyConnectFunc)(uSynergyCookie cookie); + + + +/** +@brief Send function + +This function is called when uSynergy needs to send something over the default connection. It should return +USYNERGY_TRUE if sending succeeded and USYNERGY_FALSE otherwise. This function should block until the send +operation is completed. + +@param cookie Cookie supplied in the Synergy context +@param buffer Address of buffer to send +@param length Length of buffer to send +**/ +typedef uSynergyBool (*uSynergySendFunc)(uSynergyCookie cookie, const uint8_t *buffer, int length); + + + +/** +@brief Receive function + +This function is called when uSynergy needs to receive data from the default connection. It should return +USYNERGY_TRUE if receiving data succeeded and USYNERGY_FALSE otherwise. This function should block until data +has been received and wait for data to become available. If @a outLength is set to 0 upon completion it is +assumed that the connection is alive, but still in a connecting state and needs time to settle. + +@param cookie Cookie supplied in the Synergy context +@param buffer Address of buffer to receive data into +@param maxLength Maximum amount of bytes to write into the receive buffer +@param outLength Address of integer that receives the actual amount of bytes written into @a buffer +**/ +typedef uSynergyBool (*uSynergyReceiveFunc)(uSynergyCookie cookie, uint8_t *buffer, int maxLength, int* outLength); + + + +/** +@brief Thread sleep function + +This function is called when uSynergy wants to suspend operation for a while before retrying an operation. It +is mostly used when a socket times out or disconnect occurs to prevent uSynergy from continuously hammering a +network connection in case the network is down. + +@param cookie Cookie supplied in the Synergy context +@param timeMs Time to sleep the current thread (in milliseconds) +**/ +typedef void (*uSynergySleepFunc)(uSynergyCookie cookie, int timeMs); + + + +/** +@brief Get time function + +This function is called when uSynergy needs to know the current time. This is used to determine when timeouts +have occured. The time base should be a cyclic millisecond time value. + +@returns Time value in milliseconds +**/ +typedef uint32_t (*uSynergyGetTimeFunc)(); + + + +/** +@brief Trace function + +This function is called when uSynergy wants to trace something. It is optional to show these messages, but they +are often useful when debugging. uSynergy only traces major events like connecting and disconnecting. Usually +only a single trace is shown when the connection is established and no more trace are called. + +@param cookie Cookie supplied in the Synergy context +@param text Text to be traced +**/ +typedef void (*uSynergyTraceFunc)(uSynergyCookie cookie, const char *text); + + + +/** +@brief Screen active callback + +This callback is called when Synergy makes the screen active or inactive. This +callback is usually sent when the mouse enters or leaves the screen. + +@param cookie Cookie supplied in the Synergy context +@param active Activation flag, 1 if the screen has become active, 0 if the screen has become inactive +**/ +typedef void (*uSynergyScreenActiveCallback)(uSynergyCookie cookie, uSynergyBool active); + + + +/** +@brief Mouse callback + +This callback is called when a mouse events happens. The mouse X and Y position, +wheel and button state is communicated in the message. It's up to the user to +interpret if this is a mouse up, down, double-click or other message. + +@param cookie Cookie supplied in the Synergy context +@param x Mouse X position +@param y Mouse Y position +@param wheelX Mouse wheel X position +@param wheelY Mouse wheel Y position +@param buttonLeft Left button pressed status, 0 for released, 1 for pressed +@param buttonMiddle Middle button pressed status, 0 for released, 1 for pressed +@param buttonRight Right button pressed status, 0 for released, 1 for pressed +**/ +typedef void (*uSynergyMouseCallback)(uSynergyCookie cookie, uint16_t x, uint16_t y, int16_t wheelX, int16_t wheelY, uSynergyBool buttonLeft, uSynergyBool buttonRight, uSynergyBool buttonMiddle); + + + +/** +@brief Key event callback + +This callback is called when a key is pressed or released. + +@param cookie Cookie supplied in the Synergy context +@param key Key code of key that was pressed or released +@param modifiers Status of modifier keys (alt, shift, etc.) +@param down Down or up status, 1 is key is pressed down, 0 if key is released (up) +@param repeat Repeat flag, 1 if the key is down because the key is repeating, 0 if the key is initially pressed by the user +**/ +typedef void (*uSynergyKeyboardCallback)(uSynergyCookie cookie, uint16_t key, uint16_t modifiers, uSynergyBool down, uSynergyBool repeat); + + + +/** +@brief Joystick event callback + +This callback is called when a joystick stick or button changes. It is possible that multiple callbacks are +fired when different sticks or buttons change as these are individual messages in the packet stream. Each +callback will contain all the valid state for the different axes and buttons. The last callback received will +represent the most current joystick state. + +@param cookie Cookie supplied in the Synergy context +@param joyNum Joystick number, always in the range [0 ... USYNERGY_NUM_JOYSTICKS> +@param buttons Button pressed mask +@param leftStickX Left stick X position, in range [-127 ... 127] +@param leftStickY Left stick Y position, in range [-127 ... 127] +@param rightStickX Right stick X position, in range [-127 ... 127] +@param rightStickY Right stick Y position, in range [-127 ... 127] +**/ +typedef void (*uSynergyJoystickCallback)(uSynergyCookie cookie, uint8_t joyNum, uint16_t buttons, int8_t leftStickX, int8_t leftStickY, int8_t rightStickX, int8_t rightStickY); + + + +/** +@brief Clipboard event callback + +This callback is called when something is placed on the clipboard. Multiple callbacks may be fired for +multiple clipboard formats if they are supported. The data provided is read-only and may not be modified +by the application. + +@param cookie Cookie supplied in the Synergy context +@param format Clipboard format +@param data Memory area containing the clipboard raw data +@param size Size of clipboard data +**/ +typedef void (*uSynergyClipboardCallback)(uSynergyCookie cookie, enum uSynergyClipboardFormat format, const uint8_t *data, uint32_t size); + + + +//--------------------------------------------------------------------------------------------------------------------- +// Context +//--------------------------------------------------------------------------------------------------------------------- + + + +/** +@brief uSynergy context +**/ +typedef struct +{ + /* Mandatory configuration data, filled in by client */ + uSynergyConnectFunc m_connectFunc; /* Connect function */ + uSynergySendFunc m_sendFunc; /* Send data function */ + uSynergyReceiveFunc m_receiveFunc; /* Receive data function */ + uSynergySleepFunc m_sleepFunc; /* Thread sleep function */ + uSynergyGetTimeFunc m_getTimeFunc; /* Get current time function */ + const char* m_clientName; /* Name of Synergy Screen / Client */ + uint16_t m_clientWidth; /* Width of screen */ + uint16_t m_clientHeight; /* Height of screen */ + + /* Optional configuration data, filled in by client */ + uSynergyCookie m_cookie; /* Cookie pointer passed to callback functions (can be NULL) */ + uSynergyTraceFunc m_traceFunc; /* Function for tracing status (can be NULL) */ + uSynergyScreenActiveCallback m_screenActiveCallback; /* Callback for entering and leaving screen */ + uSynergyMouseCallback m_mouseCallback; /* Callback for mouse events */ + uSynergyKeyboardCallback m_keyboardCallback; /* Callback for keyboard events */ + uSynergyJoystickCallback m_joystickCallback; /* Callback for joystick events */ + uSynergyClipboardCallback m_clipboardCallback; /* Callback for clipboard events */ + + /* State data, used internall by client, initialized by uSynergyInit() */ + uSynergyBool m_connected; /* Is our socket connected? */ + uSynergyBool m_hasReceivedHello; /* Have we received a 'Hello' from the server? */ + uSynergyBool m_isCaptured; /* Is Synergy active (i.e. this client is receiving input messages?) */ + uint32_t m_lastMessageTime; /* Time at which last message was received */ + uint32_t m_sequenceNumber; /* Packet sequence number */ + uint8_t m_receiveBuffer[USYNERGY_RECEIVE_BUFFER_SIZE]; /* Receive buffer */ + int m_receiveOfs; /* Receive buffer offset */ + uint8_t m_replyBuffer[USYNERGY_REPLY_BUFFER_SIZE]; /* Reply buffer */ + uint8_t* m_replyCur; /* Write offset into reply buffer */ + uint16_t m_mouseX; /* Mouse X position */ + uint16_t m_mouseY; /* Mouse Y position */ + int16_t m_mouseWheelX; /* Mouse wheel X position */ + int16_t m_mouseWheelY; /* Mouse wheel Y position */ + uSynergyBool m_mouseButtonLeft; /* Mouse left button */ + uSynergyBool m_mouseButtonRight; /* Mouse right button */ + uSynergyBool m_mouseButtonMiddle; /* Mouse middle button */ + int8_t m_joystickSticks[USYNERGY_NUM_JOYSTICKS][4]; /* Joystick stick position in 2 axes for 2 sticks */ + uint16_t m_joystickButtons[USYNERGY_NUM_JOYSTICKS]; /* Joystick button state */ +} uSynergyContext; + + + +//--------------------------------------------------------------------------------------------------------------------- +// Interface +//--------------------------------------------------------------------------------------------------------------------- + + + +/** +@brief Initialize uSynergy context + +This function initializes @a context for use. Call this function directly after +creating the context, before filling in any configuration data in it. Not calling +this function will cause undefined behavior. + +@param context Context to be initialized +**/ +extern void uSynergyInit(uSynergyContext *context); + + + +/** +@brief Update uSynergy + +This function updates uSynergy and does the bulk of the work. It does connection management, +receiving data, reconnecting after errors or timeouts and so on. It assumes that networking +operations are blocking and it can suspend the current thread if it needs to wait. It is +best practice to call uSynergyUpdate from a background thread so it is responsive. + +Because uSynergy relies mostly on blocking calls it will mostly stay in thread sleep state +waiting for system mutexes and won't eat much memory. + +uSynergyUpdate doesn't do any memory allocations or have any side effects beyond those of +the callbacks it calls. + +@param context Context to be updated +**/ +extern void uSynergyUpdate(uSynergyContext *context); + + + +/** +@brief Send clipboard data + +This function sets new clipboard data and sends it to the server. Use this function if +your client cuts or copies data onto the clipboard that it needs to share with the +server. + +Currently there is only support for plaintext, but HTML and image data could be +supported with some effort. + +@param context Context to send clipboard data to +@param text Text to set to the clipboard +**/ +extern void uSynergySendClipboard(uSynergyContext *context, const char *text); + + + +#ifdef __cplusplus +}; +#endif diff --git a/src/plugin/winmmjoy/winmmjoy.cpp b/src/plugin/winmmjoy/winmmjoy.cpp index a1de587a..51e69a56 100644 --- a/src/plugin/winmmjoy/winmmjoy.cpp +++ b/src/plugin/winmmjoy/winmmjoy.cpp @@ -19,15 +19,15 @@ #include "winmmjoy.h" #include -#include +#include #include #pragma comment(lib, "winmm.lib") - -std::stringstream _logStream; -#define LOG(s) \ - _logStream.str(""); \ - _logStream << "winmmjoy: " << s << std::endl; \ + +std::stringstream _logStream; +#define LOG(s) \ + _logStream.str(""); \ + _logStream << "winmmjoy: " << s << std::endl; \ s_log( _logStream.str().c_str()) static bool s_running = true; @@ -64,7 +64,7 @@ mainLoop(void* data) const char* triggersEvent = "IPrimaryScreen::getGameDeviceTriggersEvent"; JOYINFOEX joyInfo; - ZeroMemory(&joyInfo, sizeof(joyInfo)); + ZeroMemory(&joyInfo, sizeof(joyInfo)); joyInfo.dwSize = sizeof(joyInfo); joyInfo.dwFlags = JOY_RETURNALL; diff --git a/src/test/guitests/src/VersionCheckerTests.cpp b/src/test/guitests/src/VersionCheckerTests.cpp index d01fbeec..38bd3804 100644 --- a/src/test/guitests/src/VersionCheckerTests.cpp +++ b/src/test/guitests/src/VersionCheckerTests.cpp @@ -1,47 +1,47 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 "VersionCheckerTests.h" -#include "VersionChecker.cpp" -#include "../../gui/tmp/debug/moc_VersionChecker.cpp" - -#include - -void VersionCheckerTests::compareVersions() -{ - VersionChecker versionChecker; - - // compare majors - QCOMPARE(versionChecker.compareVersions("1.0.0", "2.0.0"), 1); - QCOMPARE(versionChecker.compareVersions("2.0.0", "1.0.0"), -1); - QCOMPARE(versionChecker.compareVersions("1.0.0", "1.0.0"), 0); - QCOMPARE(versionChecker.compareVersions("1.4.8", "2.4.7"), 1); - QCOMPARE(versionChecker.compareVersions("2.4.7", "1.4.8"), -1); - - // compare minors - QCOMPARE(versionChecker.compareVersions("1.3.0", "1.4.0"), 1); - QCOMPARE(versionChecker.compareVersions("1.4.0", "1.3.0"), -1); - QCOMPARE(versionChecker.compareVersions("1.4.0", "1.4.0"), 0); - QCOMPARE(versionChecker.compareVersions("1.3.8", "1.4.7"), 1); - QCOMPARE(versionChecker.compareVersions("1.4.7", "1.3.8"), -1); - - // compare revs - QCOMPARE(versionChecker.compareVersions("1.4.7", "1.4.8"), 1); - QCOMPARE(versionChecker.compareVersions("1.4.8", "1.4.7"), -1); - QCOMPARE(versionChecker.compareVersions("1.4.7", "1.4.7"), 0); -} + * 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 "VersionCheckerTests.h" +#include "VersionChecker.cpp" +#include "../../gui/tmp/debug/moc_VersionChecker.cpp" + +#include + +void VersionCheckerTests::compareVersions() +{ + VersionChecker versionChecker; + + // compare majors + QCOMPARE(versionChecker.compareVersions("1.0.0", "2.0.0"), 1); + QCOMPARE(versionChecker.compareVersions("2.0.0", "1.0.0"), -1); + QCOMPARE(versionChecker.compareVersions("1.0.0", "1.0.0"), 0); + QCOMPARE(versionChecker.compareVersions("1.4.8", "2.4.7"), 1); + QCOMPARE(versionChecker.compareVersions("2.4.7", "1.4.8"), -1); + + // compare minors + QCOMPARE(versionChecker.compareVersions("1.3.0", "1.4.0"), 1); + QCOMPARE(versionChecker.compareVersions("1.4.0", "1.3.0"), -1); + QCOMPARE(versionChecker.compareVersions("1.4.0", "1.4.0"), 0); + QCOMPARE(versionChecker.compareVersions("1.3.8", "1.4.7"), 1); + QCOMPARE(versionChecker.compareVersions("1.4.7", "1.3.8"), -1); + + // compare revs + QCOMPARE(versionChecker.compareVersions("1.4.7", "1.4.8"), 1); + QCOMPARE(versionChecker.compareVersions("1.4.8", "1.4.7"), -1); + QCOMPARE(versionChecker.compareVersions("1.4.7", "1.4.7"), 0); +} diff --git a/src/test/guitests/src/VersionCheckerTests.h b/src/test/guitests/src/VersionCheckerTests.h index 79cb78e4..db6c899d 100644 --- a/src/test/guitests/src/VersionCheckerTests.h +++ b/src/test/guitests/src/VersionCheckerTests.h @@ -1,26 +1,26 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 "QObject.h" - -class VersionCheckerTests : public QObject -{ - Q_OBJECT -private slots: - void compareVersions(); -}; + * 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 "QObject.h" + +class VersionCheckerTests : public QObject +{ + Q_OBJECT +private slots: + void compareVersions(); +}; diff --git a/src/test/guitests/src/main.cpp b/src/test/guitests/src/main.cpp index c466529f..2fd81fe9 100644 --- a/src/test/guitests/src/main.cpp +++ b/src/test/guitests/src/main.cpp @@ -1,26 +1,26 @@ -/* - * synergy -- mouse and keyboard sharing utility +/* + * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Bolton Software Ltd. - * 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 -#include "VersionCheckerTests.h" - -int main(int argc, char *argv[]) -{ - VersionCheckerTests versionCheckerTests; - QTest::qExec(&versionCheckerTests, argc, argv); -} + * 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 +#include "VersionCheckerTests.h" + +int main(int argc, char *argv[]) +{ + VersionCheckerTests versionCheckerTests; + QTest::qExec(&versionCheckerTests, argc, argv); +} diff --git a/src/test/integtests/CTestEventQueue.cpp b/src/test/integtests/CTestEventQueue.cpp index f6cde4da..c92176cf 100644 --- a/src/test/integtests/CTestEventQueue.cpp +++ b/src/test/integtests/CTestEventQueue.cpp @@ -19,6 +19,7 @@ #include "CLog.h" #include "TMethodEventJob.h" #include "CSimpleEventQueueBuffer.h" +#include void CTestEventQueue::raiseQuitEvent() @@ -47,6 +48,5 @@ CTestEventQueue::cleanupQuitTimeout() void CTestEventQueue::handleQuitTimeout(const CEvent&, void* vclient) { - LOG((CLOG_ERR "timeout")); - raiseQuitEvent(); + throw std::runtime_error("test event queue timeout"); } diff --git a/src/test/integtests/NetworkTests.cpp b/src/test/integtests/NetworkTests.cpp index 195ad80c..4b173a13 100644 --- a/src/test/integtests/NetworkTests.cpp +++ b/src/test/integtests/NetworkTests.cpp @@ -16,8 +16,11 @@ */ #include -#include +#include +#include #include +#include +#include #define TEST_ENV @@ -33,42 +36,74 @@ #include "CTCPSocketFactory.h" #include "CCryptoOptions.h" #include "CSocketMultiplexer.h" -#include "CMSWindowsScreen.h" -#include "CGameDevice.h" -#include "CThread.h" -#include "TMethodJob.h" #include "CTestEventQueue.h" #include "server/CMockInputFilter.h" +#include "TMethodJob.h" +#include "CThread.h" +#include "CFileChunker.h" + +using namespace std; using ::testing::_; -using ::testing::NiceMock; +using ::testing::NiceMock; using ::testing::Return; using ::testing::Invoke; #define TEST_PORT 24803 #define TEST_HOST "localhost" -const int klargeDataSize = 512; -char g_largeData[klargeDataSize] = "large data:head.1221412312341244213123fdsfasdawdwadwadacwdd.12321412312341244213123fdsfasdawdwadwadacwdawddawdwacawdawd232141231awddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewedacwdawddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewe12134123njk1u31i2nm3e123hu23oi132213njk.tail"; +const size_t kMockDataSize = 1024 * 1024 * 10; // 10MB +const UInt16 kMockDataChunkIncrement = 1024; // 1KB +const char* kMockFilename = "NetworkTests.mock"; +const size_t kMockFileSize = 1024 * 1024 * 10; // 10MB -void sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h); -void sendFileToClient_getCursorPos(SInt32& x, SInt32& y); +void getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h); +void getCursorPos(SInt32& x, SInt32& y); +CString intToString(size_t i); +UInt8* newMockData(size_t size); +void createFile(fstream& file, const char* filename, size_t size); class NetworkTests : public ::testing::Test { public: - NetworkTests() { } + NetworkTests() : + m_mockData(NULL), + m_mockDataSize(0), + m_mockFileSize(0) + { + m_mockData = newMockData(kMockDataSize); + createFile(m_mockFile, kMockFilename, kMockFileSize); + } - void sendData(CServer* server); + ~NetworkTests() + { + remove(kMockFilename); + delete[] m_mockData; + } + + void sendMockData(void* eventTarget); - void sendFileToClient_handleClientConnected(const CEvent&, void* vlistener); - void sendFileToClient_fileRecieveComplete(const CEvent&, void*); + void sendToClient_mockData_handleClientConnected(const CEvent&, void* vlistener); + void sendToClient_mockData_fileRecieveComplete(const CEvent&, void*); + + void sendToClient_mockFile_handleClientConnected(const CEvent&, void* vlistener); + void sendToClient_mockFile_fileRecieveComplete(const CEvent& event, void*); + + void sendToServer_mockData_handleClientConnected(const CEvent&, void* vlistener); + void sendToServer_mockData_fileRecieveComplete(const CEvent& event, void*); + void sendToServer_mockFile_handleClientConnected(const CEvent&, void* vlistener); + void sendToServer_mockFile_fileRecieveComplete(const CEvent& event, void*); + public: CTestEventQueue m_events; + UInt8* m_mockData; + size_t m_mockDataSize; + fstream m_mockFile; + size_t m_mockFileSize; }; -TEST_F(NetworkTests, sendFileToClient) +TEST_F(NetworkTests, sendToClient_mockData) { // server and client CNetworkAddress serverAddress(TEST_HOST, TEST_PORT); @@ -88,7 +123,7 @@ TEST_F(NetworkTests, sendFileToClient) m_events.adoptHandler( m_events.forCClientListener().connected(), &listener, new TMethodEventJob( - this, &NetworkTests::sendFileToClient_handleClientConnected, &listener)); + this, &NetworkTests::sendToClient_mockData_handleClientConnected, &listener)); ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); @@ -102,43 +137,196 @@ TEST_F(NetworkTests, sendFileToClient) CSocketMultiplexer clientSocketMultiplexer; CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer); - ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(sendFileToClient_getShape)); - ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(sendFileToClient_getCursorPos)); + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); + ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions); m_events.adoptHandler( m_events.forIScreen().fileRecieveComplete(), &client, new TMethodEventJob( - this, &NetworkTests::sendFileToClient_fileRecieveComplete)); + this, &NetworkTests::sendToClient_mockData_fileRecieveComplete)); client.connect(); - m_events.initQuitTimeout(10); + m_events.initQuitTimeout(5); + m_events.loop(); + m_events.cleanupQuitTimeout(); +} + +TEST_F(NetworkTests, sendToClient_mockFile) +{ + // server and client + CNetworkAddress serverAddress(TEST_HOST, TEST_PORT); + CCryptoOptions cryptoOptions; + + serverAddress.resolve(); + + // server + CSocketMultiplexer serverSocketMultiplexer; + CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer); + CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events); + NiceMock serverScreen; + NiceMock primaryClient; + NiceMock serverConfig; + NiceMock serverInputFilter; + + m_events.adoptHandler( + m_events.forCClientListener().connected(), &listener, + new TMethodEventJob( + this, &NetworkTests::sendToClient_mockFile_handleClientConnected, &listener)); + + ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); + ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); + + CServer server(serverConfig, &primaryClient, &serverScreen, &m_events); + server.m_mock = true; + listener.setServer(&server); + + // client + NiceMock clientScreen; + CSocketMultiplexer clientSocketMultiplexer; + CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer); + + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); + ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); + + CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions); + + m_events.adoptHandler( + m_events.forIScreen().fileRecieveComplete(), &client, + new TMethodEventJob( + this, &NetworkTests::sendToClient_mockFile_fileRecieveComplete)); + + client.connect(); + + m_events.initQuitTimeout(5); + m_events.loop(); + m_events.cleanupQuitTimeout(); +} + +TEST_F(NetworkTests, sendToServer_mockData) +{ + // server and client + CNetworkAddress serverAddress(TEST_HOST, TEST_PORT); + CCryptoOptions cryptoOptions; + + serverAddress.resolve(); + + // server + CSocketMultiplexer serverSocketMultiplexer; + CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer); + CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events); + NiceMock serverScreen; + NiceMock primaryClient; + NiceMock serverConfig; + NiceMock serverInputFilter; + + ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); + ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); + + CServer server(serverConfig, &primaryClient, &serverScreen, &m_events); + server.m_mock = true; + listener.setServer(&server); + + // client + NiceMock clientScreen; + CSocketMultiplexer clientSocketMultiplexer; + CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer); + + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); + ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); + + CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions); + + m_events.adoptHandler( + m_events.forCClientListener().connected(), &listener, + new TMethodEventJob( + this, &NetworkTests::sendToServer_mockData_handleClientConnected, &client)); + + m_events.adoptHandler( + m_events.forIScreen().fileRecieveComplete(), &server, + new TMethodEventJob( + this, &NetworkTests::sendToServer_mockData_fileRecieveComplete)); + + client.connect(); + + m_events.initQuitTimeout(5); + m_events.loop(); + m_events.cleanupQuitTimeout(); +} + +TEST_F(NetworkTests, sendToServer_mockFile) +{ + // server and client + CNetworkAddress serverAddress(TEST_HOST, TEST_PORT); + CCryptoOptions cryptoOptions; + + serverAddress.resolve(); + + // server + CSocketMultiplexer serverSocketMultiplexer; + CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer); + CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events); + NiceMock serverScreen; + NiceMock primaryClient; + NiceMock serverConfig; + NiceMock serverInputFilter; + + ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); + ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); + + CServer server(serverConfig, &primaryClient, &serverScreen, &m_events); + server.m_mock = true; + listener.setServer(&server); + + // client + NiceMock clientScreen; + CSocketMultiplexer clientSocketMultiplexer; + CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer); + + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); + ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); + + CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions); + + m_events.adoptHandler( + m_events.forCClientListener().connected(), &listener, + new TMethodEventJob( + this, &NetworkTests::sendToServer_mockFile_handleClientConnected, &client)); + + m_events.adoptHandler( + m_events.forIScreen().fileRecieveComplete(), &server, + new TMethodEventJob( + this, &NetworkTests::sendToServer_mockFile_fileRecieveComplete)); + + client.connect(); + + m_events.initQuitTimeout(5); m_events.loop(); m_events.cleanupQuitTimeout(); } void -NetworkTests::sendFileToClient_handleClientConnected(const CEvent&, void* vlistener) +NetworkTests::sendToClient_mockData_handleClientConnected(const CEvent&, void* vlistener) { CClientListener* listener = reinterpret_cast(vlistener); CServer* server = listener->getServer(); CClientProxy* client = listener->getNextClient(); if (client == NULL) { - throw std::exception("client is null"); + throw runtime_error("client is null"); } CBaseClientProxy* bcp = reinterpret_cast(client); server->adoptClient(bcp); server->setActive(bcp); - sendData(server); + sendMockData(server); } void -NetworkTests::sendFileToClient_fileRecieveComplete(const CEvent& event, void*) +NetworkTests::sendToClient_mockData_fileRecieveComplete(const CEvent& event, void*) { CClient* client = reinterpret_cast(event.getTarget()); EXPECT_TRUE(client->isReceivedFileSizeValid()); @@ -147,32 +335,166 @@ NetworkTests::sendFileToClient_fileRecieveComplete(const CEvent& event, void*) } void -NetworkTests::sendData(CServer* server) +NetworkTests::sendToClient_mockFile_handleClientConnected(const CEvent&, void* vlistener) { - UInt8* largeDataSize = new UInt8[5]; - largeDataSize[0] = '0'; - largeDataSize[1] = '5'; - largeDataSize[2] = '1'; - largeDataSize[3] = '1'; - largeDataSize[4] = '\0'; - - // transfer data from server -> client - m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, largeDataSize)); - - UInt8* largeData = new UInt8[klargeDataSize + 1]; - largeData[0] = '1'; - memcpy(&largeData[1], g_largeData, klargeDataSize); - m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, (UInt8*)largeData)); + CClientListener* listener = reinterpret_cast(vlistener); + CServer* server = listener->getServer(); - UInt8* transferFinished = new UInt8[2]; - transferFinished[0] = '2'; - transferFinished[1] = '\0'; + CClientProxy* client = listener->getNextClient(); + if (client == NULL) { + throw runtime_error("client is null"); + } - m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, transferFinished)); + CBaseClientProxy* bcp = reinterpret_cast(client); + server->adoptClient(bcp); + server->setActive(bcp); + + server->sendFileToClient(kMockFilename); +} + +void +NetworkTests::sendToClient_mockFile_fileRecieveComplete(const CEvent& event, void*) +{ + CClient* client = reinterpret_cast(event.getTarget()); + EXPECT_TRUE(client->isReceivedFileSizeValid()); + + m_events.raiseQuitEvent(); +} + +void +NetworkTests::sendToServer_mockData_handleClientConnected(const CEvent&, void* vclient) +{ + CClient* client = reinterpret_cast(vclient); + sendMockData(client); +} + +void +NetworkTests::sendToServer_mockData_fileRecieveComplete(const CEvent& event, void*) +{ + CServer* server = reinterpret_cast(event.getTarget()); + EXPECT_TRUE(server->isReceivedFileSizeValid()); + + m_events.raiseQuitEvent(); +} + +void +NetworkTests::sendToServer_mockFile_handleClientConnected(const CEvent&, void* vclient) +{ + CClient* client = reinterpret_cast(vclient); + client->sendFileToServer(kMockFilename); +} + +void +NetworkTests::sendToServer_mockFile_fileRecieveComplete(const CEvent& event, void*) +{ + CServer* server = reinterpret_cast(event.getTarget()); + EXPECT_TRUE(server->isReceivedFileSizeValid()); + + m_events.raiseQuitEvent(); +} + +void +NetworkTests::sendMockData(void* eventTarget) +{ + // send first message (file size) + CString size = intToString(kMockDataSize); + UInt32 sizeLength = size.size(); + CFileChunker::CFileChunk* sizeMessage = new CFileChunker::CFileChunk(sizeLength + 2); + char* chunkData = sizeMessage->m_chunk; + + chunkData[0] = kFileStart; + memcpy(&chunkData[1], size.c_str(), sizeLength); + chunkData[sizeLength + 1] = '\0'; + m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage)); + + // send chunk messages with incrementing chunk size + size_t lastSize = 0; + size_t sentLength = 0; + while (true) { + size_t chunkSize = lastSize + kMockDataChunkIncrement; + + // make sure we don't read too much from the mock data. + if (sentLength + chunkSize > kMockDataSize) { + chunkSize = kMockDataSize - sentLength; + } + + // first byte is the chunk mark, last is \0 + CFileChunker::CFileChunk* fileChunk = new CFileChunker::CFileChunk(chunkSize + 2); + char* chunkData = fileChunk->m_chunk; + + chunkData[0] = kFileChunk; + memcpy(&chunkData[1], &m_mockData[sentLength], chunkSize); + chunkData[chunkSize + 1] = '\0'; + m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), eventTarget, fileChunk)); + + sentLength += chunkSize; + lastSize = chunkSize; + + if (sentLength == kMockDataSize) { + break; + } + + } + + // send last message + CFileChunker::CFileChunk* transferFinished = new CFileChunker::CFileChunk(2); + chunkData = transferFinished->m_chunk; + + chunkData[0] = kFileEnd; + chunkData[1] = '\0'; + m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), eventTarget, transferFinished)); +} + +UInt8* +newMockData(size_t size) +{ + UInt8* buffer = new UInt8[size]; + + UInt8* data = buffer; + const UInt8 head[] = "mock head... "; + size_t headSize = sizeof(head) - 1; + const UInt8 tail[] = "... mock tail"; + size_t tailSize = sizeof(tail) - 1; + const UInt8 synergyRocks[] = "synergy\0 rocks! "; + size_t synergyRocksSize = sizeof(synergyRocks) - 1; + + memcpy(data, head, headSize); + data += headSize; + + SInt32 times = (size - headSize - tailSize) / synergyRocksSize; + for (SInt32 i = 0; i < times; ++i) { + memcpy(data, synergyRocks, synergyRocksSize); + data += synergyRocksSize; + } + + SInt32 remainder = (size - headSize - tailSize) % synergyRocksSize; + if (remainder != 0) { + memset(data, '.', remainder); + data += remainder; + } + + memcpy(data, tail, tailSize); + return buffer; } void -sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) +createFile(fstream& file, const char* filename, size_t size) +{ + UInt8* buffer = newMockData(size); + + file.open(filename, ios::out | ios::binary); + if (!file.is_open()) { + throw runtime_error("file not open"); + } + + file.write(reinterpret_cast(buffer), size); + file.close(); + + delete[] buffer; +} + +void +getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) { x = 0; y = 0; @@ -181,8 +503,16 @@ sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) } void -sendFileToClient_getCursorPos(SInt32& x, SInt32& y) +getCursorPos(SInt32& x, SInt32& y) { x = 0; y = 0; } + +CString +intToString(size_t i) +{ + stringstream ss; + ss << i; + return ss.str(); +} diff --git a/src/test/integtests/platform/COSXKeyStateTests.cpp b/src/test/integtests/platform/COSXKeyStateTests.cpp index 372e549f..f74bc2d7 100644 --- a/src/test/integtests/platform/COSXKeyStateTests.cpp +++ b/src/test/integtests/platform/COSXKeyStateTests.cpp @@ -20,8 +20,8 @@ #include #include "COSXKeyState.h" -#include "CMockKeyMap.h" -#include "CMockEventQueue.h" +#include "synergy/CMockKeyMap.h" +#include "synergy/CMockEventQueue.h" #include "CLog.h" diff --git a/src/test/integtests/platform/CXWindowsKeyStateTests.cpp b/src/test/integtests/platform/CXWindowsKeyStateTests.cpp index 45d45a04..35c45b3c 100644 --- a/src/test/integtests/platform/CXWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/CXWindowsKeyStateTests.cpp @@ -22,8 +22,8 @@ #define TEST_ENV #include "Global.h" -#include "CMockKeyMap.h" -#include "CMockEventQueue.h" +#include "synergy/CMockKeyMap.h" +#include "synergy/CMockEventQueue.h" #include "CXWindowsKeyState.h" #include "CLog.h" #include diff --git a/src/test/integtests/platform/CXWindowsScreenTests.cpp b/src/test/integtests/platform/CXWindowsScreenTests.cpp index 846e35b0..e694b4c9 100644 --- a/src/test/integtests/platform/CXWindowsScreenTests.cpp +++ b/src/test/integtests/platform/CXWindowsScreenTests.cpp @@ -18,7 +18,7 @@ #include #include "CXWindowsScreen.h" -#include "CMockEventQueue.h" +#include "synergy/CMockEventQueue.h" using ::testing::_; diff --git a/src/test/unittests/client/CServerProxyTests.cpp b/src/test/unittests/client/CServerProxyTests.cpp index 09139131..8c985924 100644 --- a/src/test/unittests/client/CServerProxyTests.cpp +++ b/src/test/unittests/client/CServerProxyTests.cpp @@ -48,8 +48,8 @@ TEST(CServerProxyTests, mouseMove) g_mouseMove_bufferIndex = 0; NiceMock eventQueue; - NiceMock client; NiceMock stream; + NiceMock client; IStreamEvents streamEvents; streamEvents.setEvents(&eventQueue);