| 1 | /* |
| 2 | uSynergy client -- Interface for the embedded Synergy client library |
| 3 | version 1.0.0, July 7th, 2012 |
| 4 | |
| 5 | Copyright (C) 2012 Synergy Si Ltd. |
| 6 | Copyright (c) 2012 Alex Evans |
| 7 | |
| 8 | This software is provided 'as-is', without any express or implied |
| 9 | warranty. In no event will the authors be held liable for any damages |
| 10 | arising from the use of this software. |
| 11 | |
| 12 | Permission is granted to anyone to use this software for any purpose, |
| 13 | including commercial applications, and to alter it and redistribute it |
| 14 | freely, subject to the following restrictions: |
| 15 | |
| 16 | 1. The origin of this software must not be misrepresented; you must not |
| 17 | claim that you wrote the original software. If you use this software |
| 18 | in a product, an acknowledgment in the product documentation would be |
| 19 | appreciated but is not required. |
| 20 | |
| 21 | 2. Altered source versions must be plainly marked as such, and must not be |
| 22 | misrepresented as being the original software. |
| 23 | |
| 24 | 3. This notice may not be removed or altered from any source |
| 25 | distribution. |
| 26 | */ |
| 27 | #include <stdint.h> |
| 28 | |
| 29 | #ifdef __cplusplus |
| 30 | extern "C" { |
| 31 | #endif |
| 32 | |
| 33 | |
| 34 | |
| 35 | //--------------------------------------------------------------------------------------------------------------------- |
| 36 | // Configuration |
| 37 | //--------------------------------------------------------------------------------------------------------------------- |
| 38 | |
| 39 | |
| 40 | |
| 41 | /** |
| 42 | @brief Determine endianness |
| 43 | **/ |
| 44 | #if defined(USYNERGY_LITTLE_ENDIAN) && defined(USYNERGY_BIG_ENDIAN) |
| 45 | /* Ambiguous: both endians specified */ |
| 46 | #error "Can't define both USYNERGY_LITTLE_ENDIAN and USYNERGY_BIG_ENDIAN" |
| 47 | #elif !defined(USYNERGY_LITTLE_ENDIAN) && !defined(USYNERGY_BIG_ENDIAN) |
| 48 | /* Attempt to auto detect */ |
| 49 | #if defined(__LITTLE_ENDIAN__) || defined(LITTLE_ENDIAN) || (_BYTE_ORDER == _LITTLE_ENDIAN) |
| 50 | #define USYNERGY_LITTLE_ENDIAN |
| 51 | #elif defined(__BIG_ENDIAN__) || defined(BIG_ENDIAN) || (_BYTE_ORDER == _BIG_ENDIAN) |
| 52 | #define USYNERGY_BIG_ENDIAN |
| 53 | #else |
| 54 | #error "Can't detect endian-nes, please defined either USYNERGY_LITTLE_ENDIAN or USYNERGY_BIG_ENDIAN"; |
| 55 | #endif |
| 56 | #else |
| 57 | /* User-specified endian-nes, nothing to do for us */ |
| 58 | #endif |
| 59 | |
| 60 | |
| 61 | |
| 62 | //--------------------------------------------------------------------------------------------------------------------- |
| 63 | // Types and Constants |
| 64 | //--------------------------------------------------------------------------------------------------------------------- |
| 65 | |
| 66 | |
| 67 | |
| 68 | /** |
| 69 | @brief Boolean type |
| 70 | **/ |
| 71 | typedef int uSynergyBool; |
| 72 | #define USYNERGY_FALSE 0 /* False value */ |
| 73 | #define USYNERGY_TRUE 1 /* True value */ |
| 74 | |
| 75 | |
| 76 | /** |
| 77 | @brief User context type |
| 78 | |
| 79 | The uSynergyCookie type is an opaque type that is used by uSynergy to communicate to the client. It is passed along to |
| 80 | callback functions as context. |
| 81 | **/ |
| 82 | typedef struct { int ignored; } * uSynergyCookie; |
| 83 | |
| 84 | |
| 85 | |
| 86 | /** |
| 87 | @brief Clipboard types |
| 88 | **/ |
| 89 | enum uSynergyClipboardFormat |
| 90 | { |
| 91 | USYNERGY_CLIPBOARD_FORMAT_TEXT = 0, /* Text format, UTF-8, newline is LF */ |
| 92 | USYNERGY_CLIPBOARD_FORMAT_BITMAP = 1, /* Bitmap format, BMP 24/32bpp, BI_RGB */ |
| 93 | USYNERGY_CLIPBOARD_FORMAT_HTML = 2, /* HTML format, HTML fragment, UTF-8, newline is LF */ |
| 94 | }; |
| 95 | |
| 96 | |
| 97 | |
| 98 | /** |
| 99 | @brief Constants and limits |
| 100 | **/ |
| 101 | #define USYNERGY_NUM_JOYSTICKS 4 /* Maximum number of supported joysticks */ |
| 102 | |
| 103 | #define USYNERGY_PROTOCOL_MAJOR 1 /* Major protocol version */ |
| 104 | #define USYNERGY_PROTOCOL_MINOR 4 /* Minor protocol version */ |
| 105 | |
| 106 | #define USYNERGY_IDLE_TIMEOUT 2000 /* Timeout in milliseconds before reconnecting */ |
| 107 | |
| 108 | #define USYNERGY_TRACE_BUFFER_SIZE 1024 /* Maximum length of traced message */ |
| 109 | #define USYNERGY_REPLY_BUFFER_SIZE 1024 /* Maximum size of a reply packet */ |
| 110 | #define USYNERGY_RECEIVE_BUFFER_SIZE 4096 /* Maximum size of an incoming packet */ |
| 111 | |
| 112 | |
| 113 | |
| 114 | /** |
| 115 | @brief Keyboard constants |
| 116 | **/ |
| 117 | #define USYNERGY_MODIFIER_SHIFT 0x0001 /* Shift key modifier */ |
| 118 | #define USYNERGY_MODIFIER_CTRL 0x0002 /* Ctrl key modifier */ |
| 119 | #define USYNERGY_MODIFIER_ALT 0x0004 /* Alt key modifier */ |
| 120 | #define USYNERGY_MODIFIER_META 0x0008 /* Meta key modifier */ |
| 121 | #define USYNERGY_MODIFIER_WIN 0x0010 /* Windows key modifier */ |
| 122 | #define USYNERGY_MODIFIER_ALT_GR 0x0020 /* AltGr key modifier */ |
| 123 | #define USYNERGY_MODIFIER_LEVEL5LOCK 0x0040 /* Level5Lock key modifier */ |
| 124 | #define USYNERGY_MODIFIER_CAPSLOCK 0x1000 /* CapsLock key modifier */ |
| 125 | #define USYNERGY_MODIFIER_NUMLOCK 0x2000 /* NumLock key modifier */ |
| 126 | #define USYNERGY_MODIFIER_SCROLLOCK 0x4000 /* ScrollLock key modifier */ |
| 127 | |
| 128 | |
| 129 | |
| 130 | |
| 131 | //--------------------------------------------------------------------------------------------------------------------- |
| 132 | // Functions and Callbacks |
| 133 | //--------------------------------------------------------------------------------------------------------------------- |
| 134 | |
| 135 | |
| 136 | |
| 137 | /** |
| 138 | @brief Connect function |
| 139 | |
| 140 | This function is called when uSynergy needs to connect to the host. It doesn't imply a network implementation or |
| 141 | destination address, that must all be handled on the user side. The function should return USYNERGY_TRUE if a |
| 142 | connection was established or USYNERGY_FALSE if it could not connect. |
| 143 | |
| 144 | When network errors occur (e.g. uSynergySend or uSynergyReceive fail) then the connect call will be called again |
| 145 | so the implementation of the function must close any old connections and clean up resources before retrying. |
| 146 | |
| 147 | @param cookie Cookie supplied in the Synergy context |
| 148 | **/ |
| 149 | typedef uSynergyBool (*uSynergyConnectFunc)(uSynergyCookie cookie); |
| 150 | |
| 151 | |
| 152 | |
| 153 | /** |
| 154 | @brief Send function |
| 155 | |
| 156 | This function is called when uSynergy needs to send something over the default connection. It should return |
| 157 | USYNERGY_TRUE if sending succeeded and USYNERGY_FALSE otherwise. This function should block until the send |
| 158 | operation is completed. |
| 159 | |
| 160 | @param cookie Cookie supplied in the Synergy context |
| 161 | @param buffer Address of buffer to send |
| 162 | @param length Length of buffer to send |
| 163 | **/ |
| 164 | typedef uSynergyBool (*uSynergySendFunc)(uSynergyCookie cookie, const uint8_t *buffer, int length); |
| 165 | |
| 166 | |
| 167 | |
| 168 | /** |
| 169 | @brief Receive function |
| 170 | |
| 171 | This function is called when uSynergy needs to receive data from the default connection. It should return |
| 172 | USYNERGY_TRUE if receiving data succeeded and USYNERGY_FALSE otherwise. This function should block until data |
| 173 | has been received and wait for data to become available. If @a outLength is set to 0 upon completion it is |
| 174 | assumed that the connection is alive, but still in a connecting state and needs time to settle. |
| 175 | |
| 176 | @param cookie Cookie supplied in the Synergy context |
| 177 | @param buffer Address of buffer to receive data into |
| 178 | @param maxLength Maximum amount of bytes to write into the receive buffer |
| 179 | @param outLength Address of integer that receives the actual amount of bytes written into @a buffer |
| 180 | **/ |
| 181 | typedef uSynergyBool (*uSynergyReceiveFunc)(uSynergyCookie cookie, uint8_t *buffer, int maxLength, int* outLength); |
| 182 | |
| 183 | |
| 184 | |
| 185 | /** |
| 186 | @brief Thread sleep function |
| 187 | |
| 188 | This function is called when uSynergy wants to suspend operation for a while before retrying an operation. It |
| 189 | is mostly used when a socket times out or disconnect occurs to prevent uSynergy from continuously hammering a |
| 190 | network connection in case the network is down. |
| 191 | |
| 192 | @param cookie Cookie supplied in the Synergy context |
| 193 | @param timeMs Time to sleep the current thread (in milliseconds) |
| 194 | **/ |
| 195 | typedef void (*uSynergySleepFunc)(uSynergyCookie cookie, int timeMs); |
| 196 | |
| 197 | |
| 198 | |
| 199 | /** |
| 200 | @brief Get time function |
| 201 | |
| 202 | This function is called when uSynergy needs to know the current time. This is used to determine when timeouts |
| 203 | have occurred. The time base should be a cyclic millisecond time value. |
| 204 | |
| 205 | @returns Time value in milliseconds |
| 206 | **/ |
| 207 | typedef uint32_t (*uSynergyGetTimeFunc)(); |
| 208 | |
| 209 | |
| 210 | |
| 211 | /** |
| 212 | @brief Trace function |
| 213 | |
| 214 | This function is called when uSynergy wants to trace something. It is optional to show these messages, but they |
| 215 | are often useful when debugging. uSynergy only traces major events like connecting and disconnecting. Usually |
| 216 | only a single trace is shown when the connection is established and no more trace are called. |
| 217 | |
| 218 | @param cookie Cookie supplied in the Synergy context |
| 219 | @param text Text to be traced |
| 220 | **/ |
| 221 | typedef void (*uSynergyTraceFunc)(uSynergyCookie cookie, const char *text); |
| 222 | |
| 223 | |
| 224 | |
| 225 | /** |
| 226 | @brief Screen active callback |
| 227 | |
| 228 | This callback is called when Synergy makes the screen active or inactive. This |
| 229 | callback is usually sent when the mouse enters or leaves the screen. |
| 230 | |
| 231 | @param cookie Cookie supplied in the Synergy context |
| 232 | @param active Activation flag, 1 if the screen has become active, 0 if the screen has become inactive |
| 233 | **/ |
| 234 | typedef void (*uSynergyScreenActiveCallback)(uSynergyCookie cookie, uSynergyBool active); |
| 235 | |
| 236 | |
| 237 | |
| 238 | /** |
| 239 | @brief Mouse callback |
| 240 | |
| 241 | This callback is called when a mouse events happens. The mouse X and Y position, |
| 242 | wheel and button state is communicated in the message. It's up to the user to |
| 243 | interpret if this is a mouse up, down, double-click or other message. |
| 244 | |
| 245 | @param cookie Cookie supplied in the Synergy context |
| 246 | @param x Mouse X position |
| 247 | @param y Mouse Y position |
| 248 | @param wheelX Mouse wheel X position |
| 249 | @param wheelY Mouse wheel Y position |
| 250 | @param buttonLeft Left button pressed status, 0 for released, 1 for pressed |
| 251 | @param buttonMiddle Middle button pressed status, 0 for released, 1 for pressed |
| 252 | @param buttonRight Right button pressed status, 0 for released, 1 for pressed |
| 253 | **/ |
| 254 | typedef void (*uSynergyMouseCallback)(uSynergyCookie cookie, uint16_t x, uint16_t y, int16_t wheelX, int16_t wheelY, uSynergyBool buttonLeft, uSynergyBool buttonRight, uSynergyBool buttonMiddle); |
| 255 | |
| 256 | |
| 257 | |
| 258 | /** |
| 259 | @brief Key event callback |
| 260 | |
| 261 | This callback is called when a key is pressed or released. |
| 262 | |
| 263 | @param cookie Cookie supplied in the Synergy context |
| 264 | @param key Key code of key that was pressed or released |
| 265 | @param modifiers Status of modifier keys (alt, shift, etc.) |
| 266 | @param down Down or up status, 1 is key is pressed down, 0 if key is released (up) |
| 267 | @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 |
| 268 | **/ |
| 269 | typedef void (*uSynergyKeyboardCallback)(uSynergyCookie cookie, uint16_t key, uint16_t modifiers, uSynergyBool down, uSynergyBool repeat); |
| 270 | |
| 271 | |
| 272 | |
| 273 | /** |
| 274 | @brief Joystick event callback |
| 275 | |
| 276 | This callback is called when a joystick stick or button changes. It is possible that multiple callbacks are |
| 277 | fired when different sticks or buttons change as these are individual messages in the packet stream. Each |
| 278 | callback will contain all the valid state for the different axes and buttons. The last callback received will |
| 279 | represent the most current joystick state. |
| 280 | |
| 281 | @param cookie Cookie supplied in the Synergy context |
| 282 | @param joyNum Joystick number, always in the range [0 ... USYNERGY_NUM_JOYSTICKS> |
| 283 | @param buttons Button pressed mask |
| 284 | @param leftStickX Left stick X position, in range [-127 ... 127] |
| 285 | @param leftStickY Left stick Y position, in range [-127 ... 127] |
| 286 | @param rightStickX Right stick X position, in range [-127 ... 127] |
| 287 | @param rightStickY Right stick Y position, in range [-127 ... 127] |
| 288 | **/ |
| 289 | typedef void (*uSynergyJoystickCallback)(uSynergyCookie cookie, uint8_t joyNum, uint16_t buttons, int8_t leftStickX, int8_t leftStickY, int8_t rightStickX, int8_t rightStickY); |
| 290 | |
| 291 | |
| 292 | |
| 293 | /** |
| 294 | @brief Clipboard event callback |
| 295 | |
| 296 | This callback is called when something is placed on the clipboard. Multiple callbacks may be fired for |
| 297 | multiple clipboard formats if they are supported. The data provided is read-only and may not be modified |
| 298 | by the application. |
| 299 | |
| 300 | @param cookie Cookie supplied in the Synergy context |
| 301 | @param format Clipboard format |
| 302 | @param data Memory area containing the clipboard raw data |
| 303 | @param size Size of clipboard data |
| 304 | **/ |
| 305 | typedef void (*uSynergyClipboardCallback)(uSynergyCookie cookie, enum uSynergyClipboardFormat format, const uint8_t *data, uint32_t size); |
| 306 | |
| 307 | |
| 308 | |
| 309 | //--------------------------------------------------------------------------------------------------------------------- |
| 310 | // Context |
| 311 | //--------------------------------------------------------------------------------------------------------------------- |
| 312 | |
| 313 | |
| 314 | |
| 315 | /** |
| 316 | @brief uSynergy context |
| 317 | **/ |
| 318 | typedef struct |
| 319 | { |
| 320 | /* Mandatory configuration data, filled in by client */ |
| 321 | uSynergyConnectFunc m_connectFunc; /* Connect function */ |
| 322 | uSynergySendFunc m_sendFunc; /* Send data function */ |
| 323 | uSynergyReceiveFunc m_receiveFunc; /* Receive data function */ |
| 324 | uSynergySleepFunc m_sleepFunc; /* Thread sleep function */ |
| 325 | uSynergyGetTimeFunc m_getTimeFunc; /* Get current time function */ |
| 326 | const char* m_clientName; /* Name of Synergy Screen / Client */ |
| 327 | uint16_t m_clientWidth; /* Width of screen */ |
| 328 | uint16_t m_clientHeight; /* Height of screen */ |
| 329 | |
| 330 | /* Optional configuration data, filled in by client */ |
| 331 | uSynergyCookie m_cookie; /* Cookie pointer passed to callback functions (can be NULL) */ |
| 332 | uSynergyTraceFunc m_traceFunc; /* Function for tracing status (can be NULL) */ |
| 333 | uSynergyScreenActiveCallback m_screenActiveCallback; /* Callback for entering and leaving screen */ |
| 334 | uSynergyMouseCallback m_mouseCallback; /* Callback for mouse events */ |
| 335 | uSynergyKeyboardCallback m_keyboardCallback; /* Callback for keyboard events */ |
| 336 | uSynergyJoystickCallback m_joystickCallback; /* Callback for joystick events */ |
| 337 | uSynergyClipboardCallback m_clipboardCallback; /* Callback for clipboard events */ |
| 338 | |
| 339 | /* State data, used internally by client, initialized by uSynergyInit() */ |
| 340 | uSynergyBool m_connected; /* Is our socket connected? */ |
| 341 | uSynergyBool m_hasReceivedHello; /* Have we received a 'Hello' from the server? */ |
| 342 | uSynergyBool m_isCaptured; /* Is Synergy active (i.e. this client is receiving input messages?) */ |
| 343 | uint32_t m_lastMessageTime; /* Time at which last message was received */ |
| 344 | uint32_t m_sequenceNumber; /* Packet sequence number */ |
| 345 | uint8_t m_receiveBuffer[USYNERGY_RECEIVE_BUFFER_SIZE]; /* Receive buffer */ |
| 346 | int m_receiveOfs; /* Receive buffer offset */ |
| 347 | uint8_t m_replyBuffer[USYNERGY_REPLY_BUFFER_SIZE]; /* Reply buffer */ |
| 348 | uint8_t* m_replyCur; /* Write offset into reply buffer */ |
| 349 | uint16_t m_mouseX; /* Mouse X position */ |
| 350 | uint16_t m_mouseY; /* Mouse Y position */ |
| 351 | int16_t m_mouseWheelX; /* Mouse wheel X position */ |
| 352 | int16_t m_mouseWheelY; /* Mouse wheel Y position */ |
| 353 | uSynergyBool m_mouseButtonLeft; /* Mouse left button */ |
| 354 | uSynergyBool m_mouseButtonRight; /* Mouse right button */ |
| 355 | uSynergyBool m_mouseButtonMiddle; /* Mouse middle button */ |
| 356 | int8_t m_joystickSticks[USYNERGY_NUM_JOYSTICKS][4]; /* Joystick stick position in 2 axes for 2 sticks */ |
| 357 | uint16_t m_joystickButtons[USYNERGY_NUM_JOYSTICKS]; /* Joystick button state */ |
| 358 | } uSynergyContext; |
| 359 | |
| 360 | |
| 361 | |
| 362 | //--------------------------------------------------------------------------------------------------------------------- |
| 363 | // Interface |
| 364 | //--------------------------------------------------------------------------------------------------------------------- |
| 365 | |
| 366 | |
| 367 | |
| 368 | /** |
| 369 | @brief Initialize uSynergy context |
| 370 | |
| 371 | This function initializes @a context for use. Call this function directly after |
| 372 | creating the context, before filling in any configuration data in it. Not calling |
| 373 | this function will cause undefined behavior. |
| 374 | |
| 375 | @param context Context to be initialized |
| 376 | **/ |
| 377 | extern void uSynergyInit(uSynergyContext *context); |
| 378 | |
| 379 | |
| 380 | |
| 381 | /** |
| 382 | @brief Update uSynergy |
| 383 | |
| 384 | This function updates uSynergy and does the bulk of the work. It does connection management, |
| 385 | receiving data, reconnecting after errors or timeouts and so on. It assumes that networking |
| 386 | operations are blocking and it can suspend the current thread if it needs to wait. It is |
| 387 | best practice to call uSynergyUpdate from a background thread so it is responsive. |
| 388 | |
| 389 | Because uSynergy relies mostly on blocking calls it will mostly stay in thread sleep state |
| 390 | waiting for system mutexes and won't eat much memory. |
| 391 | |
| 392 | uSynergyUpdate doesn't do any memory allocations or have any side effects beyond those of |
| 393 | the callbacks it calls. |
| 394 | |
| 395 | @param context Context to be updated |
| 396 | **/ |
| 397 | extern void uSynergyUpdate(uSynergyContext *context); |
| 398 | |
| 399 | |
| 400 | |
| 401 | /** |
| 402 | @brief Send clipboard data |
| 403 | |
| 404 | This function sets new clipboard data and sends it to the server. Use this function if |
| 405 | your client cuts or copies data onto the clipboard that it needs to share with the |
| 406 | server. |
| 407 | |
| 408 | Currently there is only support for plaintext, but HTML and image data could be |
| 409 | supported with some effort. |
| 410 | |
| 411 | @param context Context to send clipboard data to |
| 412 | @param text Text to set to the clipboard |
| 413 | **/ |
| 414 | extern void uSynergySendClipboard(uSynergyContext *context, const char *text); |
| 415 | |
| 416 | |
| 417 | |
| 418 | #ifdef __cplusplus |
| 419 | }; |
| 420 | #endif |