2019-10-30 18:50:11 +03:00
/*
uSynergy client - - Implementation for the embedded Synergy client library
version 1.0 .0 , July 7 th , 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 <stdio.h>
# include <string.h>
//---------------------------------------------------------------------------------------------------------------------
// 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 [ 1 ] | ( value [ 0 ] < < 8 ) ;
# else
return value [ 0 ] | ( value [ 1 ] < < 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 [ 3 ] | ( value [ 2 ] < < 8 ) | ( value [ 1 ] < < 16 ) | ( value [ 0 ] < < 24 ) ;
# else
return value [ 0 ] | ( value [ 1 ] < < 8 ) | ( value [ 2 ] < < 16 ) | ( value [ 3 ] < < 24 ) ;
# endif
}
/**
@ brief Trace text to client
* */
static void sTrace ( uSynergyContext * context , const char * text )
{
// Don't trace if we don't have a trace function
if ( context - > m_traceFunc ! = 0L )
context - > m_traceFunc ( context - > m_cookie , text ) ;
}
/**
@ 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 , uint16_t modifiers , uSynergyBool down , uSynergyBool repeat )
{
// Skip if no callback is installed
if ( context - > m_keyboardCallback = = 0L )
return ;
// Send callback
context - > m_keyboardCallback ( context - > m_cookie , key , modifiers , 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
char buffer [ 256 + 1 ] ;
sprintf ( buffer , " Connected as client \" %s \" " , context - > m_clientName ) ;
sTrace ( context , buffer ) ;
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 , mod , USYNERGY_TRUE , USYNERGY_FALSE ) ;
}
else if ( USYNERGY_IS_PACKET ( " DKRP " ) )
{
// Key repeat
// kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i"
// kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i"
uint16_t mod = sNetToNative16 ( message + 10 ) ;
// uint16_t count = sNetToNative16(message+12);
uint16_t key = sNetToNative16 ( message + 14 ) ;
sSendKeyboardCallback ( context , key , mod , USYNERGY_TRUE , USYNERGY_TRUE ) ;
}
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 , mod , USYNERGY_FALSE , USYNERGY_FALSE ) ;
}
else if ( USYNERGY_IS_PACKET ( " DGBT " ) )
{
// Joystick buttons
// kMsgDGameButtons = "DGBT%1i%2i";
uint8_t joy_num = message [ 8 ] ;
if ( joy_num < USYNERGY_NUM_JOYSTICKS )
{
// Copy button state, then send callback
context - > m_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_num < USYNERGY_NUM_JOYSTICKS )
{
// Copy stick state, then send callback
memcpy ( context - > m_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"
char buffer [ 64 ] ;
sprintf ( buffer , " Unknown packet '%c%c%c%c' " , message [ 4 ] , message [ 5 ] , message [ 6 ] , message [ 7 ] ) ;
sTrace ( context , buffer ) ;
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 + context - > m_receiveOfs , receive_size , & num_received ) = = USYNERGY_FALSE )
{
/* Receive failed, let's try to reconnect */
char buffer [ 128 ] ;
sprintf ( buffer , " Receive failed (%d bytes asked, %d bytes received), trying to reconnect in a second " , receive_size , num_received ) ;
sTrace ( context , buffer ) ;
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 */
char buffer [ 128 ] ;
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 ) ;
sTrace ( context , buffer ) ;
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 , " 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 )
{
char buffer [ 128 ] ;
sprintf ( buffer , " Clipboard buffer too small, clipboard truncated at %d characters " , max_length ) ;
sTrace ( context , buffer ) ;
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 ) ;
}