--- /dev/null
+/*****************************************************************************
+ * remoteosd.c: remote osd over vnc filter module
+ *****************************************************************************
+ * Copyright (C) 2007-2008 Matthias Bauer
+ * $Id$
+ *
+ * Authors: Matthias Bauer <matthias dot bauer #_at_# gmx dot ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implid warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * RemoteOSD uses the RFB-Protocol of VNC to display an On-Screen-Display
+ * menu generated by a streaming server as overlay for the streamed video.
+ *
+ * The streaming server that implements this is the ffnetdev plugin for VDR.
+ * VDR (VideoDiskRecorder) is an Linux based OpenSource harddisk recorder
+ * software.
+ * The VDR ffnetdev plugin emulates the hardware MPEG decoder and streams the
+ * video over the network instead of hardware video outputs.
+ * The OSD menu of VDR is offered with the RFB protocol to a VNC client.
+ *
+ * In fact this video-filter is a simple VNC client that could be also used to
+ * connect to a real VNC host.
+ * Only 8-bit color is supported at the moment.
+ * Using password protected VNC hosts is supported but not recommended, because
+ * you need to insert the used password in the plugin configuration page of
+ * VLC configuration in plain text and it's saved in plain text.
+ *****************************************************************************/
+
+//#define VNC_DEBUG
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout.h>
+
+#include "vlc_filter.h"
+#include "filter_common.h"
+#include "vlc_image.h"
+#include "vlc_osd.h"
+#include "vlc_keys.h"
+
+#include <vlc_network.h>
+#include <gcrypt.h> /* to encrypt password */
+#include <vlc_gcrypt.h>
+
+#define CHALLENGESIZE 16
+#define MAX_VNC_SERVER_NAME_LENGTH 255
+
+#include "remoteosd_rfbproto.h" /* type definitions of the RFB protocol for VNC */
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+/* subfilter functions */
+static int CreateFilter ( vlc_object_t * );
+static void DestroyFilter( vlc_object_t * );
+static subpicture_t *Filter( filter_t *, mtime_t );
+
+static int MouseEvent ( vlc_object_t *p_this, char const *psz_var,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data );
+
+static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data );
+
+static void stop_osdvnc ( filter_t *p_filter );
+
+static void vnc_worker_thread ( vlc_object_t *p_thread_obj );
+
+static void update_request_thread( vlc_object_t *p_thread_obj );
+
+static bool open_vnc_connection ( filter_t *p_filter );
+
+static bool handshaking ( filter_t *p_filter );
+
+static bool process_server_message ( filter_t *p_filter,
+ rfbServerToClientMsg *msg );
+
+static bool read_exact( filter_t *p_filter,
+ int i_socket,
+ char* p_readbuf,
+ int i_bytes );
+
+static bool write_exact( filter_t *p_filter,
+ int i_socket,
+ char* p_writebuf,
+ int i_bytes );
+
+static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
+ int r, int g, int b );
+
+static inline bool fill_rect( filter_sys_t* p_sys,
+ uint16_t i_x, uint16_t i_y,
+ uint16_t i_w, uint16_t i_h,
+ uint8_t i_color );
+
+static inline bool raw_line( filter_sys_t* p_sys,
+ uint16_t i_x, uint16_t i_y,
+ uint16_t i_w );
+
+static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
+
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define READ_BUFFER_SIZE 1000000
+
+#define RMTOSD_HOST_TEXT N_("VNC Host")
+#define RMTOSD_HOST_LONGTEXT N_( \
+ "VNC hostname or IP address." )
+
+#define RMTOSD_PORT_TEXT N_("VNC Port")
+#define RMTOSD_PORT_LONGTEXT N_( \
+ "VNC portnumber." )
+
+#define RMTOSD_PASSWORD_TEXT N_("VNC Password")
+#define RMTOSD_PASSWORD_LONGTEXT N_( \
+ "VNC password." )
+
+#define RMTOSD_UPDATE_TEXT N_("VNC poll interval" )
+#define RMTOSD_UPDATE_LONGTEXT N_( \
+ "In this interval an update from VNC is requested, default every 300 ms. ")
+
+#define RMTOSD_POLL_TEXT N_("VNC polling")
+#define RMTOSD_POLL_LONGTEXT N_( \
+ "Activate VNC polling. Do NOT activate for use as VDR ffnetdev client." )
+
+#define RMTOSD_MOUSE_TEXT N_("Mouse events")
+#define RMTOSD_MOUSE_LONGTEXT N_( \
+ "Send mouse events to VNC host. Not needed for use as VDR ffnetdev client." )
+
+#define RMTOSD_KEYS_TEXT N_("Key events")
+#define RMTOSD_KEYS_LONGTEXT N_( \
+ "Send key events to VNC host." )
+
+#define RMTOSD_ALPHA_TEXT N_("Alpha transparency value (default 255)")
+#define RMTOSD_ALPHA_LONGTEXT N_( \
+ "The transparency of the OSD VNC can be changed by giving a value " \
+ "between 0 and 255. A lower value specifies more transparency a higher " \
+ "means less transparency. The default is being not transparent " \
+ "(value 255) the minimum is fully transparent (value 0)." )
+
+#define RMTOSD_CFG "rmtosd-"
+
+#define RMTOSD_UPDATE_MIN 200
+#define RMTOSD_UPDATE_DEFAULT 1000
+#define RMTOSD_UPDATE_MAX 300
+
+
+vlc_module_begin();
+ set_description( N_("Remote-OSD over VNC") );
+ set_capability( "sub filter", 100 );
+ set_shortname( N_("Remote-OSD") );
+ set_category( CAT_VIDEO );
+ set_subcategory( SUBCAT_VIDEO_SUBPIC );
+ add_shortcut( "rmtosd" );
+ set_callbacks( CreateFilter, DestroyFilter );
+
+ add_string( RMTOSD_CFG "host", "myvdr", NULL, RMTOSD_HOST_TEXT,
+ RMTOSD_HOST_LONGTEXT, false );
+ add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF, NULL,
+ RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false );
+ add_password( RMTOSD_CFG "password", "", NULL, RMTOSD_PASSWORD_TEXT,
+ RMTOSD_PASSWORD_LONGTEXT, false );
+ add_integer_with_range( RMTOSD_CFG "update", RMTOSD_UPDATE_DEFAULT,
+ RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, NULL, RMTOSD_UPDATE_TEXT,
+ RMTOSD_UPDATE_LONGTEXT, true );
+ add_bool( RMTOSD_CFG "vnc-polling", 0, NULL,
+ RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false );
+ add_bool( RMTOSD_CFG "mouse-events", 0, NULL,
+ RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false );
+ add_bool( RMTOSD_CFG "key-events", 0, NULL,
+ RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false );
+ add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255, NULL,
+ RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true );
+
+vlc_module_end();
+
+/*****************************************************************************
+ * Sub filter code
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+struct filter_sys_t
+{
+ VLC_COMMON_MEMBERS
+
+ bool b_need_update; /* VNC picture is updated, do update the OSD*/
+ mtime_t i_vnc_poll_interval; /* Update the OSD menu every n ms */
+
+ uint8_t i_alpha; /* alpha transparency value */
+
+ char *psz_host; /* VNC host */
+ int i_port;
+
+ char *psz_passwd; /* VNC password */
+
+ bool b_vnc_poll; /* Activate VNC polling ? */
+ bool b_vnc_mouse_events; /* Send MouseEvents ? */
+ bool b_vnc_key_events; /* Send KeyEvents ? */
+
+ bool b_connection_active; /* Handshaking finished ? */
+
+ vlc_mutex_t lock; /* To lock for read/write on picture */
+
+ picture_t *p_pic; /* The picture with OSD data from VNC */
+
+ vout_thread_t *p_vout; /* Pointer to video-out thread */
+
+ int i_socket; /* Socket used for VNC */
+
+ uint16_t i_vnc_width; /* The with of the VNC screen */
+ uint16_t i_vnc_height; /* The height of the VNC screen */
+ uint32_t i_vnc_pixels; /* The pixels of the VNC screen */
+
+ bool b_alpha_from_vnc; /* Special ffnetdev alpha feature enabled ? */
+
+ char read_buffer[READ_BUFFER_SIZE];
+
+ bool b_continue;
+
+ vlc_object_t* p_worker_thread;
+ vlc_object_t* p_update_request_thread;
+
+ uint8_t ar_color_table_yuv[256][4];
+};
+
+/*****************************************************************************
+ * CreateFilter: Create the filter and open the definition file
+ *****************************************************************************/
+static int CreateFilter ( vlc_object_t *p_this )
+{
+ filter_t *p_filter = (filter_t *)p_this;
+ filter_sys_t *p_sys = NULL;
+
+ msg_Dbg( p_filter, "Creating vnc osd filter..." );
+
+ p_filter->p_sys = p_sys = (filter_sys_t *) malloc( sizeof(filter_sys_t) );
+ if( !p_filter->p_sys )
+ {
+ msg_Err( p_filter, "out of memory" );
+ return VLC_ENOMEM;
+ }
+ memset( p_sys, 0, sizeof(filter_sys_t) );
+
+ /* Populating struct */
+ vlc_mutex_init( &p_sys->lock );
+ p_sys->b_continue = true;
+ p_sys->i_socket = -1;
+
+ p_sys->psz_host = var_CreateGetString( p_this, RMTOSD_CFG "host" );
+ if( EMPTY_STR(p_sys->psz_host) )
+ {
+ msg_Err( p_filter, "unable to get vnc host" );
+ goto error;
+ }
+
+ p_sys->psz_passwd = var_CreateGetString( p_this, RMTOSD_CFG "password" );
+ if( !p_sys->psz_passwd )
+ {
+ msg_Err( p_filter, "unable to get vnc password" );
+ goto error;
+ }
+
+ p_sys->i_port = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "port" );
+
+ p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "alpha" );
+
+ /* in miliseconds, 0 disables polling, should not be lower than 100 */
+ p_sys->i_vnc_poll_interval = var_CreateGetIntegerCommand( p_this,
+ RMTOSD_CFG "update" );
+ if ( p_sys->i_vnc_poll_interval < 100)
+ {
+ p_sys->i_vnc_poll_interval = 100;
+ }
+
+ for ( int i = 0; i < 256; i++ )
+ {
+ p_sys->ar_color_table_yuv[i][0] = 255;
+ p_sys->ar_color_table_yuv[i][1] = 255;
+ p_sys->ar_color_table_yuv[i][2] = 255;
+ p_sys->ar_color_table_yuv[i][3] = 255;
+ }
+
+ p_sys->b_vnc_poll = var_CreateGetBoolCommand( p_this,
+ RMTOSD_CFG "vnc-polling" );
+ p_sys->b_vnc_mouse_events = var_CreateGetBoolCommand( p_this,
+ RMTOSD_CFG "mouse-events" );
+ p_sys->b_vnc_key_events = var_CreateGetBoolCommand( p_this,
+ RMTOSD_CFG "key-events" );
+
+ /* Keep track of OSD Events */
+ p_sys->b_need_update = false;
+
+ /* Attach subpicture filter callback */
+ p_filter->pf_sub_filter = Filter;
+
+ p_sys->p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_PARENT );
+
+ if( p_sys->p_vout )
+ {
+ var_AddCallback( p_sys->p_vout, "mouse-moved",
+ MouseEvent, p_this );
+ var_AddCallback( p_sys->p_vout, "mouse-button-down",
+ MouseEvent, p_this );
+ var_AddCallback( p_sys->p_vout->p_libvlc, "key-pressed",
+ KeyEvent, p_this );
+ }
+
+ es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
+ p_filter->fmt_out.i_priority = 0;
+
+ /* create the vnc worker thread */
+ p_sys->p_worker_thread = vlc_object_create( p_this, VLC_OBJECT_GENERIC );
+ vlc_object_attach( p_sys->p_worker_thread, p_this );
+ if( vlc_thread_create( p_sys->p_worker_thread, "vnc worker thread",
+ vnc_worker_thread,
+ VLC_THREAD_PRIORITY_LOW, false ) )
+ {
+ vlc_object_detach( p_sys->p_worker_thread );
+ vlc_object_release( p_sys->p_worker_thread );
+ p_sys->p_worker_thread = NULL;
+ msg_Err( p_filter, "cannot spawn vnc message reader thread" );
+ goto error;
+ }
+
+ msg_Dbg( p_filter, "osdvnc filter started" );
+
+ return VLC_SUCCESS;
+
+error:
+ msg_Err( p_filter, "osdvnc filter discarded" );
+
+ p_sys->b_continue = false;
+
+ stop_osdvnc( p_filter );
+
+ free( p_sys->psz_host );
+ free( p_sys->psz_passwd );
+ free( p_sys );
+
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * DestroyFilter: Make a clean exit of this plugin
+ *****************************************************************************/
+static void DestroyFilter( vlc_object_t *p_this )
+{
+ filter_t *p_filter = (filter_t*)p_this;
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ msg_Dbg( p_filter, "DestroyFilter called." );
+
+ stop_osdvnc( p_filter );
+
+ if( p_sys->p_vout )
+ {
+ var_DelCallback( p_sys->p_vout, "mouse-moved",
+ MouseEvent, p_this );
+ var_DelCallback( p_sys->p_vout, "mouse-button-down",
+ MouseEvent, p_this );
+ var_DelCallback( p_sys->p_vout->p_libvlc, "key-pressed",
+ KeyEvent, p_this );
+
+ vlc_object_release( p_sys->p_vout );
+ p_sys->p_vout = NULL;
+ }
+
+ var_Destroy( p_this, RMTOSD_CFG "host" );
+ var_Destroy( p_this, RMTOSD_CFG "port" );
+ var_Destroy( p_this, RMTOSD_CFG "password" );
+ var_Destroy( p_this, RMTOSD_CFG "update" );
+ var_Destroy( p_this, RMTOSD_CFG "vnc-polling" );
+ var_Destroy( p_this, RMTOSD_CFG "mouse-events" );
+ var_Destroy( p_this, RMTOSD_CFG "key-events" );
+ var_Destroy( p_this, RMTOSD_CFG "alpha" );
+
+ free( p_sys->psz_host );
+ free( p_sys->psz_passwd );
+ free( p_sys );
+}
+
+static void stop_osdvnc ( filter_t *p_filter )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ if (p_sys->i_socket >= 0)
+ {
+ net_Close(p_sys->i_socket);
+ }
+ p_sys->b_continue = false; /* this causes the threads to stop */
+
+ if ( p_sys->p_worker_thread )
+ {
+ msg_Dbg( p_filter, "joining worker_thread" );
+ vlc_thread_join( p_sys->p_worker_thread );
+ vlc_object_detach( p_sys->p_worker_thread );
+ vlc_object_release( p_sys->p_worker_thread );
+ msg_Dbg( p_filter, "released worker_thread" );
+ }
+
+ if ( p_sys->p_update_request_thread )
+ {
+ msg_Dbg( p_filter, "joining update_request_thread" );
+ vlc_thread_join( p_sys->p_update_request_thread );
+ vlc_object_detach( p_sys->p_update_request_thread );
+ vlc_object_release( p_sys->p_update_request_thread );
+ msg_Dbg( p_filter, "released update_request_thread" );
+ }
+
+ msg_Dbg( p_filter, "osdvnc stopped" );
+}
+
+static bool read_exact( filter_t *p_filter,
+ int i_socket,
+ char* p_readbuf,
+ int i_bytes )
+{
+ return i_bytes == net_Read( p_filter, i_socket, NULL,
+ (unsigned char*)p_readbuf,
+ i_bytes, true );
+}
+
+
+static bool write_exact( filter_t *p_filter,
+ int i_socket,
+ char* p_writebuf,
+ int i_bytes )
+{
+ return i_bytes == net_Write( p_filter, i_socket, NULL,
+ (unsigned char*)p_writebuf, i_bytes );
+}
+
+static bool open_vnc_connection ( filter_t *p_filter )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ msg_Dbg( p_filter, "Open socket to vnc server on %s:%u.",
+ p_sys->psz_host, p_sys->i_port );
+
+ p_sys->i_socket = net_ConnectTCP( p_filter, p_sys->psz_host, p_sys->i_port );
+
+ if( p_sys->i_socket < 0 )
+ {
+ msg_Err( p_filter, "Could not open socket" );
+ return false;
+ }
+
+ msg_Dbg( p_filter, "socket is open." );
+
+ return true;
+}
+
+static bool handshaking ( filter_t *p_filter )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ msg_Dbg( p_filter, "Reading protocol version" );
+
+ rfbProtocolVersionMsg pv;
+ if ( !read_exact( p_filter, p_sys->i_socket, pv,
+ sz_rfbProtocolVersionMsg ) )
+ {
+ msg_Err( p_filter, "Could not read version message" );
+ return false;
+ }
+ pv[sz_rfbProtocolVersionMsg] = '\0'; /* pv size is sz_rfbProtocolVersionMsg+1 */
+
+ msg_Dbg( p_filter, "Server version is %s", pv );
+
+ strncpy(pv, "RFB 003.003\n", sz_rfbProtocolVersionMsg);
+
+ if( !write_exact(p_filter, p_sys->i_socket, pv,
+ sz_rfbProtocolVersionMsg) )
+ {
+ msg_Err( p_filter, "Could not write version message" );
+ return false;
+ }
+
+ msg_Dbg( p_filter, "Reading authentication scheme" );
+ uint32_t i_authScheme;
+ if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authScheme, 4 ) )
+ {
+ msg_Err( p_filter, "Could not read authentication scheme" );
+ return false;
+ }
+ i_authScheme = htonl(i_authScheme);
+
+ msg_Dbg( p_filter, "Authentication scheme = %x", i_authScheme );
+ if ( i_authScheme == rfbConnFailed )
+ {
+ msg_Err( p_filter, "Connection rejected by server" );
+ return false;
+ }
+ if (i_authScheme == rfbVncAuth)
+ {
+ unsigned char challenge[CHALLENGESIZE];
+ if ( !read_exact( p_filter, p_sys->i_socket,
+ (char*)challenge, CHALLENGESIZE ) )
+ {
+ msg_Err( p_filter, "Could not read password challenge" );
+ return false;
+ }
+
+ vnc_encrypt_bytes( challenge, p_sys->psz_passwd );
+
+ if( !write_exact(p_filter, p_sys->i_socket,
+ (char*)challenge, CHALLENGESIZE ) )
+ {
+ msg_Err( p_filter, "Could not write password" );
+ return false;
+ }
+ uint32_t i_authResult;
+ if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authResult, 4 ) )
+ {
+ msg_Err( p_filter, "Could not read authentication result" );
+ return false;
+ }
+ i_authResult = htonl(i_authResult);
+ if (i_authResult != rfbVncAuthOK)
+ {
+ msg_Err( p_filter, "VNC authentication failed" );
+ return false;
+ }
+ }
+
+ msg_Dbg( p_filter, "Writing client init message" );
+ rfbClientInitMsg ci;
+ ci.shared = 1;
+ if( !write_exact( p_filter, p_sys->i_socket,
+ (char*)&ci, sz_rfbClientInitMsg ) )
+ {
+ msg_Err( p_filter, "Could not write client init message" );
+ return false;
+ }
+
+ msg_Dbg( p_filter, "Reading server init message" );
+ rfbServerInitMsg si;
+ if( !read_exact( p_filter, p_sys->i_socket,
+ (char*)&si, sz_rfbServerInitMsg ) )
+ {
+ msg_Err( p_filter, "Could not read server init message" );
+ return false;
+ }
+ si.framebufferWidth = htons(si.framebufferWidth);
+ si.framebufferHeight = htons(si.framebufferHeight);
+ si.format.redMax = htons(si.format.redMax);
+ si.format.greenMax = htons(si.format.greenMax);
+ si.format.blueMax = htons(si.format.blueMax);
+
+ p_sys->i_vnc_width = si.framebufferWidth;
+ p_sys->i_vnc_height = si.framebufferHeight;
+
+ msg_Dbg( p_filter, "Servers preferred pixelformat: "
+ "%ux%u, R(%u),G(%u),B(%u), %u bit, depht=%u, %s",
+ si.framebufferWidth,
+ si.framebufferHeight,
+ si.format.redMax,
+ si.format.greenMax,
+ si.format.blueMax,
+ si.format.bitsPerPixel,
+ si.format.depth,
+ si.format.trueColour ? "TrueColor" : "Not-TrueColor");
+
+ uint32_t i_nameLength = htonl(si.nameLength);
+ if( i_nameLength > MAX_VNC_SERVER_NAME_LENGTH )
+ {
+ msg_Err( p_filter, "Server name too long" );
+ return false;
+ }
+ char s_ServerName[MAX_VNC_SERVER_NAME_LENGTH+1];
+
+ msg_Dbg( p_filter, "Reading server name with size = %u", i_nameLength );
+ if( !read_exact( p_filter, p_sys->i_socket, s_ServerName, i_nameLength ) )
+ {
+ msg_Err( p_filter, "Could not read server name" );
+ return false;
+ }
+ s_ServerName[i_nameLength] = '\0';
+
+ if( strcmp( s_ServerName, "VDR-OSD") == 0 )
+ {
+ msg_Dbg( p_filter, "Server is a VDR" );
+ p_sys->b_alpha_from_vnc = true;
+ }
+ else
+ {
+ msg_Dbg( p_filter, "Server is a normal VNC" );
+ p_sys->b_alpha_from_vnc = false;
+ }
+
+
+ msg_Dbg( p_filter, "Server init message read properly" );
+ msg_Dbg( p_filter, "Server name is %s", s_ServerName );
+
+ msg_Dbg( p_filter, "Writing SetPixelFormat message" );
+
+ rfbSetPixelFormatMsg sp;
+ sp.type = rfbSetPixelFormat;
+ sp.format.bitsPerPixel = 8;
+ sp.format.depth = 8 ;
+ sp.format.bigEndian = 1;
+ sp.format.trueColour = 0;
+ sp.format.redMax = htons(31);
+ sp.format.greenMax = htons(31);
+ sp.format.blueMax = htons(31);
+ sp.format.redShift = 10;
+ sp.format.greenShift = 5;
+ sp.format.blueShift = 0;
+
+ if( !write_exact( p_filter, p_sys->i_socket,
+ (char*)&sp, sz_rfbSetPixelFormatMsg) )
+ {
+ msg_Err( p_filter, "Could not write SetPixelFormat message" );
+ return false;
+ }
+
+ msg_Dbg( p_filter, "Writing SetEncodings message" );
+
+ rfbSetEncodingsMsg se;
+ se.type = rfbSetEncodings;
+ se.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
+
+ if( !write_exact( p_filter, p_sys->i_socket,
+ (char*)&se, sz_rfbSetEncodingsMsg) )
+ {
+ msg_Err( p_filter, "Could not write SetEncodings message begin" );
+ return false;
+ }
+
+ uint32_t i_encoding;
+
+ msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingCopyRect" );
+ i_encoding = htonl(rfbEncodingCopyRect);
+ if( !write_exact( p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
+ {
+ msg_Err( p_filter, "Could not write encoding type rfbEncodingCopyRect." );
+ return false;
+ }
+
+ msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingRRE" );
+ i_encoding = htonl(rfbEncodingRRE);
+ if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
+ {
+ msg_Err( p_filter, "Could not write encoding type rfbEncodingRRE." );
+ return false;
+ }
+
+ if( p_sys->b_alpha_from_vnc )
+ {
+ msg_Dbg( p_filter, "Writing SetEncodings rfbEncSpecialUseAlpha" );
+ i_encoding = 0x00F0FFFF; /* rfbEncSpecialUseAlpha is 0xFFFFF000
+ * before we swap it */
+ if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
+ {
+ msg_Err( p_filter, "Could not write encoding type rfbEncSpecialUseAlpha." );
+ return false;
+ }
+ }
+ return true;
+
+}
+
+static void vnc_worker_thread( vlc_object_t *p_thread_obj )
+{
+ filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ msg_Dbg( p_filter, "VNC worker thread started" );
+
+ if ( open_vnc_connection ( p_filter ) == false )
+ {
+ msg_Err( p_filter, "Could not connect to vnc host" );
+ return;
+ }
+
+ if ( handshaking ( p_filter ) == false )
+ {
+ msg_Err( p_filter, "Error occured while handshaking vnc host" );
+ return;
+ }
+
+ p_sys->b_connection_active = true; /* to enable sending key
+ * and mouse events to host */
+
+ /* Create an empty picture for VNC the data */
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->p_pic = malloc( sizeof(picture_t) );
+ if( !p_sys->p_pic )
+ {
+ vlc_mutex_unlock( &p_sys->lock );
+ return;
+ }
+ vout_AllocatePicture( VLC_OBJECT(p_filter), p_sys->p_pic,
+ VLC_FOURCC('Y','U','V','A'),
+ p_sys->i_vnc_width,
+ p_sys->i_vnc_height,
+ VOUT_ASPECT_FACTOR );
+ if( !p_sys->p_pic->i_planes )
+ {
+ free( p_sys->p_pic );
+ p_sys->p_pic = NULL;
+ vlc_mutex_unlock( &p_sys->lock );
+ return;
+ }
+ p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
+
+ vlc_mutex_unlock( &p_sys->lock );
+
+ /* create the update request thread */
+ p_sys->p_update_request_thread = vlc_object_create( p_filter,
+ VLC_OBJECT_GENERIC );
+ vlc_object_attach( p_sys->p_update_request_thread, p_filter );
+ if( vlc_thread_create( p_sys->p_update_request_thread,
+ "vnc update request thread",
+ update_request_thread,
+ VLC_THREAD_PRIORITY_LOW, false ) )
+ {
+ vlc_object_detach( p_sys->p_update_request_thread );
+ vlc_object_release( p_sys->p_update_request_thread );
+ p_sys->p_update_request_thread = NULL;
+ msg_Err( p_filter, "cannot spawn vnc update request thread" );
+ return;
+ }
+
+ /* connection is initialized, now read and handle server messages */
+
+ int i_msgSize;
+
+ rfbServerToClientMsg msg;
+
+ while (p_sys->b_continue)
+ {
+ msg.type = 0;
+ if ( !read_exact(p_filter, p_sys->i_socket, (char*)&msg, 1 ) )
+ {
+ msg_Err( p_filter, "Error while waiting for next server message");
+ p_sys->b_continue = false;
+ }
+ else
+ {
+ switch (msg.type)
+ {
+ case rfbFramebufferUpdate:
+ i_msgSize = sz_rfbFramebufferUpdateMsg;
+ break;
+ case rfbSetColourMapEntries:
+ i_msgSize = sz_rfbSetColourMapEntriesMsg;
+ break;
+ case rfbBell:
+ i_msgSize = sz_rfbBellMsg;
+ break;
+ case rfbServerCutText:
+ i_msgSize = sz_rfbServerCutTextMsg;
+ break;
+ case rfbReSizeFrameBuffer:
+ i_msgSize = sz_rfbReSizeFrameBufferMsg;
+ break;
+ default:
+ i_msgSize = 0;
+ msg_Err( p_filter, "Invalid message %u received", msg.type );
+ p_sys->b_continue = false;
+ }
+ if (p_sys->b_continue == true)
+ {
+ if (--i_msgSize > 0)
+ {
+ if ( !read_exact( p_filter, p_sys->i_socket,
+ ((char*)&msg)+1, i_msgSize ) )
+ {
+ msg_Err( p_filter, "Error while reading message of type %u",
+ msg.type );
+ p_sys->b_continue = false;
+ }
+ else
+ {
+ process_server_message( p_filter, &msg);
+ }
+ }
+ else
+ {
+ process_server_message( p_filter, &msg);
+ }
+ }
+
+ }
+
+ if (p_sys->p_worker_thread->b_die ||
+ p_sys->p_worker_thread->b_error)
+ {
+ p_sys->b_continue = false;
+ }
+
+ }
+
+ msg_Dbg( p_filter, "VNC message reader thread ended" );
+
+}
+
+static void update_request_thread( vlc_object_t *p_thread_obj )
+{
+ filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ msg_Dbg( p_filter, "VNC update request thread started" );
+
+ rfbFramebufferUpdateRequestMsg udr;
+ udr.type = rfbFramebufferUpdateRequest;
+ udr.incremental = 0;
+ udr.x = 0;
+ udr.y = 0;
+ udr.w = htons(p_sys->i_vnc_width);
+ udr.h = htons(p_sys->i_vnc_height);
+
+ if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
+ sz_rfbFramebufferUpdateRequestMsg) == false)
+ {
+ msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
+ p_sys->b_continue = false;
+ }
+
+ udr.incremental = 1;
+ mtime_t i_poll_interval_microsec = p_sys->i_vnc_poll_interval * 1000;
+
+ if (p_sys->b_vnc_poll)
+ {
+ while ( p_sys->b_continue == true )
+ {
+ msleep( i_poll_interval_microsec );
+ if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
+ sz_rfbFramebufferUpdateRequestMsg) == false)
+ {
+ msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
+ p_sys->b_continue = false;
+ }
+ if (p_sys->p_update_request_thread->b_die ||
+ p_sys->p_update_request_thread->b_error)
+ {
+ p_sys->b_continue = false;
+ }
+ }
+ }
+ else
+ {
+ msg_Dbg( p_filter, "VNC polling disabled." );
+ }
+ msg_Dbg( p_filter, "VNC update request thread ended" );
+}
+
+static bool process_server_message ( filter_t *p_filter,
+ rfbServerToClientMsg *msg )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ switch (msg->type)
+ {
+ case rfbFramebufferUpdate:
+ {
+ msg->fu.nRects = htons(msg->fu.nRects);
+ rfbFramebufferUpdateRectHeader hdr;
+
+ for (int i_rect = 0; i_rect < msg->fu.nRects; i_rect++)
+ {
+ if (!read_exact(p_filter, p_sys->i_socket, (char*)&hdr,
+ sz_rfbFramebufferUpdateRectHeader ) )
+ {
+ msg_Err( p_filter, "Could not read FrameBufferUpdate header" );
+ return false;
+ }
+ hdr.r.x = htons(hdr.r.x);
+ hdr.r.y = htons(hdr.r.y);
+ hdr.r.w = htons(hdr.r.w);
+ hdr.r.h = htons(hdr.r.h);
+ hdr.encoding = htonl(hdr.encoding);
+
+ switch (hdr.encoding)
+ {
+ case rfbEncodingRaw:
+ {
+ int i_line;
+ for (i_line = 0; i_line < hdr.r.h; i_line++)
+ {
+ if ( !read_exact( p_filter, p_sys->i_socket,
+ p_sys->read_buffer, hdr.r.w ) )
+ {
+ msg_Err( p_filter,
+ "Could not read FrameBufferUpdate line data" );
+ return false;
+ }
+ vlc_mutex_lock( &p_sys->lock );
+ if ( !raw_line( p_sys, hdr.r.x,
+ hdr.r.y + i_line,
+ hdr.r.w ) )
+ {
+ msg_Err( p_filter, "raw_line failed." );
+ vlc_mutex_unlock( &p_sys->lock );
+ return false;
+ }
+ vlc_mutex_unlock( &p_sys->lock );
+ }
+ }
+ break;
+
+ case rfbEncodingCopyRect:
+ {
+ msg_Err( p_filter,
+ "Rect in unsupported encoding rfbEncodingCopyRect" );
+ return false;
+ }
+
+ case rfbEncodingRRE:
+ {
+ rfbRREHeader rrehdr;
+ if ( !read_exact( p_filter, p_sys->i_socket,
+ (char*)&rrehdr,
+ sz_rfbRREHeader ) )
+ {
+ msg_Err( p_filter, "Could not read rfbRREHeader" );
+ return false;
+ }
+ uint8_t i_pixcolor;
+ if ( !read_exact(p_filter, p_sys->i_socket,
+ (char*)&i_pixcolor, 1 ) )
+ {
+ msg_Err( p_filter, "Could not read RRE pixcolor" );
+ return false;
+ }
+
+ vlc_mutex_lock( &p_sys->lock );
+ if ( !fill_rect( p_sys,
+ hdr.r.x, hdr.r.y,
+ hdr.r.w, hdr.r.h,
+ i_pixcolor) )
+ {
+ msg_Err( p_filter, "main fill_rect failed." );
+ vlc_mutex_unlock( &p_sys->lock );
+ return false;
+ }
+ vlc_mutex_unlock( &p_sys->lock );
+
+ rrehdr.nSubrects = htonl(rrehdr.nSubrects);
+
+ int i_datasize = rrehdr.nSubrects *
+ ( sizeof(i_pixcolor) + sz_rfbRectangle ) ;
+ if ( i_datasize > READ_BUFFER_SIZE )
+ {
+ msg_Err( p_filter, "Buffer too small, "
+ "need %u bytes", i_datasize );
+ return false;
+ }
+ if ( !read_exact( p_filter, p_sys->i_socket,
+ p_sys->read_buffer, i_datasize ) )
+ {
+ msg_Err( p_filter,
+ "Could not read RRE subrect data" );
+ return false;
+ }
+
+ uint32_t i_subrect;
+ rfbRectangle* p_subrect;
+ int i_offset = 0;
+ vlc_mutex_lock( &p_sys->lock );
+ for ( i_subrect = 0;
+ i_subrect < rrehdr.nSubrects; i_subrect++)
+ {
+ i_pixcolor = p_sys->read_buffer[i_offset];
+ i_offset += sizeof(i_pixcolor);
+ p_subrect =
+ (rfbRectangle*)(p_sys->read_buffer + i_offset);
+ i_offset += sz_rfbRectangle;
+
+ if (!fill_rect( p_sys,
+ htons(p_subrect->x) + hdr.r.x,
+ htons(p_subrect->y) + hdr.r.y,
+ htons(p_subrect->w),
+ htons(p_subrect->h),
+ i_pixcolor) )
+ {
+ msg_Err( p_filter,
+ "subrect %u fill_rect failed.", i_subrect );
+ vlc_mutex_unlock( &p_sys->lock );
+ return false;
+ }
+ }
+ vlc_mutex_unlock( &p_sys->lock );
+ }
+ break;
+
+ }
+
+ }
+ vlc_mutex_lock( &p_sys->lock );
+ p_sys->b_need_update = true;
+ vlc_mutex_unlock( &p_sys->lock );
+ }
+ return true;
+
+ case rfbSetColourMapEntries:
+ {
+ msg->scme.nColours = htons(msg->scme.nColours);
+ msg->scme.firstColour = htons(msg->scme.firstColour);
+ int i_datasize;
+ if ( p_sys->b_alpha_from_vnc == true )
+ {
+ i_datasize = 2 * msg->scme.nColours * 4;
+ }
+ else
+ {
+ i_datasize = 2 * msg->scme.nColours * 3;
+ }
+ if ( i_datasize > READ_BUFFER_SIZE )
+ {
+ msg_Err( p_filter, "Buffer too small, need %u bytes",
+ i_datasize );
+ return false;
+ }
+
+ if ( !read_exact( p_filter, p_sys->i_socket,
+ p_sys->read_buffer, i_datasize ) )
+ {
+ msg_Err( p_filter, "Could not read color map data" );
+ return false;
+ }
+
+ uint8_t i_red, i_green, i_blue, i_alpha, i_color_index;
+ uint16_t i_offset = 0;
+ i_alpha = 255;
+
+ for (int i = 0; i < msg->scme.nColours; i++)
+ {
+ i_color_index = i+msg->scme.firstColour;
+ if ( p_sys->b_alpha_from_vnc == true )
+ {
+ i_alpha = p_sys->read_buffer[i_offset];
+ i_offset += 2;
+ }
+ i_red = p_sys->read_buffer[i_offset];
+ i_offset += 2;
+ i_green = p_sys->read_buffer[i_offset];
+ i_offset += 2;
+ i_blue = p_sys->read_buffer[i_offset];
+ i_offset += 2;
+ rgb_to_yuv( &p_sys->ar_color_table_yuv[i_color_index][0],
+ &p_sys->ar_color_table_yuv[i_color_index][1],
+ &p_sys->ar_color_table_yuv[i_color_index][2],
+ i_red,
+ i_green,
+ i_blue );
+ p_sys->ar_color_table_yuv[i][3] = i_alpha;
+ }
+ }
+ return true;
+
+ case rfbBell:
+ msg_Err( p_filter, "rfbBell received" );
+ return true;
+
+ case rfbServerCutText:
+ msg->sct.length = htons(msg->sct.length);
+ if ( msg->sct.length > READ_BUFFER_SIZE )
+ {
+ msg_Err( p_filter, "Buffer too small, need %u bytes", msg->sct.length );
+ return false;
+ }
+ if ( !read_exact(p_filter, p_sys->i_socket,
+ p_sys->read_buffer, msg->sct.length ) )
+ {
+ msg_Err( p_filter, "Could not read Reading rfbServerCutText data" );
+ return false;
+ }
+ return true;
+
+ case rfbReSizeFrameBuffer:
+ msg_Err( p_filter, "Reading rfbReSizeFrameBuffer not implemented" );
+ return false;
+
+ default:
+ msg_Err( p_filter, "Invalid message %u received", msg->type );
+ return false;
+ }
+ return false;
+}
+
+/****************************************************************************
+ * Filter: the whole thing
+ ****************************************************************************
+ * This function outputs subpictures at regular time intervals.
+ ****************************************************************************/
+static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
+{
+ filter_sys_t *p_sys = p_filter->p_sys;
+ subpicture_t *p_spu;
+ subpicture_region_t *p_region;
+ video_format_t fmt;
+ picture_t *p_pic;
+
+ if( !p_sys->b_need_update )
+ {
+ return NULL;
+ }
+
+ vlc_mutex_lock( &p_sys->lock );
+
+ p_pic = p_sys->p_pic;
+
+ if( !p_pic )
+ {
+ vlc_mutex_unlock( &p_sys->lock );
+ return NULL;
+ }
+
+ /* Allocate the subpicture internal data. */
+ p_spu = p_filter->pf_sub_buffer_new( p_filter );
+ if( !p_spu )
+ {
+ vlc_mutex_unlock( &p_sys->lock );
+ return NULL;
+ }
+
+ p_spu->b_absolute = false;
+ p_spu->i_start = date;
+ p_spu->i_stop = 0;
+ p_spu->b_ephemer = true;
+
+ /* Create new SPU region */
+ memset( &fmt, 0, sizeof(video_format_t) );
+ fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
+ fmt.i_aspect = VOUT_ASPECT_FACTOR;
+ fmt.i_sar_num = fmt.i_sar_den = 1;
+ fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
+ fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
+ fmt.i_x_offset = fmt.i_y_offset = 0;
+ p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
+ if( !p_region )
+ {
+ msg_Err( p_filter, "cannot allocate SPU region" );
+ p_filter->pf_sub_buffer_del( p_filter, p_spu );
+ vlc_mutex_unlock( &p_sys->lock );
+ return NULL;
+ }
+
+ vout_CopyPicture( p_filter, &p_region->picture, p_pic );
+
+ p_sys->b_need_update = false;
+
+ vlc_mutex_unlock( &p_sys->lock );
+
+ /* set to one of the 9 relative locations */
+ p_region->i_align = 0; //=CENTER
+ p_spu->b_absolute = false;
+
+
+ p_spu->i_x = 0;
+ p_spu->i_y = 0;
+
+ p_spu->p_region = p_region;
+
+ p_spu->i_alpha = ( p_sys->i_alpha );
+
+ return p_spu;
+}
+
+
+static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
+ int r, int g, int b )
+{
+ *y = ( ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16 );
+ *u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
+ *v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ;
+}
+
+static inline bool fill_rect( filter_sys_t* p_sys,
+ uint16_t i_x, uint16_t i_y,
+ uint16_t i_w, uint16_t i_h,
+ uint8_t i_color)
+{
+ plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
+ plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
+ plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
+ plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
+ int i_pitch = p_outY->i_pitch;
+ int i_lines = p_outY->i_lines;
+ if ( i_x + i_w > i_pitch)
+ return false;
+ if ( i_y + i_h > i_lines)
+ return false;
+ int i_line_offset = i_y * i_pitch;
+ uint8_t i_yuv_y = p_sys->ar_color_table_yuv[i_color][0];
+ uint8_t i_yuv_u = p_sys->ar_color_table_yuv[i_color][1];
+ uint8_t i_yuv_v = p_sys->ar_color_table_yuv[i_color][2];
+ uint8_t i_alpha = p_sys->ar_color_table_yuv[i_color][3];
+ for( int i_line = 0; i_line < i_h; i_line++ )
+ {
+ for( int i_column = 0; i_column < i_w; i_column++ )
+ {
+ int i_total_offset = i_line_offset + i_x + i_column;
+ p_outY->p_pixels[ i_total_offset ] = i_yuv_y;
+ p_outU->p_pixels[ i_total_offset ] = i_yuv_u;
+ p_outV->p_pixels[ i_total_offset ] = i_yuv_v;
+ p_outA->p_pixels[ i_total_offset ] = i_alpha;
+ }
+ i_line_offset += i_pitch;
+ }
+ return true;
+}
+
+static inline bool raw_line( filter_sys_t* p_sys,
+ uint16_t i_x, uint16_t i_y,
+ uint16_t i_w )
+{
+ plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
+ plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
+ plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
+ plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
+ int i_pitch = p_outY->i_pitch;
+ int i_lines = p_outY->i_lines;
+ if ( i_x + i_w > i_pitch)
+ return false;
+ if ( i_y > i_lines)
+ return false;
+
+ int i_line_offset = i_y * i_pitch + i_x;
+
+ for( int i_column = 0; i_column < i_w; i_column++ )
+ {
+ int i_offset = i_line_offset + i_column;
+ uint8_t i_color = p_sys->read_buffer[i_column];
+ p_outY->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][0];
+ p_outU->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][1];
+ p_outV->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][2];
+ p_outA->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][3];
+ }
+
+ return true;
+}
+
+
+/*****************************************************************************
+ * MouseEvent: callback for mouse events
+ *****************************************************************************/
+static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
+
+ filter_t *p_filter = (filter_t *)p_data;
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ if( !p_sys->b_connection_active )
+ return VLC_SUCCESS;
+
+ if( !p_sys->b_vnc_mouse_events )
+ return VLC_SUCCESS;
+
+ vout_thread_t *p_vout = (vout_thread_t*)p_sys->p_vout;
+ int i_x, i_y;
+ int i_v;
+
+ int v_h = p_vout->output.i_height;
+ int v_w = p_vout->output.i_width;
+
+ i_v = var_GetInteger( p_sys->p_vout, "mouse-button-down" );
+ i_y = var_GetInteger( p_sys->p_vout, "mouse-y" );
+ i_x = var_GetInteger( p_sys->p_vout, "mouse-x" );
+
+ if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
+ {
+ msg_Dbg( p_this, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
+ return VLC_SUCCESS;
+ }
+#ifdef VNC_DEBUG
+ msg_Dbg( p_this, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
+#endif
+
+ /* FIXME: calculate x and y coordinates for scaled output */
+
+ /* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
+ rfbPointerEventMsg ev;
+ ev.type = rfbPointerEvent;
+ ev.buttonMask = i_v;
+ ev.x = htons(i_x);
+ ev.y = htons(i_y);
+
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbPointerEventMsg);
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * KeyEvent: callback for keyboard events
+ *****************************************************************************/
+static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
+
+ filter_t *p_filter = (filter_t *)p_data;
+ filter_sys_t *p_sys = p_filter->p_sys;
+
+ if( !p_sys->b_connection_active )
+ return VLC_SUCCESS;
+
+ if( !p_sys->b_vnc_key_events )
+ return VLC_SUCCESS;
+
+ msg_Dbg( p_this, "key pressed (%d) ", newval.i_int );
+
+ if ( !newval.i_int )
+ {
+ msg_Err( p_this, "Received invalid key event %d", newval.i_int );
+ return VLC_EGENERIC;
+ }
+
+ uint32_t i_key32 = newval.i_int;
+ i_key32 = htonl(i_key32);
+ rfbKeyEventMsg ev;
+ ev.type = rfbKeyEvent;
+ ev.down = 1;
+
+ /* first key-down for modifier-keys */
+ if (newval.i_int & KEY_MODIFIER_CTRL)
+ {
+ ev.key = 0xffe3;
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+ }
+ if (newval.i_int & KEY_MODIFIER_SHIFT)
+ {
+ ev.key = 0xffe1;
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+ }
+ if (newval.i_int & KEY_MODIFIER_ALT)
+ {
+ ev.key = 0xffe9;
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+ }
+
+ /* then key-down for the pressed key */
+ ev.key = i_key32;
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+
+ ev.down = 0;
+
+ /* then key-up for the pressed key */
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+
+ /* last key-down for modifier-keys */
+ if (newval.i_int & KEY_MODIFIER_CTRL)
+ {
+ ev.key = 0xffe3;
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+ }
+ if (newval.i_int & KEY_MODIFIER_SHIFT)
+ {
+ ev.key = 0xffe1;
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+ }
+ if (newval.i_int & KEY_MODIFIER_ALT)
+ {
+ ev.key = 0xffe9;
+ write_exact( p_filter, p_sys->i_socket,
+ (char*)&ev, sz_rfbKeyEventMsg);
+ }
+
+ return VLC_SUCCESS;
+}
+
+static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
+{
+ unsigned char key[8];
+ unsigned int i;
+
+ for (i = 0; i < 8; i++)
+ key[i] = i < strlen( passwd ) ? passwd[i] : '\0';
+
+ gcry_cipher_hd_t ctx;
+ gcry_cipher_open( &ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB,0);
+
+ /* reverse bits of the key */
+ for( i = 0 ; i < 8 ; i ++ )
+ key[i] =
+ (key[i] >> 7) +
+ (((key[i] >> 6) & 0x01 ) << 1 ) +
+ (((key[i] >> 5) & 0x01 ) << 2 ) +
+ (((key[i] >> 4) & 0x01 ) << 3 ) +
+ (((key[i] >> 3) & 0x01 ) << 4 ) +
+ (((key[i] >> 2) & 0x01 ) << 5 ) +
+ (((key[i] >> 1) & 0x01 ) << 6 ) +
+ ((key[i] & 0x01) << 7 );
+
+ gcry_cipher_setkey( ctx, key, 8 );
+ gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
+ gcry_cipher_close( ctx );
+}
--- /dev/null
+/*
+ * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * rfbproto.h - header file for the RFB protocol version 3.3
+ *
+ * Uses types CARD<n> for an n-bit unsigned integer, INT<n> for an n-bit signed
+ * integer (for n = 8, 16 and 32).
+ *
+ * All multiple byte integers are in big endian (network) order (most
+ * significant byte first). Unless noted otherwise there is no special
+ * alignment of protocol structures.
+ *
+ *
+ * Once the initial handshaking is done, all messages start with a type byte,
+ * (usually) followed by message-specific data. The order of definitions in
+ * this file is as follows:
+ *
+ * (1) Structures used in several types of message.
+ * (2) Structures used in the initial handshaking.
+ * (3) Message types.
+ * (4) Encoding types.
+ * (5) For each message type, the form of the data following the type byte.
+ * Sometimes this is defined by a single structure but the more complex
+ * messages have to be explained by comments.
+ */
+
+/*****************************************************************************
+ *
+ * Structures used in several messages
+ *
+ *****************************************************************************/
+
+#include "inttypes.h"
+#define CARD8 uint8_t
+#define CARD16 uint16_t
+#define CARD32 uint32_t
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify a rectangle. This structure is a multiple of 4
+ * bytes so that it can be interspersed with 32-bit pixel data without
+ * affecting alignment.
+ */
+typedef struct {
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbRectangle;
+
+#define sz_rfbRectangle 8
+
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify pixel format.
+ */
+
+typedef struct {
+
+ CARD8 bitsPerPixel; /* 8,16,32 only */
+
+ CARD8 depth; /* 8 to 32 */
+
+ CARD8 bigEndian; /* True if multi-byte pixels are interpreted
+ as big endian, or if single-bit-per-pixel
+ has most significant bit of the byte
+ corresponding to first (leftmost) pixel. Of
+ course this is meaningless for 8 bits/pix */
+
+ CARD8 trueColour; /* If false then we need a "colour map" to
+ convert pixels to RGB. If true, xxxMax and
+ xxxShift specify bits used for red, green
+ and blue */
+
+ /* the following fields are only meaningful if trueColour is true */
+
+ CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the
+ number of bits used for red). Note this
+ value is always in big endian order. */
+
+ CARD16 greenMax; /* similar for green */
+
+ CARD16 blueMax; /* and blue */
+
+ CARD8 redShift; /* number of shifts needed to get the red
+ value in a pixel to the least significant
+ bit. To find the red value from a given
+ pixel, do the following:
+ 1) Swap pixel value according to bigEndian
+ (e.g. if bigEndian is false and host byte
+ order is big endian, then swap).
+ 2) Shift right by redShift.
+ 3) AND with redMax (in host byte order).
+ 4) You now have the red value between 0 and
+ redMax. */
+
+ CARD8 greenShift; /* similar for green */
+
+ CARD8 blueShift; /* and blue */
+
+ CARD8 pad1;
+ CARD16 pad2;
+
+} rfbPixelFormat;
+
+#define sz_rfbPixelFormat 16
+
+
+
+/*****************************************************************************
+ *
+ * Initial handshaking messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Protocol Version
+ *
+ * The server always sends 12 bytes to start which identifies the latest RFB
+ * protocol version number which it supports. These bytes are interpreted
+ * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where
+ * xxx and yyy are the major and minor version numbers (for version 3.3
+ * this is "RFB 003.003\n").
+ *
+ * The client then replies with a similar 12-byte message giving the version
+ * number of the protocol which should actually be used (which may be different
+ * to that quoted by the server).
+ *
+ * It is intended that both clients and servers may provide some level of
+ * backwards compatibility by this mechanism. Servers in particular should
+ * attempt to provide backwards compatibility, and even forwards compatibility
+ * to some extent. For example if a client demands version 3.1 of the
+ * protocol, a 3.0 server can probably assume that by ignoring requests for
+ * encoding types it doesn't understand, everything will still work OK. This
+ * will probably not be the case for changes in the major version number.
+ *
+ * The format string below can be used in sprintf or sscanf to generate or
+ * decode the version string respectively.
+ */
+
+#define rfbProtocolVersionFormat "RFB %03d.%03d\n"
+#define rfbProtocolMajorVersion 3
+#define rfbProtocolMinorVersion 3
+
+typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
+
+#define sz_rfbProtocolVersionMsg 12
+
+
+/*-----------------------------------------------------------------------------
+ * Authentication
+ *
+ * Once the protocol version has been decided, the server then sends a 32-bit
+ * word indicating whether any authentication is needed on the connection.
+ * The value of this word determines the authentication scheme in use. For
+ * version 3.0 of the protocol this may have one of the following values:
+ */
+
+#define rfbConnFailed 0
+#define rfbNoAuth 1
+#define rfbVncAuth 2
+
+/*
+ * rfbConnFailed: For some reason the connection failed (e.g. the server
+ * cannot support the desired protocol version). This is
+ * followed by a string describing the reason (where a
+ * string is specified as a 32-bit length followed by that
+ * many ASCII characters).
+ *
+ * rfbNoAuth: No authentication is needed.
+ *
+ * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte
+ * challenge follows, which the client encrypts as
+ * appropriate using the password and sends the resulting
+ * 16-byte response. If the response is correct, the
+ * server sends the 32-bit word rfbVncAuthOK. If a simple
+ * failure happens, the server sends rfbVncAuthFailed and
+ * closes the connection. If the server decides that too
+ * many failures have occurred, it sends rfbVncAuthTooMany
+ * and closes the connection. In the latter case, the
+ * server should not allow an immediate reconnection by
+ * the client.
+ */
+
+#define rfbVncAuthOK 0
+#define rfbVncAuthFailed 1
+#define rfbVncAuthTooMany 2
+
+
+/*-----------------------------------------------------------------------------
+ * Client Initialisation Message
+ *
+ * Once the client and server are sure that they're happy to talk to one
+ * another, the client sends an initialisation message. At present this
+ * message only consists of a boolean indicating whether the server should try
+ * to share the desktop by leaving other clients connected, or give exclusive
+ * access to this client by disconnecting all other clients.
+ */
+
+typedef struct {
+ CARD8 shared;
+} rfbClientInitMsg;
+
+#define sz_rfbClientInitMsg 1
+
+
+/*-----------------------------------------------------------------------------
+ * Server Initialisation Message
+ *
+ * After the client initialisation message, the server sends one of its own.
+ * This tells the client the width and height of the server's framebuffer,
+ * its pixel format and the name associated with the desktop.
+ */
+
+typedef struct {
+ CARD16 framebufferWidth;
+ CARD16 framebufferHeight;
+ rfbPixelFormat format; /* the server's preferred pixel format */
+ CARD32 nameLength;
+ /* followed by char name[nameLength] */
+} rfbServerInitMsg;
+
+#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat)
+
+
+/*
+ * Following the server initialisation message it's up to the client to send
+ * whichever protocol messages it wants. Typically it will send a
+ * SetPixelFormat message and a SetEncodings message, followed by a
+ * FramebufferUpdateRequest. From then on the server will send
+ * FramebufferUpdate messages in response to the client's
+ * FramebufferUpdateRequest messages. The client should send
+ * FramebufferUpdateRequest messages with incremental set to true when it has
+ * finished processing one FramebufferUpdate and is ready to process another.
+ * With a fast client, the rate at which FramebufferUpdateRequests are sent
+ * should be regulated to avoid hogging the network.
+ */
+
+
+
+/*****************************************************************************
+ *
+ * Message types
+ *
+ *****************************************************************************/
+
+/* server -> client */
+
+#define rfbFramebufferUpdate 0
+#define rfbSetColourMapEntries 1
+#define rfbBell 2
+#define rfbServerCutText 3
+#define rfbReSizeFrameBuffer 0xF
+
+
+/* client -> server */
+
+#define rfbSetPixelFormat 0
+#define rfbFixColourMapEntries 1 /* not currently supported */
+#define rfbSetEncodings 2
+#define rfbFramebufferUpdateRequest 3
+#define rfbKeyEvent 4
+#define rfbPointerEvent 5
+#define rfbClientCutText 6
+#define rfbSetScaleFactor 0xF
+
+
+
+
+/*****************************************************************************
+ *
+ * Encoding types
+ *
+ *****************************************************************************/
+
+#define rfbEncodingRaw 0
+#define rfbEncodingCopyRect 1
+#define rfbEncodingRRE 2
+#define rfbEncodingCoRRE 4
+#define rfbEncodingHextile 5
+#define rfbEncodingZRLE 16
+
+
+
+/*****************************************************************************
+ *
+ * Server -> client message definitions
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdate - a block of rectangles to be copied to the framebuffer.
+ *
+ * This message consists of a header giving the number of rectangles of pixel
+ * data followed by the rectangles themselves. The header is padded so that
+ * together with the type byte it is an exact multiple of 4 bytes (to help
+ * with alignment of 32-bit pixels):
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdate */
+ CARD8 pad;
+ CARD16 nRects;
+ /* followed by nRects rectangles */
+} rfbFramebufferUpdateMsg;
+
+#define sz_rfbFramebufferUpdateMsg 4
+
+/*
+ * Each rectangle of pixel data consists of a header describing the position
+ * and size of the rectangle and a type word describing the encoding of the
+ * pixel data, followed finally by the pixel data. Note that if the client has
+ * not sent a SetEncodings message then it will only receive raw pixel data.
+ * Also note again that this structure is a multiple of 4 bytes.
+ */
+
+typedef struct {
+ rfbRectangle r;
+ CARD32 encoding; /* one of the encoding types rfbEncoding... */
+} rfbFramebufferUpdateRectHeader;
+
+#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Raw Encoding. Pixels are sent in top-to-bottom scanline order,
+ * left-to-right within a scanline with no padding in between.
+ */
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CopyRect Encoding. The pixels are specified simply by the x and y position
+ * of the source rectangle.
+ */
+
+typedef struct {
+ CARD16 srcX;
+ CARD16 srcY;
+} rfbCopyRect;
+
+#define sz_rfbCopyRect 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure
+ * giving the number of subrectangles following. Finally the data follows in
+ * the form [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbRectangle>].
+ */
+
+typedef struct {
+ CARD32 nSubrects;
+} rfbRREHeader;
+
+#define sz_rfbRREHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving
+ * the number of subrectangles following. Finally the data follows in the form
+ * [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbCoRRERectangle>]. This means that
+ * the whole rectangle must be at most 255x255 pixels.
+ */
+
+typedef struct {
+ CARD8 x;
+ CARD8 y;
+ CARD8 w;
+ CARD8 h;
+} rfbCoRRERectangle;
+
+#define sz_rfbCoRRERectangle 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels,
+ * starting at the top left going in left-to-right, top-to-bottom order. If
+ * the width of the rectangle is not an exact multiple of 16 then the width of
+ * the last tile in each row will be correspondingly smaller. Similarly if the
+ * height is not an exact multiple of 16 then the height of each tile in the
+ * final row will also be smaller. Each tile begins with a "subencoding" type
+ * byte, which is a mask made up of a number of bits. If the Raw bit is set
+ * then the other bits are irrelevant; w*h pixel values follow (where w and h
+ * are the width and height of the tile). Otherwise the tile is encoded in a
+ * similar way to RRE, except that the position and size of each subrectangle
+ * can be specified in just two bytes. The other bits in the mask are as
+ * follows:
+ *
+ * BackgroundSpecified - if set, a pixel value follows which specifies
+ * the background colour for this tile. The first non-raw tile in a
+ * rectangle must have this bit set. If this bit isn't set then the
+ * background is the same as the last tile.
+ *
+ * ForegroundSpecified - if set, a pixel value follows which specifies
+ * the foreground colour to be used for all subrectangles in this tile.
+ * If this bit is set then the SubrectsColoured bit must be zero.
+ *
+ * AnySubrects - if set, a single byte follows giving the number of
+ * subrectangles following. If not set, there are no subrectangles (i.e.
+ * the whole tile is just solid background colour).
+ *
+ * SubrectsColoured - if set then each subrectangle is preceded by a pixel
+ * value giving the colour of that subrectangle. If not set, all
+ * subrectangles are the same colour, the foreground colour; if the
+ * ForegroundSpecified bit wasn't set then the foreground is the same as
+ * the last tile.
+ *
+ * The position and size of each subrectangle is specified in two bytes. The
+ * Pack macros below can be used to generate the two bytes from x, y, w, h,
+ * and the Extract macros can be used to extract the x, y, w, h values from
+ * the two bytes.
+ */
+
+#define rfbHextileRaw (1 << 0)
+#define rfbHextileBackgroundSpecified (1 << 1)
+#define rfbHextileForegroundSpecified (1 << 2)
+#define rfbHextileAnySubrects (1 << 3)
+#define rfbHextileSubrectsColoured (1 << 4)
+
+#define rfbHextilePackXY(x,y) (((x) << 4) | (y))
+#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1))
+#define rfbHextileExtractX(byte) ((byte) >> 4)
+#define rfbHextileExtractY(byte) ((byte) & 0xf)
+#define rfbHextileExtractW(byte) (((byte) >> 4) + 1)
+#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * ZRLE - encoding combining Zlib compression, tiling, palettisation and
+ * run-length encoding.
+ */
+
+typedef struct {
+ CARD32 length;
+} rfbZRLEHeader;
+
+#define sz_rfbZRLEHeader 4
+
+#define rfbZRLETileWidth 64
+#define rfbZRLETileHeight 64
+
+
+/*-----------------------------------------------------------------------------
+ * SetColourMapEntries - these messages are only sent if the pixel
+ * format uses a "colour map" (i.e. trueColour false) and the client has not
+ * fixed the entire colour map using FixColourMapEntries. In addition they
+ * will only start being sent after the client has sent its first
+ * FramebufferUpdateRequest. So if the client always tells the server to use
+ * trueColour then it never needs to process this type of message.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbSetColourMapEntriesMsg;
+
+#define sz_rfbSetColourMapEntriesMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * Bell - ring a bell on the client if it has one.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbBell */
+} rfbBellMsg;
+
+#define sz_rfbBellMsg 1
+
+
+
+/*-----------------------------------------------------------------------------
+ * ServerCutText - the server has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbServerCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbServerCutTextMsg;
+
+#define sz_rfbServerCutTextMsg 8
+
+/*-----------------------------------------------------------------------------
+ * ReSizeFrameBuffer - tell the RFB client to alter its framebuffer, either
+ * due to a resize of the server desktop or a client-requested scaling factor.
+ * The pixel format remains unchanged.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbReSizeFrameBuffer */
+ CARD8 pad1;
+ CARD16 desktop_w; /* Desktop width */
+ CARD16 desktop_h; /* Desktop height */
+ CARD16 buffer_w; /* FrameBuffer width */
+ CARD16 buffer_h; /* Framebuffer height */
+ CARD16 pad2;
+
+} rfbReSizeFrameBufferMsg;
+
+#define sz_rfbReSizeFrameBufferMsg (12)
+
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all server->client messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbFramebufferUpdateMsg fu;
+ rfbSetColourMapEntriesMsg scme;
+ rfbBellMsg b;
+ rfbServerCutTextMsg sct;
+ rfbReSizeFrameBufferMsg rsfb;
+} rfbServerToClientMsg;
+
+
+
+/*****************************************************************************
+ *
+ * Message definitions (client -> server)
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * SetPixelFormat - tell the RFB server the format in which the client wants
+ * pixels sent.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetPixelFormat */
+ CARD8 pad1;
+ CARD16 pad2;
+ rfbPixelFormat format;
+} rfbSetPixelFormatMsg;
+
+#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4)
+
+
+/*-----------------------------------------------------------------------------
+ * FixColourMapEntries - when the pixel format uses a "colour map", fix
+ * read-only colour map entries.
+ *
+ * ***************** NOT CURRENTLY SUPPORTED *****************
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFixColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbFixColourMapEntriesMsg;
+
+#define sz_rfbFixColourMapEntriesMsg 6
+
+
+/*-----------------------------------------------------------------------------
+ * SetEncodings - tell the RFB server which encoding types we accept. Put them
+ * in order of preference, if we have any. We may always receive raw
+ * encoding, even if we don't specify it here.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetEncodings */
+ CARD8 pad;
+ CARD16 nEncodings;
+ /* followed by nEncodings * CARD32 encoding types */
+} rfbSetEncodingsMsg;
+
+#define sz_rfbSetEncodingsMsg 4
+
+/*-----------------------------------------------------------------------------
+ * SetScaleFactor - tell the RFB server to alter the scale factor for the
+ * client buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetScaleFactor */
+ CARD8 scale; /* Scale factor (positive non-zero integer) */
+ CARD16 pad2;
+} rfbSetScaleFactorMsg;
+
+#define sz_rfbSetScaleFactorMsg (4)
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdateRequest - request for a framebuffer update. If incremental
+ * is true then the client just wants the changes since the last update. If
+ * false then it wants the whole of the specified rectangle.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdateRequest */
+ CARD8 incremental;
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbFramebufferUpdateRequestMsg;
+
+#define sz_rfbFramebufferUpdateRequestMsg 10
+
+
+/*-----------------------------------------------------------------------------
+ * KeyEvent - key press or release
+ *
+ * Keys are specified using the "keysym" values defined by the X Window System.
+ * For most ordinary keys, the keysym is the same as the corresponding ASCII
+ * value. Other common keys are:
+ *
+ * BackSpace 0xff08
+ * Tab 0xff09
+ * Return or Enter 0xff0d
+ * Escape 0xff1b
+ * Insert 0xff63
+ * Delete 0xffff
+ * Home 0xff50
+ * End 0xff57
+ * Page Up 0xff55
+ * Page Down 0xff56
+ * Left 0xff51
+ * Up 0xff52
+ * Right 0xff53
+ * Down 0xff54
+ * F1 0xffbe
+ * F2 0xffbf
+ * ... ...
+ * F12 0xffc9
+ * Shift 0xffe1
+ * Control 0xffe3
+ * Meta 0xffe7
+ * Alt 0xffe9
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbKeyEvent */
+ CARD8 down; /* true if down (press), false if up */
+ CARD16 pad;
+ CARD32 key; /* key is specified as an X keysym */
+} rfbKeyEventMsg;
+
+#define sz_rfbKeyEventMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * PointerEvent - mouse/pen move and/or button press.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbPointerEvent */
+ CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */
+ CARD16 x;
+ CARD16 y;
+} rfbPointerEventMsg;
+
+#define rfbButton1Mask 1
+#define rfbButton2Mask 2
+#define rfbButton3Mask 4
+#define rfbButton4Mask 8
+#define rfbButton5Mask 16
+#define rfbWheelUpMask rfbButton4Mask
+#define rfbWheelDownMask rfbButton5Mask
+
+#define sz_rfbPointerEventMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * ClientCutText - the client has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbClientCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbClientCutTextMsg;
+
+#define sz_rfbClientCutTextMsg 8
+
+/*-----------------------------------------------------------------------------
+ * Union of all client->server messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbSetPixelFormatMsg spf;
+ rfbSetScaleFactorMsg ssf;
+ rfbFixColourMapEntriesMsg fcme;
+ rfbSetEncodingsMsg se;
+ rfbFramebufferUpdateRequestMsg fur;
+ rfbKeyEventMsg ke;
+ rfbPointerEventMsg pe;
+ rfbClientCutTextMsg cct;
+} rfbClientToServerMsg;