diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 32cf6f48..abc9f2ed 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -17,6 +17,7 @@ add_subdirectory(lib)
add_subdirectory(cmd)
add_subdirectory(test)
add_subdirectory(plugin)
+add_subdirectory(micro)
if (VNC_SUPPORT)
add_subdirectory(vnc)
diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt
index be81494b..11c2692b 100644
--- a/src/cmd/CMakeLists.txt
+++ b/src/cmd/CMakeLists.txt
@@ -16,3 +16,4 @@
add_subdirectory(synergyc)
add_subdirectory(synergys)
add_subdirectory(synergyd)
+add_subdirectory(usynergy)
diff --git a/src/cmd/usynergy/CMakeLists.txt b/src/cmd/usynergy/CMakeLists.txt
new file mode 100644
index 00000000..08fd17e3
--- /dev/null
+++ b/src/cmd/usynergy/CMakeLists.txt
@@ -0,0 +1,24 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2012 Nick Bolton
+#
+# This package is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# found in the file COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+if (WIN32)
+ set(src uSynergyWin32.c)
+elseif (UNIX)
+ set(src uSynergyUnix.c)
+endif()
+
+include_directories(../../micro)
+add_executable(usynergy ${src})
+target_link_libraries(usynergy micro)
diff --git a/src/cmd/usynergy/uSynergyUnix.c b/src/cmd/usynergy/uSynergyUnix.c
new file mode 100644
index 00000000..ba29aecb
--- /dev/null
+++ b/src/cmd/usynergy/uSynergyUnix.c
@@ -0,0 +1,37 @@
+/*
+uSynergy client -- Implementation for the embedded Synergy client library
+ version 1.0.0, July 7th, 2012
+
+Copyright (c) 2012 Nick Bolton
+
+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 "uSynergy.h"
+
+// TODO: implement callbacks.
+int main(char* argv, int argc) {
+ uSynergyContext* context = malloc(sizeof(uSynergyContext));
+ uSynergyInit(context);
+
+ for(;;) {
+ uSynergyUpdate(context);
+ }
+}
diff --git a/src/cmd/usynergy/uSynergyWin32.c b/src/cmd/usynergy/uSynergyWin32.c
new file mode 100644
index 00000000..5e8afbb5
--- /dev/null
+++ b/src/cmd/usynergy/uSynergyWin32.c
@@ -0,0 +1,141 @@
+/*
+uSynergy client -- Implementation for the embedded Synergy client library
+ version 1.0.0, July 7th, 2012
+
+Copyright (c) 2012 Nick Bolton
+
+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 "uSynergy.h"
+
+#include
+#include
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+uSynergyBool connect(uSynergyCookie cookie);
+uSynergyBool send(uSynergyCookie cookie, const uint8_t *buffer, int length);
+uSynergyBool receive(uSynergyCookie cookie, uint8_t *buffer, int maxLength, int* outLength);
+void sleep(uSynergyCookie cookie, int timeMs);
+uint32_t getTime();
+void trace(uSynergyCookie cookie, const char *text);
+void screenActive(uSynergyCookie cookie, uSynergyBool active);
+void mouse(uSynergyCookie cookie, uint16_t x, uint16_t y, int16_t wheelX,
+ int16_t wheelY, uSynergyBool buttonLeft, uSynergyBool buttonRight,
+ uSynergyBool buttonMiddle);
+void keyboard(uSynergyCookie cookie, uint16_t key, uSynergyBool down,
+ uSynergyBool repeat);
+void joystick(uSynergyCookie cookie, uint8_t joyNum, uint16_t buttons,
+ int8_t leftStickX, int8_t leftStickY, int8_t rightStickX,
+ int8_t rightStickY);
+void clipboard(uSynergyCookie cookie, enum uSynergyClipboardFormat format,
+ const uint8_t *data, uint32_t size);
+
+// TODO: implement callbacks.
+int main(char* argv, int argc)
+{
+ uSynergyContext context;
+ uSynergyInit(&context);
+
+ context.m_connectFunc = &connect;
+ context.m_sendFunc = &send;
+ context.m_receiveFunc = &receive;
+ context.m_sleepFunc = &sleep;
+ context.m_getTimeFunc = &getTime;
+ context.m_traceFunc = &trace;
+ context.m_screenActiveCallback = &screenActive;
+ context.m_mouseCallback = &mouse;
+ context.m_keyboardCallback = &keyboard;
+ context.m_joystickCallback = &joystick;
+ context.m_clipboardCallback = &clipboard;
+
+ for(;;) {
+ uSynergyUpdate(&context);
+ }
+}
+
+uSynergyBool connect(uSynergyCookie cookie)
+{
+ printf("connect\n");
+ return USYNERGY_TRUE;
+}
+
+uSynergyBool send(uSynergyCookie cookie, const uint8_t *buffer, int length)
+{
+ printf("send\n");
+ return USYNERGY_TRUE;
+}
+
+uSynergyBool receive(uSynergyCookie cookie, uint8_t *buffer, int maxLength, int* outLength)
+{
+ printf("receive\n");
+ return USYNERGY_TRUE;
+}
+
+void sleep(uSynergyCookie cookie, int timeMs)
+{
+ printf("sleep, timeMs=%d\n", timeMs);
+ Sleep(timeMs);
+}
+
+uint32_t getTime()
+{
+ printf("getTime\n");
+ return 0;
+}
+
+void trace(uSynergyCookie cookie, const char *text)
+{
+ printf("%s\n", text);
+}
+
+void screenActive(uSynergyCookie cookie, uSynergyBool active)
+{
+ printf("screenActive, active=%d\n", active);
+}
+
+void mouse(uSynergyCookie cookie, uint16_t x, uint16_t y, int16_t wheelX,
+ int16_t wheelY, uSynergyBool buttonLeft, uSynergyBool buttonRight,
+ uSynergyBool buttonMiddle)
+{
+ printf("mouse, pos=%d,%d\n", x, y);
+}
+
+void keyboard(uSynergyCookie cookie, uint16_t key, uSynergyBool down,
+ uSynergyBool repeat)
+{
+ printf("keyboard, key=%d down=%d repeat=%d\n", key, down, repeat);
+}
+
+void joystick(uSynergyCookie cookie, uint8_t joyNum, uint16_t buttons,
+ int8_t leftStickX, int8_t leftStickY, int8_t rightStickX,
+ int8_t rightStickY)
+{
+ printf("joystick, left=%d,%d right=%d,%d\n", leftStickX, leftStickY,
+ rightStickX, rightStickY);
+}
+
+void clipboard(uSynergyCookie cookie, enum uSynergyClipboardFormat format,
+ const uint8_t *data, uint32_t size)
+{
+ printf("clipboard, size=%d\n", size);
+}
diff --git a/src/micro/CMakeLists.txt b/src/micro/CMakeLists.txt
new file mode 100644
index 00000000..4380cbbe
--- /dev/null
+++ b/src/micro/CMakeLists.txt
@@ -0,0 +1,24 @@
+# synergy -- mouse and keyboard sharing utility
+# Copyright (C) 2012 Nick Bolton
+#
+# This package is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# found in the file COPYING that should have accompanied this file.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+set(src
+ uSynergy.c
+)
+
+if (WIN32)
+ list(APPEND src uSynergy.h)
+endif()
+
+add_library(micro STATIC ${src})
diff --git a/src/micro/uSynergy.c b/src/micro/uSynergy.c
new file mode 100644
index 00000000..12f1a5f2
--- /dev/null
+++ b/src/micro/uSynergy.c
@@ -0,0 +1,633 @@
+/*
+uSynergy client -- Implementation for the embedded Synergy client library
+ version 1.0.0, July 7th, 2012
+
+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 "uSynergy.h"
+#include
+#include
+#include
+
+
+
+//---------------------------------------------------------------------------------------------------------------------
+// Internal helpers
+//---------------------------------------------------------------------------------------------------------------------
+
+
+
+/**
+@brief Read 16 bit integer in network byte order and convert to native byte order
+**/
+static int16_t sNetToNative16(const unsigned char *value)
+{
+#ifdef USYNERGY_LITTLE_ENDIAN
+ return value[0] | (value[1] << 8);
+#else
+ return value[1] | (value[0] << 8);
+#endif
+}
+
+
+
+/**
+@brief Read 32 bit integer in network byte order and convert to native byte order
+**/
+static int32_t sNetToNative32(const unsigned char *value)
+{
+#ifdef USYNERGY_LITTLE_ENDIAN
+ return value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+#else
+ return value[3] | (value[2] << 8) | (value[1] << 16) | (value[0] << 24);
+#endif
+}
+
+
+
+/**
+@brief Trace text to client
+**/
+static void sTrace(uSynergyContext *context, const char* fmt, ...)
+{
+ // Print to buffer, then call callback
+ char buffer[USYNERGY_TRACE_BUFFER_SIZE];
+ va_list va_alist;
+
+ // Expand arguments and format to string
+ va_start(va_alist);
+ vsprintf_s(buffer, USYNERGY_TRACE_BUFFER_SIZE, fmt, va_alist);
+ va_end(va_alist);
+
+ // Don't trace if we don't have a trace function
+ if (context->m_traceFunc != 0L)
+ context->m_traceFunc(context->m_cookie, buffer);
+}
+
+
+
+/**
+@brief Add string to reply packet
+**/
+static void sAddString(uSynergyContext *context, const char *string)
+{
+ size_t len = strlen(string);
+ memcpy(context->m_replyCur, string, len);
+ context->m_replyCur += len;
+}
+
+
+
+/**
+@brief Add uint8 to reply packet
+**/
+static void sAddUInt8(uSynergyContext *context, uint8_t value)
+{
+ *context->m_replyCur++ = value;
+}
+
+
+
+/**
+@brief Add uint16 to reply packet
+**/
+static void sAddUInt16(uSynergyContext *context, uint16_t value)
+{
+ uint8_t *reply = context->m_replyCur;
+ *reply++ = (uint8_t)(value >> 8);
+ *reply++ = (uint8_t)value;
+ context->m_replyCur = reply;
+}
+
+
+
+/**
+@brief Add uint32 to reply packet
+**/
+static void sAddUInt32(uSynergyContext *context, uint32_t value)
+{
+ uint8_t *reply = context->m_replyCur;
+ *reply++ = (uint8_t)(value >> 24);
+ *reply++ = (uint8_t)(value >> 16);
+ *reply++ = (uint8_t)(value >> 8);
+ *reply++ = (uint8_t)value;
+ context->m_replyCur = reply;
+}
+
+
+
+/**
+@brief Send reply packet
+**/
+static uSynergyBool sSendReply(uSynergyContext *context)
+{
+ // Set header size
+ uint8_t *reply_buf = context->m_replyBuffer;
+ uint32_t reply_len = (uint32_t)(context->m_replyCur - reply_buf); /* Total size of reply */
+ uint32_t body_len = reply_len - 4; /* Size of body */
+ uSynergyBool ret;
+ reply_buf[0] = (uint8_t)(body_len >> 24);
+ reply_buf[1] = (uint8_t)(body_len >> 16);
+ reply_buf[2] = (uint8_t)(body_len >> 8);
+ reply_buf[3] = (uint8_t)body_len;
+
+ // Send reply
+ ret = context->m_sendFunc(context->m_cookie, context->m_replyBuffer, reply_len);
+
+ // Reset reply buffer write pointer
+ context->m_replyCur = context->m_replyBuffer+4;
+ return ret;
+}
+
+
+
+/**
+@brief Call mouse callback after a mouse event
+**/
+static void sSendMouseCallback(uSynergyContext *context)
+{
+ // Skip if no callback is installed
+ if (context->m_mouseCallback == 0L)
+ return;
+
+ // Send callback
+ context->m_mouseCallback(context->m_cookie, context->m_mouseX, context->m_mouseY, context->m_mouseWheelX,
+ context->m_mouseWheelY, context->m_mouseButtonLeft, context->m_mouseButtonRight, context->m_mouseButtonMiddle);
+}
+
+
+
+/**
+@brief Send keyboard callback when a key has been pressed or released
+**/
+static void sSendKeyboardCallback(uSynergyContext *context, uint16_t key, char down, char repeat, uint16_t modifiers)
+{
+ // Skip if no callback is installed
+ if (context->m_keyboardCallback == 0L)
+ return;
+
+ // Send callback
+ modifiers;
+ context->m_keyboardCallback(context->m_cookie, key, down, repeat);
+}
+
+
+
+/**
+@brief Send joystick callback
+**/
+static void sSendJoystickCallback(uSynergyContext *context, uint8_t joyNum)
+{
+ int8_t *sticks;
+
+ // Skip if no callback is installed
+ if (context->m_joystickCallback == 0L)
+ return;
+
+ // Send callback
+ sticks = context->m_joystickSticks[joyNum];
+ context->m_joystickCallback(context->m_cookie, joyNum, context->m_joystickButtons[joyNum], sticks[0], sticks[1], sticks[2], sticks[3]);
+}
+
+
+
+/**
+@brief Parse a single client message, update state, send callbacks and send replies
+**/
+#define USYNERGY_IS_PACKET(pkt_id) memcmp(message+4, pkt_id, 4)==0
+static void sProcessMessage(uSynergyContext *context, const uint8_t *message)
+{
+ // We have a packet!
+ if (memcmp(message+4, "Synergy", 7)==0)
+ {
+ // Welcome message
+ // kMsgHello = "Synergy%2i%2i"
+ // kMsgHelloBack = "Synergy%2i%2i%s"
+ sAddString(context, "Synergy");
+ sAddUInt16(context, USYNERGY_PROTOCOL_MAJOR);
+ sAddUInt16(context, USYNERGY_PROTOCOL_MINOR);
+ sAddUInt32(context, (uint32_t)strlen(context->m_clientName));
+ sAddString(context, context->m_clientName);
+ if (!sSendReply(context))
+ {
+ // Send reply failed, let's try to reconnect
+ sTrace(context, "SendReply failed, trying to reconnect in a second");
+ context->m_connected = USYNERGY_FALSE;
+ context->m_sleepFunc(context->m_cookie, 1000);
+ }
+ else
+ {
+ // Let's assume we're connected
+ sTrace(context, "Connected as client \"%s\"", context->m_clientName);
+ context->m_hasReceivedHello = USYNERGY_TRUE;
+ }
+ return;
+ }
+ else if (USYNERGY_IS_PACKET("QINF"))
+ {
+ // Screen info. Reply with DINF
+ // kMsgQInfo = "QINF"
+ // kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i"
+ uint16_t x = 0, y = 0, warp = 0;
+ sAddString(context, "DINF");
+ sAddUInt16(context, x);
+ sAddUInt16(context, y);
+ sAddUInt16(context, context->m_clientWidth);
+ sAddUInt16(context, context->m_clientHeight);
+ sAddUInt16(context, warp);
+ sAddUInt16(context, 0); // mx?
+ sAddUInt16(context, 0); // my?
+ sSendReply(context);
+ return;
+ }
+ else if (USYNERGY_IS_PACKET("CIAK"))
+ {
+ // Do nothing?
+ // kMsgCInfoAck = "CIAK"
+ return;
+ }
+ else if (USYNERGY_IS_PACKET("CROP"))
+ {
+ // Do nothing?
+ // kMsgCResetOptions = "CROP"
+ return;
+ }
+ else if (USYNERGY_IS_PACKET("CINN"))
+ {
+ // Screen enter. Reply with CNOP
+ // kMsgCEnter = "CINN%2i%2i%4i%2i"
+
+ // Obtain the Synergy sequence number
+ context->m_sequenceNumber = sNetToNative32(message + 12);
+ context->m_isCaptured = USYNERGY_TRUE;
+
+ // Call callback
+ if (context->m_screenActiveCallback != 0L)
+ context->m_screenActiveCallback(context->m_cookie, USYNERGY_TRUE);
+ }
+ else if (USYNERGY_IS_PACKET("COUT"))
+ {
+ // Screen leave
+ // kMsgCLeave = "COUT"
+ context->m_isCaptured = USYNERGY_FALSE;
+
+ // Call callback
+ if (context->m_screenActiveCallback != 0L)
+ context->m_screenActiveCallback(context->m_cookie, USYNERGY_FALSE);
+ }
+ else if (USYNERGY_IS_PACKET("DMDN"))
+ {
+ // Mouse down
+ // kMsgDMouseDown = "DMDN%1i"
+ char btn = message[8]-1;
+ if (btn==2)
+ context->m_mouseButtonRight = USYNERGY_TRUE;
+ else if (btn==1)
+ context->m_mouseButtonMiddle = USYNERGY_TRUE;
+ else
+ context->m_mouseButtonLeft = USYNERGY_TRUE;
+ sSendMouseCallback(context);
+ }
+ else if (USYNERGY_IS_PACKET("DMUP"))
+ {
+ // Mouse up
+ // kMsgDMouseUp = "DMUP%1i"
+ char btn = message[8]-1;
+ if (btn==2)
+ context->m_mouseButtonRight = USYNERGY_FALSE;
+ else if (btn==1)
+ context->m_mouseButtonMiddle = USYNERGY_FALSE;
+ else
+ context->m_mouseButtonLeft = USYNERGY_FALSE;
+ sSendMouseCallback(context);
+ }
+ else if (USYNERGY_IS_PACKET("DMMV"))
+ {
+ // Mouse move. Reply with CNOP
+ // kMsgDMouseMove = "DMMV%2i%2i"
+ context->m_mouseX = sNetToNative16(message+8);
+ context->m_mouseY = sNetToNative16(message+10);
+ sSendMouseCallback(context);
+ }
+ else if (USYNERGY_IS_PACKET("DMWM"))
+ {
+ // Mouse wheel
+ // kMsgDMouseWheel = "DMWM%2i%2i"
+ // kMsgDMouseWheel1_0 = "DMWM%2i"
+ context->m_mouseWheelX += sNetToNative16(message+8);
+ context->m_mouseWheelY += sNetToNative16(message+10);
+ sSendMouseCallback(context);
+ }
+ else if (USYNERGY_IS_PACKET("DKDN"))
+ {
+ // Key down
+ // kMsgDKeyDown = "DKDN%2i%2i%2i"
+ // kMsgDKeyDown1_0 = "DKDN%2i%2i"
+ //uint16_t id = sNetToNative16(message+8);
+ uint16_t mod = sNetToNative16(message+10);
+ uint16_t key = sNetToNative16(message+12);
+ sSendKeyboardCallback(context, key, 1, 0, mod);
+ }
+ else if (USYNERGY_IS_PACKET("DKRP"))
+ {
+ // Key repeat
+ // kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i"
+ // kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i"
+ }
+ else if (USYNERGY_IS_PACKET("DKUP"))
+ {
+ // Key up
+ // kMsgDKeyUp = "DKUP%2i%2i%2i"
+ // kMsgDKeyUp1_0 = "DKUP%2i%2i"
+ //uint16 id=Endian::sNetToNative(sbuf[4]);
+ uint16_t mod = sNetToNative16(message+10);
+ uint16_t key = sNetToNative16(message+12);
+ sSendKeyboardCallback(context, key, 0, 0, mod);
+ }
+ else if (USYNERGY_IS_PACKET("DGBT"))
+ {
+ // Joystick buttons
+ // kMsgDGameButtons = "DGBT%1i%2i";
+ uint8_t joy_num = message[8];
+ if (joy_numm_joystickButtons[joy_num] = (message[9] << 8) | message[10];
+ sSendJoystickCallback(context, joy_num);
+ }
+ }
+ else if (USYNERGY_IS_PACKET("DGST"))
+ {
+ // Joystick sticks
+ // kMsgDGameSticks = "DGST%1i%1i%1i%1i%1i";
+ uint8_t joy_num = message[8];
+ if (joy_numm_joystickSticks[joy_num], message+9, 4);
+ sSendJoystickCallback(context, joy_num);
+ }
+ }
+ else if (USYNERGY_IS_PACKET("DSOP"))
+ {
+ // Set options
+ // kMsgDSetOptions = "DSOP%4I"
+ }
+ else if (USYNERGY_IS_PACKET("CALV"))
+ {
+ // Keepalive, reply with CALV and then CNOP
+ // kMsgCKeepAlive = "CALV"
+ sAddString(context, "CALV");
+ sSendReply(context);
+ // now reply with CNOP
+ }
+ else if (USYNERGY_IS_PACKET("DCLP"))
+ {
+ // Clipboard message
+ // kMsgDClipboard = "DCLP%1i%4i%s"
+ //
+ // The clipboard message contains:
+ // 1 uint32: The size of the message
+ // 4 chars: The identifier ("DCLP")
+ // 1 uint8: The clipboard index
+ // 1 uint32: The sequence number. It's zero, because this message is always coming from the server?
+ // 1 uint32: The total size of the remaining 'string' (as per the Synergy %s string format (which is 1 uint32 for size followed by a char buffer (not necessarily null terminated)).
+ // 1 uint32: The number of formats present in the message
+ // And then 'number of formats' times the following:
+ // 1 uint32: The format of the clipboard data
+ // 1 uint32: The size n of the clipboard data
+ // n uint8: The clipboard data
+ const uint8_t * parse_msg = message+17;
+ uint32_t num_formats = sNetToNative32(parse_msg);
+ parse_msg += 4;
+ for (; num_formats; num_formats--)
+ {
+ // Parse clipboard format header
+ uint32_t format = sNetToNative32(parse_msg);
+ uint32_t size = sNetToNative32(parse_msg+4);
+ parse_msg += 8;
+
+ // Call callback
+ if (context->m_clipboardCallback)
+ context->m_clipboardCallback(context->m_cookie, format, parse_msg, size);
+
+ parse_msg += size;
+ }
+ }
+ else
+ {
+ // Unknown packet, could be any of these
+ // kMsgCNoop = "CNOP"
+ // kMsgCClose = "CBYE"
+ // kMsgCClipboard = "CCLP%1i%4i"
+ // kMsgCScreenSaver = "CSEC%1i"
+ // kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i"
+ // kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i"
+ // kMsgDMouseRelMove = "DMRM%2i%2i"
+ // kMsgEIncompatible = "EICV%2i%2i"
+ // kMsgEBusy = "EBSY"
+ // kMsgEUnknown = "EUNK"
+ // kMsgEBad = "EBAD"
+ sTrace(context, "Unknown packet '%c%c%c%c'", message[4], message[5], message[6], message[7]);
+ return;
+ }
+
+ // Reply with CNOP maybe?
+ sAddString(context, "CNOP");
+ sSendReply(context);
+}
+#undef USYNERGY_IS_PACKET
+
+
+
+/**
+@brief Mark context as being disconnected
+**/
+static void sSetDisconnected(uSynergyContext *context)
+{
+ context->m_connected = USYNERGY_FALSE;
+ context->m_hasReceivedHello = USYNERGY_FALSE;
+ context->m_isCaptured = USYNERGY_FALSE;
+ context->m_replyCur = context->m_replyBuffer + 4;
+ context->m_sequenceNumber = 0;
+}
+
+
+
+/**
+@brief Update a connected context
+**/
+static void sUpdateContext(uSynergyContext *context)
+{
+ /* Receive data (blocking) */
+ int receive_size = USYNERGY_RECEIVE_BUFFER_SIZE - context->m_receiveOfs;
+ int num_received = 0;
+ int packlen = 0;
+ if (context->m_receiveFunc(context->m_cookie, context->m_receiveBuffer, receive_size, &num_received) == USYNERGY_FALSE)
+ {
+ /* Receive failed, let's try to reconnect */
+ sTrace(context, "Receive failed (%d bytes asked, %d bytes received), trying to reconnect in a second", receive_size, num_received);
+ sSetDisconnected(context);
+ context->m_sleepFunc(context->m_cookie, 1000);
+ return;
+ }
+ context->m_receiveOfs += num_received;
+
+ /* If we didn't receive any data then we're probably still polling to get connected and
+ therefore not getting any data back. To avoid overloading the system with a Synergy
+ thread that would hammer on polling, we let it rest for a bit if there's no data. */
+ if (num_received == 0)
+ context->m_sleepFunc(context->m_cookie, 500);
+
+ /* Check for timeouts */
+ if (context->m_hasReceivedHello)
+ {
+ uint32_t cur_time = context->m_getTimeFunc();
+ if (num_received == 0)
+ {
+ /* Timeout after 2 secs of inactivity (we received no CALV) */
+ if ((cur_time - context->m_lastMessageTime) > USYNERGY_IDLE_TIMEOUT)
+ sSetDisconnected(context);
+ }
+ else
+ context->m_lastMessageTime = cur_time;
+ }
+
+ /* Eat packets */
+ for (;;)
+ {
+ /* Grab packet length and bail out if the packet goes beyond the end of the buffer */
+ packlen = sNetToNative32(context->m_receiveBuffer);
+ if (packlen+4 > context->m_receiveOfs)
+ break;
+
+ /* Process message */
+ sProcessMessage(context, context->m_receiveBuffer);
+
+ /* Move packet to front of buffer */
+ memmove(context->m_receiveBuffer, context->m_receiveBuffer+packlen+4, context->m_receiveOfs-packlen-4);
+ context->m_receiveOfs -= packlen+4;
+ }
+
+ /* Throw away over-sized packets */
+ if (packlen > USYNERGY_RECEIVE_BUFFER_SIZE)
+ {
+ /* Oversized packet, ditch tail end */
+ sTrace(context, "Oversized packet: '%c%c%c%c' (length %d)", context->m_receiveBuffer[4], context->m_receiveBuffer[5], context->m_receiveBuffer[6], context->m_receiveBuffer[7], packlen);
+ num_received = context->m_receiveOfs-4; // 4 bytes for the size field
+ while (num_received != packlen)
+ {
+ int buffer_left = packlen - num_received;
+ int to_receive = buffer_left < USYNERGY_RECEIVE_BUFFER_SIZE ? buffer_left : USYNERGY_RECEIVE_BUFFER_SIZE;
+ int ditch_received = 0;
+ if (context->m_receiveFunc(context->m_cookie, context->m_receiveBuffer, to_receive, &ditch_received) == USYNERGY_FALSE)
+ {
+ /* Receive failed, let's try to reconnect */
+ sTrace(context, "Synergy: Receive failed, trying to reconnect in a second");
+ sSetDisconnected(context);
+ context->m_sleepFunc(context->m_cookie, 1000);
+ break;
+ }
+ else
+ {
+ num_received += ditch_received;
+ }
+ }
+ context->m_receiveOfs = 0;
+ }
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------
+// Public interface
+//---------------------------------------------------------------------------------------------------------------------
+
+
+
+/**
+@brief Initialize uSynergy context
+**/
+void uSynergyInit(uSynergyContext *context)
+{
+ /* Zero memory */
+ memset(context, 0, sizeof(uSynergyContext));
+
+ /* Initialize to default state */
+ sSetDisconnected(context);
+}
+
+
+/**
+@brief Update uSynergy
+**/
+void uSynergyUpdate(uSynergyContext *context)
+{
+ if (context->m_connected)
+ {
+ /* Update context, receive data, call callbacks */
+ sUpdateContext(context);
+ }
+ else
+ {
+ /* Try to connect */
+ if (context->m_connectFunc(context->m_cookie))
+ context->m_connected = USYNERGY_TRUE;
+ }
+}
+
+
+
+/**
+@brief Send clipboard data
+**/
+void uSynergySendClipboard(uSynergyContext *context, const char *text)
+{
+ // Calculate maximum size that will fit in a reply packet
+ uint32_t overhead_size = 4 + /* Message size */
+ 4 + /* Message ID */
+ 1 + /* Clipboard index */
+ 4 + /* Sequence number */
+ 4 + /* Rest of message size (because it's a Synergy string from here on) */
+ 4 + /* Number of clipboard formats */
+ 4 + /* Clipboard format */
+ 4; /* Clipboard data length */
+ uint32_t max_length = USYNERGY_REPLY_BUFFER_SIZE - overhead_size;
+
+ // Clip text to max length
+ uint32_t text_length = (uint32_t)strlen(text);
+ if (text_length > max_length)
+ {
+ sTrace(context, "Clipboard buffer too small, clipboard truncated at %d characters", max_length);
+ text_length = max_length;
+ }
+
+ // Assemble packet
+ sAddString(context, "DCLP");
+ sAddUInt8(context, 0); /* Clipboard index */
+ sAddUInt32(context, context->m_sequenceNumber);
+ sAddUInt32(context, 4+4+4+text_length); /* Rest of message size: numFormats, format, length, data */
+ sAddUInt32(context, 1); /* Number of formats (only text for now) */
+ sAddUInt32(context, USYNERGY_CLIPBOARD_FORMAT_TEXT);
+ sAddUInt32(context, text_length);
+ sAddString(context, text);
+ sSendReply(context);
+}
diff --git a/src/micro/uSynergy.h b/src/micro/uSynergy.h
new file mode 100644
index 00000000..c93a8a99
--- /dev/null
+++ b/src/micro/uSynergy.h
@@ -0,0 +1,400 @@
+/*
+uSynergy client -- Interface for the embedded Synergy client library
+ version 1.0.0, July 7th, 2012
+
+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 */
+
+
+//---------------------------------------------------------------------------------------------------------------------
+// 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 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, 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