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