uSynergy.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. /*
  2. uSynergy client -- Implementation for the embedded Synergy client library
  3. version 1.0.0, July 7th, 2012
  4. Copyright (c) 2012 Alex Evans
  5. This software is provided 'as-is', without any express or implied
  6. warranty. In no event will the authors be held liable for any damages
  7. arising from the use of this software.
  8. Permission is granted to anyone to use this software for any purpose,
  9. including commercial applications, and to alter it and redistribute it
  10. freely, subject to the following restrictions:
  11. 1. The origin of this software must not be misrepresented; you must not
  12. claim that you wrote the original software. If you use this software
  13. in a product, an acknowledgment in the product documentation would be
  14. appreciated but is not required.
  15. 2. Altered source versions must be plainly marked as such, and must not be
  16. misrepresented as being the original software.
  17. 3. This notice may not be removed or altered from any source
  18. distribution.
  19. */
  20. #include "uSynergy.h"
  21. #include <stdio.h>
  22. #include <string.h>
  23. //---------------------------------------------------------------------------------------------------------------------
  24. // Internal helpers
  25. //---------------------------------------------------------------------------------------------------------------------
  26. /**
  27. @brief Read 16 bit integer in network byte order and convert to native byte order
  28. **/
  29. static int16_t sNetToNative16(const unsigned char *value)
  30. {
  31. #ifdef USYNERGY_LITTLE_ENDIAN
  32. return value[1] | (value[0] << 8);
  33. #else
  34. return value[0] | (value[1] << 8);
  35. #endif
  36. }
  37. /**
  38. @brief Read 32 bit integer in network byte order and convert to native byte order
  39. **/
  40. static int32_t sNetToNative32(const unsigned char *value)
  41. {
  42. #ifdef USYNERGY_LITTLE_ENDIAN
  43. return value[3] | (value[2] << 8) | (value[1] << 16) | (value[0] << 24);
  44. #else
  45. return value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
  46. #endif
  47. }
  48. /**
  49. @brief Trace text to client
  50. **/
  51. static void sTrace(uSynergyContext *context, const char* text)
  52. {
  53. // Don't trace if we don't have a trace function
  54. if (context->m_traceFunc != 0L)
  55. context->m_traceFunc(context->m_cookie, text);
  56. }
  57. /**
  58. @brief Add string to reply packet
  59. **/
  60. static void sAddString(uSynergyContext *context, const char *string)
  61. {
  62. size_t len = strlen(string);
  63. memcpy(context->m_replyCur, string, len);
  64. context->m_replyCur += len;
  65. }
  66. /**
  67. @brief Add uint8 to reply packet
  68. **/
  69. static void sAddUInt8(uSynergyContext *context, uint8_t value)
  70. {
  71. *context->m_replyCur++ = value;
  72. }
  73. /**
  74. @brief Add uint16 to reply packet
  75. **/
  76. static void sAddUInt16(uSynergyContext *context, uint16_t value)
  77. {
  78. uint8_t *reply = context->m_replyCur;
  79. *reply++ = (uint8_t)(value >> 8);
  80. *reply++ = (uint8_t)value;
  81. context->m_replyCur = reply;
  82. }
  83. /**
  84. @brief Add uint32 to reply packet
  85. **/
  86. static void sAddUInt32(uSynergyContext *context, uint32_t value)
  87. {
  88. uint8_t *reply = context->m_replyCur;
  89. *reply++ = (uint8_t)(value >> 24);
  90. *reply++ = (uint8_t)(value >> 16);
  91. *reply++ = (uint8_t)(value >> 8);
  92. *reply++ = (uint8_t)value;
  93. context->m_replyCur = reply;
  94. }
  95. /**
  96. @brief Send reply packet
  97. **/
  98. static uSynergyBool sSendReply(uSynergyContext *context)
  99. {
  100. // Set header size
  101. uint8_t *reply_buf = context->m_replyBuffer;
  102. uint32_t reply_len = (uint32_t)(context->m_replyCur - reply_buf); /* Total size of reply */
  103. uint32_t body_len = reply_len - 4; /* Size of body */
  104. uSynergyBool ret;
  105. reply_buf[0] = (uint8_t)(body_len >> 24);
  106. reply_buf[1] = (uint8_t)(body_len >> 16);
  107. reply_buf[2] = (uint8_t)(body_len >> 8);
  108. reply_buf[3] = (uint8_t)body_len;
  109. // Send reply
  110. ret = context->m_sendFunc(context->m_cookie, context->m_replyBuffer, reply_len);
  111. // Reset reply buffer write pointer
  112. context->m_replyCur = context->m_replyBuffer+4;
  113. return ret;
  114. }
  115. /**
  116. @brief Call mouse callback after a mouse event
  117. **/
  118. static void sSendMouseCallback(uSynergyContext *context)
  119. {
  120. // Skip if no callback is installed
  121. if (context->m_mouseCallback == 0L)
  122. return;
  123. // Send callback
  124. context->m_mouseCallback(context->m_cookie, context->m_mouseX, context->m_mouseY, context->m_mouseWheelX,
  125. context->m_mouseWheelY, context->m_mouseButtonLeft, context->m_mouseButtonRight, context->m_mouseButtonMiddle);
  126. }
  127. /**
  128. @brief Send keyboard callback when a key has been pressed or released
  129. **/
  130. static void sSendKeyboardCallback(uSynergyContext *context, uint16_t key, uint16_t modifiers, uSynergyBool down, uSynergyBool repeat)
  131. {
  132. // Skip if no callback is installed
  133. if (context->m_keyboardCallback == 0L)
  134. return;
  135. // Send callback
  136. context->m_keyboardCallback(context->m_cookie, key, modifiers, down, repeat);
  137. }
  138. /**
  139. @brief Send joystick callback
  140. **/
  141. static void sSendJoystickCallback(uSynergyContext *context, uint8_t joyNum)
  142. {
  143. int8_t *sticks;
  144. // Skip if no callback is installed
  145. if (context->m_joystickCallback == 0L)
  146. return;
  147. // Send callback
  148. sticks = context->m_joystickSticks[joyNum];
  149. context->m_joystickCallback(context->m_cookie, joyNum, context->m_joystickButtons[joyNum], sticks[0], sticks[1], sticks[2], sticks[3]);
  150. }
  151. /**
  152. @brief Parse a single client message, update state, send callbacks and send replies
  153. **/
  154. #define USYNERGY_IS_PACKET(pkt_id) memcmp(message+4, pkt_id, 4)==0
  155. static void sProcessMessage(uSynergyContext *context, const uint8_t *message)
  156. {
  157. // We have a packet!
  158. if (memcmp(message+4, "Synergy", 7)==0)
  159. {
  160. // Welcome message
  161. // kMsgHello = "Synergy%2i%2i"
  162. // kMsgHelloBack = "Synergy%2i%2i%s"
  163. sAddString(context, "Synergy");
  164. sAddUInt16(context, USYNERGY_PROTOCOL_MAJOR);
  165. sAddUInt16(context, USYNERGY_PROTOCOL_MINOR);
  166. sAddUInt32(context, (uint32_t)strlen(context->m_clientName));
  167. sAddString(context, context->m_clientName);
  168. if (!sSendReply(context))
  169. {
  170. // Send reply failed, let's try to reconnect
  171. sTrace(context, "SendReply failed, trying to reconnect in a second");
  172. context->m_connected = USYNERGY_FALSE;
  173. context->m_sleepFunc(context->m_cookie, 1000);
  174. }
  175. else
  176. {
  177. // Let's assume we're connected
  178. char buffer[256+1];
  179. sprintf(buffer, "Connected as client \"%s\"", context->m_clientName);
  180. sTrace(context, buffer);
  181. context->m_hasReceivedHello = USYNERGY_TRUE;
  182. }
  183. return;
  184. }
  185. else if (USYNERGY_IS_PACKET("QINF"))
  186. {
  187. // Screen info. Reply with DINF
  188. // kMsgQInfo = "QINF"
  189. // kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i"
  190. uint16_t x = 0, y = 0, warp = 0;
  191. sAddString(context, "DINF");
  192. sAddUInt16(context, x);
  193. sAddUInt16(context, y);
  194. sAddUInt16(context, context->m_clientWidth);
  195. sAddUInt16(context, context->m_clientHeight);
  196. sAddUInt16(context, warp);
  197. sAddUInt16(context, 0); // mx?
  198. sAddUInt16(context, 0); // my?
  199. sSendReply(context);
  200. return;
  201. }
  202. else if (USYNERGY_IS_PACKET("CIAK"))
  203. {
  204. // Do nothing?
  205. // kMsgCInfoAck = "CIAK"
  206. return;
  207. }
  208. else if (USYNERGY_IS_PACKET("CROP"))
  209. {
  210. // Do nothing?
  211. // kMsgCResetOptions = "CROP"
  212. return;
  213. }
  214. else if (USYNERGY_IS_PACKET("CINN"))
  215. {
  216. // Screen enter. Reply with CNOP
  217. // kMsgCEnter = "CINN%2i%2i%4i%2i"
  218. // Obtain the Synergy sequence number
  219. context->m_sequenceNumber = sNetToNative32(message + 12);
  220. context->m_isCaptured = USYNERGY_TRUE;
  221. // Call callback
  222. if (context->m_screenActiveCallback != 0L)
  223. context->m_screenActiveCallback(context->m_cookie, USYNERGY_TRUE);
  224. }
  225. else if (USYNERGY_IS_PACKET("COUT"))
  226. {
  227. // Screen leave
  228. // kMsgCLeave = "COUT"
  229. context->m_isCaptured = USYNERGY_FALSE;
  230. // Call callback
  231. if (context->m_screenActiveCallback != 0L)
  232. context->m_screenActiveCallback(context->m_cookie, USYNERGY_FALSE);
  233. }
  234. else if (USYNERGY_IS_PACKET("DMDN"))
  235. {
  236. // Mouse down
  237. // kMsgDMouseDown = "DMDN%1i"
  238. char btn = message[8]-1;
  239. if (btn==2)
  240. context->m_mouseButtonRight = USYNERGY_TRUE;
  241. else if (btn==1)
  242. context->m_mouseButtonMiddle = USYNERGY_TRUE;
  243. else
  244. context->m_mouseButtonLeft = USYNERGY_TRUE;
  245. sSendMouseCallback(context);
  246. }
  247. else if (USYNERGY_IS_PACKET("DMUP"))
  248. {
  249. // Mouse up
  250. // kMsgDMouseUp = "DMUP%1i"
  251. char btn = message[8]-1;
  252. if (btn==2)
  253. context->m_mouseButtonRight = USYNERGY_FALSE;
  254. else if (btn==1)
  255. context->m_mouseButtonMiddle = USYNERGY_FALSE;
  256. else
  257. context->m_mouseButtonLeft = USYNERGY_FALSE;
  258. sSendMouseCallback(context);
  259. }
  260. else if (USYNERGY_IS_PACKET("DMMV"))
  261. {
  262. // Mouse move. Reply with CNOP
  263. // kMsgDMouseMove = "DMMV%2i%2i"
  264. context->m_mouseX = sNetToNative16(message+8);
  265. context->m_mouseY = sNetToNative16(message+10);
  266. sSendMouseCallback(context);
  267. }
  268. else if (USYNERGY_IS_PACKET("DMWM"))
  269. {
  270. // Mouse wheel
  271. // kMsgDMouseWheel = "DMWM%2i%2i"
  272. // kMsgDMouseWheel1_0 = "DMWM%2i"
  273. context->m_mouseWheelX += sNetToNative16(message+8);
  274. context->m_mouseWheelY += sNetToNative16(message+10);
  275. sSendMouseCallback(context);
  276. }
  277. else if (USYNERGY_IS_PACKET("DKDN"))
  278. {
  279. // Key down
  280. // kMsgDKeyDown = "DKDN%2i%2i%2i"
  281. // kMsgDKeyDown1_0 = "DKDN%2i%2i"
  282. //uint16_t id = sNetToNative16(message+8);
  283. uint16_t mod = sNetToNative16(message+10);
  284. uint16_t key = sNetToNative16(message+12);
  285. sSendKeyboardCallback(context, key, mod, USYNERGY_TRUE, USYNERGY_FALSE);
  286. }
  287. else if (USYNERGY_IS_PACKET("DKRP"))
  288. {
  289. // Key repeat
  290. // kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i"
  291. // kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i"
  292. uint16_t mod = sNetToNative16(message+10);
  293. // uint16_t count = sNetToNative16(message+12);
  294. uint16_t key = sNetToNative16(message+14);
  295. sSendKeyboardCallback(context, key, mod, USYNERGY_TRUE, USYNERGY_TRUE);
  296. }
  297. else if (USYNERGY_IS_PACKET("DKUP"))
  298. {
  299. // Key up
  300. // kMsgDKeyUp = "DKUP%2i%2i%2i"
  301. // kMsgDKeyUp1_0 = "DKUP%2i%2i"
  302. //uint16 id=Endian::sNetToNative(sbuf[4]);
  303. uint16_t mod = sNetToNative16(message+10);
  304. uint16_t key = sNetToNative16(message+12);
  305. sSendKeyboardCallback(context, key, mod, USYNERGY_FALSE, USYNERGY_FALSE);
  306. }
  307. else if (USYNERGY_IS_PACKET("DGBT"))
  308. {
  309. // Joystick buttons
  310. // kMsgDGameButtons = "DGBT%1i%2i";
  311. uint8_t joy_num = message[8];
  312. if (joy_num<USYNERGY_NUM_JOYSTICKS)
  313. {
  314. // Copy button state, then send callback
  315. context->m_joystickButtons[joy_num] = (message[9] << 8) | message[10];
  316. sSendJoystickCallback(context, joy_num);
  317. }
  318. }
  319. else if (USYNERGY_IS_PACKET("DGST"))
  320. {
  321. // Joystick sticks
  322. // kMsgDGameSticks = "DGST%1i%1i%1i%1i%1i";
  323. uint8_t joy_num = message[8];
  324. if (joy_num<USYNERGY_NUM_JOYSTICKS)
  325. {
  326. // Copy stick state, then send callback
  327. memcpy(context->m_joystickSticks[joy_num], message+9, 4);
  328. sSendJoystickCallback(context, joy_num);
  329. }
  330. }
  331. else if (USYNERGY_IS_PACKET("DSOP"))
  332. {
  333. // Set options
  334. // kMsgDSetOptions = "DSOP%4I"
  335. }
  336. else if (USYNERGY_IS_PACKET("CALV"))
  337. {
  338. // Keepalive, reply with CALV and then CNOP
  339. // kMsgCKeepAlive = "CALV"
  340. sAddString(context, "CALV");
  341. sSendReply(context);
  342. // now reply with CNOP
  343. }
  344. else if (USYNERGY_IS_PACKET("DCLP"))
  345. {
  346. // Clipboard message
  347. // kMsgDClipboard = "DCLP%1i%4i%s"
  348. //
  349. // The clipboard message contains:
  350. // 1 uint32: The size of the message
  351. // 4 chars: The identifier ("DCLP")
  352. // 1 uint8: The clipboard index
  353. // 1 uint32: The sequence number. It's zero, because this message is always coming from the server?
  354. // 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)).
  355. // 1 uint32: The number of formats present in the message
  356. // And then 'number of formats' times the following:
  357. // 1 uint32: The format of the clipboard data
  358. // 1 uint32: The size n of the clipboard data
  359. // n uint8: The clipboard data
  360. const uint8_t * parse_msg = message+17;
  361. uint32_t num_formats = sNetToNative32(parse_msg);
  362. parse_msg += 4;
  363. for (; num_formats; num_formats--)
  364. {
  365. // Parse clipboard format header
  366. uint32_t format = sNetToNative32(parse_msg);
  367. uint32_t size = sNetToNative32(parse_msg+4);
  368. parse_msg += 8;
  369. // Call callback
  370. if (context->m_clipboardCallback)
  371. context->m_clipboardCallback(context->m_cookie, format, parse_msg, size);
  372. parse_msg += size;
  373. }
  374. }
  375. else
  376. {
  377. // Unknown packet, could be any of these
  378. // kMsgCNoop = "CNOP"
  379. // kMsgCClose = "CBYE"
  380. // kMsgCClipboard = "CCLP%1i%4i"
  381. // kMsgCScreenSaver = "CSEC%1i"
  382. // kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i"
  383. // kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i"
  384. // kMsgDMouseRelMove = "DMRM%2i%2i"
  385. // kMsgEIncompatible = "EICV%2i%2i"
  386. // kMsgEBusy = "EBSY"
  387. // kMsgEUnknown = "EUNK"
  388. // kMsgEBad = "EBAD"
  389. char buffer[64];
  390. sprintf(buffer, "Unknown packet '%c%c%c%c'", message[4], message[5], message[6], message[7]);
  391. sTrace(context, buffer);
  392. return;
  393. }
  394. // Reply with CNOP maybe?
  395. sAddString(context, "CNOP");
  396. sSendReply(context);
  397. }
  398. #undef USYNERGY_IS_PACKET
  399. /**
  400. @brief Mark context as being disconnected
  401. **/
  402. static void sSetDisconnected(uSynergyContext *context)
  403. {
  404. context->m_connected = USYNERGY_FALSE;
  405. context->m_hasReceivedHello = USYNERGY_FALSE;
  406. context->m_isCaptured = USYNERGY_FALSE;
  407. context->m_replyCur = context->m_replyBuffer + 4;
  408. context->m_sequenceNumber = 0;
  409. }
  410. /**
  411. @brief Update a connected context
  412. **/
  413. static void sUpdateContext(uSynergyContext *context)
  414. {
  415. /* Receive data (blocking) */
  416. int receive_size = USYNERGY_RECEIVE_BUFFER_SIZE - context->m_receiveOfs;
  417. int num_received = 0;
  418. int packlen = 0;
  419. if (context->m_receiveFunc(context->m_cookie, context->m_receiveBuffer + context->m_receiveOfs, receive_size, &num_received) == USYNERGY_FALSE)
  420. {
  421. /* Receive failed, let's try to reconnect */
  422. char buffer[128];
  423. sprintf(buffer, "Receive failed (%d bytes asked, %d bytes received), trying to reconnect in a second", receive_size, num_received);
  424. sTrace(context, buffer);
  425. sSetDisconnected(context);
  426. context->m_sleepFunc(context->m_cookie, 1000);
  427. return;
  428. }
  429. context->m_receiveOfs += num_received;
  430. /* If we didn't receive any data then we're probably still polling to get connected and
  431. therefore not getting any data back. To avoid overloading the system with a Synergy
  432. thread that would hammer on polling, we let it rest for a bit if there's no data. */
  433. if (num_received == 0)
  434. context->m_sleepFunc(context->m_cookie, 500);
  435. /* Check for timeouts */
  436. if (context->m_hasReceivedHello)
  437. {
  438. uint32_t cur_time = context->m_getTimeFunc();
  439. if (num_received == 0)
  440. {
  441. /* Timeout after 2 secs of inactivity (we received no CALV) */
  442. if ((cur_time - context->m_lastMessageTime) > USYNERGY_IDLE_TIMEOUT)
  443. sSetDisconnected(context);
  444. }
  445. else
  446. context->m_lastMessageTime = cur_time;
  447. }
  448. /* Eat packets */
  449. for (;;)
  450. {
  451. /* Grab packet length and bail out if the packet goes beyond the end of the buffer */
  452. packlen = sNetToNative32(context->m_receiveBuffer);
  453. if (packlen+4 > context->m_receiveOfs)
  454. break;
  455. /* Process message */
  456. sProcessMessage(context, context->m_receiveBuffer);
  457. /* Move packet to front of buffer */
  458. memmove(context->m_receiveBuffer, context->m_receiveBuffer+packlen+4, context->m_receiveOfs-packlen-4);
  459. context->m_receiveOfs -= packlen+4;
  460. }
  461. /* Throw away over-sized packets */
  462. if (packlen > USYNERGY_RECEIVE_BUFFER_SIZE)
  463. {
  464. /* Oversized packet, ditch tail end */
  465. char buffer[128];
  466. sprintf(buffer, "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);
  467. sTrace(context, buffer);
  468. num_received = context->m_receiveOfs-4; // 4 bytes for the size field
  469. while (num_received != packlen)
  470. {
  471. int buffer_left = packlen - num_received;
  472. int to_receive = buffer_left < USYNERGY_RECEIVE_BUFFER_SIZE ? buffer_left : USYNERGY_RECEIVE_BUFFER_SIZE;
  473. int ditch_received = 0;
  474. if (context->m_receiveFunc(context->m_cookie, context->m_receiveBuffer, to_receive, &ditch_received) == USYNERGY_FALSE)
  475. {
  476. /* Receive failed, let's try to reconnect */
  477. sTrace(context, "Receive failed, trying to reconnect in a second");
  478. sSetDisconnected(context);
  479. context->m_sleepFunc(context->m_cookie, 1000);
  480. break;
  481. }
  482. else
  483. {
  484. num_received += ditch_received;
  485. }
  486. }
  487. context->m_receiveOfs = 0;
  488. }
  489. }
  490. //---------------------------------------------------------------------------------------------------------------------
  491. // Public interface
  492. //---------------------------------------------------------------------------------------------------------------------
  493. /**
  494. @brief Initialize uSynergy context
  495. **/
  496. void uSynergyInit(uSynergyContext *context)
  497. {
  498. /* Zero memory */
  499. memset(context, 0, sizeof(uSynergyContext));
  500. /* Initialize to default state */
  501. sSetDisconnected(context);
  502. }
  503. /**
  504. @brief Update uSynergy
  505. **/
  506. void uSynergyUpdate(uSynergyContext *context)
  507. {
  508. if (context->m_connected)
  509. {
  510. /* Update context, receive data, call callbacks */
  511. sUpdateContext(context);
  512. }
  513. else
  514. {
  515. /* Try to connect */
  516. if (context->m_connectFunc(context->m_cookie))
  517. context->m_connected = USYNERGY_TRUE;
  518. }
  519. }
  520. /**
  521. @brief Send clipboard data
  522. **/
  523. void uSynergySendClipboard(uSynergyContext *context, const char *text)
  524. {
  525. // Calculate maximum size that will fit in a reply packet
  526. uint32_t overhead_size = 4 + /* Message size */
  527. 4 + /* Message ID */
  528. 1 + /* Clipboard index */
  529. 4 + /* Sequence number */
  530. 4 + /* Rest of message size (because it's a Synergy string from here on) */
  531. 4 + /* Number of clipboard formats */
  532. 4 + /* Clipboard format */
  533. 4; /* Clipboard data length */
  534. uint32_t max_length = USYNERGY_REPLY_BUFFER_SIZE - overhead_size;
  535. // Clip text to max length
  536. uint32_t text_length = (uint32_t)strlen(text);
  537. if (text_length > max_length)
  538. {
  539. char buffer[128];
  540. sprintf(buffer, "Clipboard buffer too small, clipboard truncated at %d characters", max_length);
  541. sTrace(context, buffer);
  542. text_length = max_length;
  543. }
  544. // Assemble packet
  545. sAddString(context, "DCLP");
  546. sAddUInt8(context, 0); /* Clipboard index */
  547. sAddUInt32(context, context->m_sequenceNumber);
  548. sAddUInt32(context, 4+4+4+text_length); /* Rest of message size: numFormats, format, length, data */
  549. sAddUInt32(context, 1); /* Number of formats (only text for now) */
  550. sAddUInt32(context, USYNERGY_CLIPBOARD_FORMAT_TEXT);
  551. sAddUInt32(context, text_length);
  552. sAddString(context, text);
  553. sSendReply(context);
  554. }