imgui

FORK: Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
git clone https://git.neptards.moe/neptards/imgui.git
Log | Files | Refs

uSynergy.c (18289B)


      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 **/
     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 **/
     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 **/
     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 **/
     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 **/
     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 **/
    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 **/
    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 **/
    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 **/
    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 **/
    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 **/
    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
    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 **/
    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 **/
    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 **/
    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 **/
    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 **/
    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 }