Refactor: rename "sub-filter" to "sub-source" this includes capability, functions...
[vlc/solaris.git] / modules / video_filter / remoteosd.c
blob29270ab4b35ef53af4d9d9893e1a06866848d8f2
1 /*****************************************************************************
2 * remoteosd.c: remote osd over vnc filter module
3 *****************************************************************************
4 * Copyright (C) 2007-2008 Matthias Bauer
5 * $Id$
7 * Authors: Matthias Bauer <matthias dot bauer #_at_# gmx dot ch>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implid warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * RemoteOSD uses the RFB-Protocol of VNC to display an On-Screen-Display
26 * menu generated by a streaming server as overlay for the streamed video.
28 * The streaming server that implements this is the ffnetdev plugin for VDR.
29 * VDR (VideoDiskRecorder) is an Linux based OpenSource harddisk recorder
30 * software.
31 * The VDR ffnetdev plugin emulates the hardware MPEG decoder and streams the
32 * video over the network instead of hardware video outputs.
33 * The OSD menu of VDR is offered with the RFB protocol to a VNC client.
35 * In fact this video-filter is a simple VNC client that could be also used to
36 * connect to a real VNC host.
37 * Only 8-bit color is supported at the moment.
38 * Using password protected VNC hosts is supported but not recommended, because
39 * you need to insert the used password in the plugin configuration page of
40 * VLC configuration in plain text and it's saved in plain text.
41 *****************************************************************************/
43 //#define VNC_DEBUG
45 /*****************************************************************************
46 * Preamble
47 *****************************************************************************/
49 #ifdef HAVE_CONFIG_H
50 # include "config.h"
51 #endif
53 #include <vlc_common.h>
54 #include <vlc_plugin.h>
55 #include <vlc_vout.h>
57 #include <vlc_filter.h>
58 #include <vlc_image.h>
59 #include <vlc_keys.h>
61 #include <vlc_network.h>
62 #include <gcrypt.h> /* to encrypt password */
63 #include <vlc_gcrypt.h>
65 #include "remoteosd_rfbproto.h" /* type definitions of the RFB protocol for VNC */
67 /*****************************************************************************
68 * Module descriptor
69 *****************************************************************************/
70 #define READ_BUFFER_SIZE 1000000
72 #define RMTOSD_HOST_TEXT N_("VNC Host")
73 #define RMTOSD_HOST_LONGTEXT N_( \
74 "VNC hostname or IP address." )
76 #define RMTOSD_PORT_TEXT N_("VNC Port")
77 #define RMTOSD_PORT_LONGTEXT N_( \
78 "VNC port number." )
80 #define RMTOSD_PASSWORD_TEXT N_("VNC Password")
81 #define RMTOSD_PASSWORD_LONGTEXT N_( \
82 "VNC password." )
84 #define RMTOSD_UPDATE_TEXT N_("VNC poll interval" )
85 #define RMTOSD_UPDATE_LONGTEXT N_( \
86 "In this interval an update from VNC is requested, default every 300 ms. ")
88 #define RMTOSD_POLL_TEXT N_("VNC polling")
89 #define RMTOSD_POLL_LONGTEXT N_( \
90 "Activate VNC polling. Do NOT activate for use as VDR ffnetdev client." )
92 #define RMTOSD_MOUSE_TEXT N_("Mouse events")
93 #define RMTOSD_MOUSE_LONGTEXT N_( \
94 "Send mouse events to VNC host. Not needed for use as VDR ffnetdev client." )
96 #define RMTOSD_KEYS_TEXT N_("Key events")
97 #define RMTOSD_KEYS_LONGTEXT N_( \
98 "Send key events to VNC host." )
100 #define RMTOSD_ALPHA_TEXT N_("Alpha transparency value (default 255)")
101 #define RMTOSD_ALPHA_LONGTEXT N_( \
102 "The transparency of the OSD VNC can be changed by giving a value " \
103 "between 0 and 255. A lower value specifies more transparency a higher " \
104 "means less transparency. The default is being not transparent " \
105 "(value 255) the minimum is fully transparent (value 0)." )
107 #define RMTOSD_CFG "rmtosd-"
109 #define RMTOSD_UPDATE_MIN 200
110 #define RMTOSD_UPDATE_DEFAULT 1000
111 #define RMTOSD_UPDATE_MAX 300
113 static int CreateFilter ( vlc_object_t * );
114 static void DestroyFilter( vlc_object_t * );
116 vlc_module_begin ()
117 set_description( N_("Remote-OSD over VNC") )
118 set_capability( "sub source", 100 )
119 set_shortname( N_("Remote-OSD") )
120 set_category( CAT_VIDEO )
121 set_subcategory( SUBCAT_VIDEO_SUBPIC )
122 add_shortcut( "rmtosd" )
123 set_callbacks( CreateFilter, DestroyFilter )
125 add_string( RMTOSD_CFG "host", "myvdr", RMTOSD_HOST_TEXT,
126 RMTOSD_HOST_LONGTEXT, false )
127 add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF, NULL,
128 RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false )
129 add_password( RMTOSD_CFG "password", "", RMTOSD_PASSWORD_TEXT,
130 RMTOSD_PASSWORD_LONGTEXT, false )
131 add_integer_with_range( RMTOSD_CFG "update", RMTOSD_UPDATE_DEFAULT,
132 RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, NULL, RMTOSD_UPDATE_TEXT,
133 RMTOSD_UPDATE_LONGTEXT, true )
134 add_bool( RMTOSD_CFG "vnc-polling", false,
135 RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false )
136 add_bool( RMTOSD_CFG "mouse-events", false,
137 RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false )
138 add_bool( RMTOSD_CFG "key-events", false,
139 RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false )
140 add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255, NULL,
141 RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true )
143 vlc_module_end ()
146 /*****************************************************************************
147 * Local prototypes
148 *****************************************************************************/
149 #define CHALLENGESIZE 16
150 #define MAX_VNC_SERVER_NAME_LENGTH 255
152 /* subsource functions */
153 static subpicture_t *Filter( filter_t *, mtime_t );
155 static int MouseEvent( filter_t *,
156 const vlc_mouse_t *,
157 const vlc_mouse_t *,
158 const video_format_t * );
160 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
161 vlc_value_t oldval, vlc_value_t newval, void *p_data );
163 static void stop_osdvnc ( filter_t *p_filter );
165 static void* vnc_worker_thread ( vlc_object_t *p_thread_obj );
167 static void* update_request_thread( vlc_object_t *p_thread_obj );
169 static bool open_vnc_connection ( filter_t *p_filter );
171 static bool handshaking ( filter_t *p_filter );
173 static bool process_server_message ( filter_t *p_filter,
174 rfbServerToClientMsg *msg );
176 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
177 int r, int g, int b );
179 static inline bool fill_rect( filter_sys_t* p_sys,
180 uint16_t i_x, uint16_t i_y,
181 uint16_t i_w, uint16_t i_h,
182 uint8_t i_color );
183 static inline bool copy_rect( filter_sys_t* p_sys,
184 uint16_t i_x, uint16_t i_y,
185 uint16_t i_w, uint16_t i_h,
186 uint16_t i_sx, uint16_t i_sy );
189 static inline bool raw_line( filter_sys_t* p_sys,
190 uint16_t i_x, uint16_t i_y,
191 uint16_t i_w );
193 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
196 /*****************************************************************************
197 * Sub source code
198 *****************************************************************************/
200 /*****************************************************************************
201 * Local prototypes
202 *****************************************************************************/
203 struct filter_sys_t
205 bool b_need_update; /* VNC picture is updated, do update the OSD*/
206 mtime_t i_vnc_poll_interval; /* Update the OSD menu every n ms */
208 uint8_t i_alpha; /* alpha transparency value */
210 char *psz_host; /* VNC host */
211 int i_port;
213 char *psz_passwd; /* VNC password */
215 bool b_vnc_poll; /* Activate VNC polling ? */
216 bool b_vnc_mouse_events; /* Send MouseEvents ? */
217 bool b_vnc_key_events; /* Send KeyEvents ? */
219 bool b_connection_active; /* Handshaking finished ? */
221 vlc_mutex_t lock; /* To lock for read/write on picture */
223 picture_t *p_pic; /* The picture with OSD data from VNC */
225 int i_socket; /* Socket used for VNC */
227 uint16_t i_vnc_width; /* The with of the VNC screen */
228 uint16_t i_vnc_height; /* The height of the VNC screen */
229 uint32_t i_vnc_pixels; /* The pixels of the VNC screen */
231 bool b_alpha_from_vnc; /* Special ffnetdev alpha feature enabled ? */
233 char read_buffer[READ_BUFFER_SIZE];
235 bool b_continue;
237 vlc_object_t* p_worker_thread;
239 uint8_t ar_color_table_yuv[256][4];
242 /*****************************************************************************
243 * CreateFilter: Create the filter and open the definition file
244 *****************************************************************************/
245 static int CreateFilter ( vlc_object_t *p_this )
247 filter_t *p_filter = (filter_t *)p_this;
248 filter_sys_t *p_sys = NULL;
250 msg_Dbg( p_filter, "Creating vnc osd filter..." );
252 p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
253 if( !p_filter->p_sys )
254 return VLC_ENOMEM;
256 /* Populating struct */
257 vlc_mutex_init( &p_sys->lock );
258 p_sys->b_continue = true;
259 p_sys->i_socket = -1;
260 p_sys->p_pic = NULL;
262 p_sys->psz_host = var_CreateGetString( p_this, RMTOSD_CFG "host" );
263 if( EMPTY_STR(p_sys->psz_host) )
265 msg_Err( p_filter, "unable to get vnc host" );
266 goto error;
269 p_sys->psz_passwd = var_CreateGetString( p_this, RMTOSD_CFG "password" );
270 if( !p_sys->psz_passwd )
272 msg_Err( p_filter, "unable to get vnc password" );
273 goto error;
276 p_sys->i_port = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "port" );
278 p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "alpha" );
280 /* in milliseconds, 0 disables polling, should not be lower than 100 */
281 p_sys->i_vnc_poll_interval = var_CreateGetIntegerCommand( p_this,
282 RMTOSD_CFG "update" );
283 if ( p_sys->i_vnc_poll_interval < 100)
285 p_sys->i_vnc_poll_interval = 100;
288 for ( int i = 0; i < 256; i++ )
290 p_sys->ar_color_table_yuv[i][0] = 255;
291 p_sys->ar_color_table_yuv[i][1] = 255;
292 p_sys->ar_color_table_yuv[i][2] = 255;
293 p_sys->ar_color_table_yuv[i][3] = 255;
296 p_sys->b_vnc_poll = var_CreateGetBoolCommand( p_this,
297 RMTOSD_CFG "vnc-polling" );
298 p_sys->b_vnc_mouse_events = var_CreateGetBoolCommand( p_this,
299 RMTOSD_CFG "mouse-events" );
300 p_sys->b_vnc_key_events = var_CreateGetBoolCommand( p_this,
301 RMTOSD_CFG "key-events" );
303 /* Keep track of OSD Events */
304 p_sys->b_need_update = false;
306 /* Attach subpicture source callback */
307 p_filter->pf_sub_source = Filter;
308 p_filter->pf_sub_mouse = MouseEvent;
310 var_AddCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
312 es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_CODEC_SPU );
313 p_filter->fmt_out.i_priority = 0;
315 vlc_gcrypt_init();
317 /* create the vnc worker thread */
318 p_sys->p_worker_thread = vlc_object_create( p_this,
319 sizeof( vlc_object_t ) );
320 vlc_object_attach( p_sys->p_worker_thread, p_this );
321 if( vlc_thread_create( p_sys->p_worker_thread,
322 vnc_worker_thread, VLC_THREAD_PRIORITY_LOW ) )
324 vlc_object_release( p_sys->p_worker_thread );
325 msg_Err( p_filter, "cannot spawn vnc message reader thread" );
326 goto error;
329 msg_Dbg( p_filter, "osdvnc filter started" );
331 return VLC_SUCCESS;
333 error:
334 msg_Err( p_filter, "osdvnc filter discarded" );
336 stop_osdvnc( p_filter );
338 vlc_mutex_destroy( &p_sys->lock );
339 free( p_sys->psz_host );
340 free( p_sys->psz_passwd );
341 free( p_sys );
343 return VLC_EGENERIC;
346 /*****************************************************************************
347 * DestroyFilter: Make a clean exit of this plugin
348 *****************************************************************************/
349 static void DestroyFilter( vlc_object_t *p_this )
351 filter_t *p_filter = (filter_t*)p_this;
352 filter_sys_t *p_sys = p_filter->p_sys;
354 msg_Dbg( p_filter, "DestroyFilter called." );
356 stop_osdvnc( p_filter );
358 var_DelCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
360 var_Destroy( p_this, RMTOSD_CFG "host" );
361 var_Destroy( p_this, RMTOSD_CFG "port" );
362 var_Destroy( p_this, RMTOSD_CFG "password" );
363 var_Destroy( p_this, RMTOSD_CFG "update" );
364 var_Destroy( p_this, RMTOSD_CFG "vnc-polling" );
365 var_Destroy( p_this, RMTOSD_CFG "mouse-events" );
366 var_Destroy( p_this, RMTOSD_CFG "key-events" );
367 var_Destroy( p_this, RMTOSD_CFG "alpha" );
369 vlc_mutex_destroy( &p_sys->lock );
370 free( p_sys->psz_host );
371 free( p_sys->psz_passwd );
372 free( p_sys );
375 static void stop_osdvnc ( filter_t *p_filter )
377 filter_sys_t *p_sys = p_filter->p_sys;
379 /* It will unlock socket reading */
380 vlc_object_kill( p_filter );
382 /* */
383 if( p_sys->p_worker_thread )
385 msg_Dbg( p_filter, "joining worker_thread" );
386 vlc_object_kill( p_sys->p_worker_thread );
387 vlc_thread_join( p_sys->p_worker_thread );
388 vlc_object_release( p_sys->p_worker_thread );
389 msg_Dbg( p_filter, "released worker_thread" );
392 msg_Dbg( p_filter, "osdvnc stopped" );
395 static bool read_exact( filter_t *p_filter,
396 int i_socket,
397 char* p_readbuf,
398 int i_bytes )
400 return i_bytes == net_Read( p_filter, i_socket, NULL,
401 (unsigned char*)p_readbuf,
402 i_bytes, true );
406 static bool write_exact( filter_t *p_filter,
407 int i_socket,
408 char* p_writebuf,
409 int i_bytes )
411 return i_bytes == net_Write( p_filter, i_socket, NULL,
412 (unsigned char*)p_writebuf, i_bytes );
415 static bool open_vnc_connection ( filter_t *p_filter )
417 filter_sys_t *p_sys = p_filter->p_sys;
419 msg_Dbg( p_filter, "Open socket to vnc server on %s:%u.",
420 p_sys->psz_host, p_sys->i_port );
422 p_sys->i_socket = net_ConnectTCP( p_filter, p_sys->psz_host, p_sys->i_port );
424 if( p_sys->i_socket < 0 )
426 msg_Err( p_filter, "Could not open socket" );
427 return false;
430 msg_Dbg( p_filter, "socket is open." );
432 return true;
435 static bool handshaking ( filter_t *p_filter )
437 filter_sys_t *p_sys = p_filter->p_sys;
439 msg_Dbg( p_filter, "Reading protocol version" );
441 rfbProtocolVersionMsg pv;
442 if ( !read_exact( p_filter, p_sys->i_socket, pv,
443 sz_rfbProtocolVersionMsg ) )
445 msg_Err( p_filter, "Could not read version message" );
446 return false;
448 pv[sz_rfbProtocolVersionMsg] = '\0'; /* pv size is sz_rfbProtocolVersionMsg+1 */
450 msg_Dbg( p_filter, "Server version is %s", pv );
452 strncpy(pv, "RFB 003.003\n", sz_rfbProtocolVersionMsg);
454 if( !write_exact(p_filter, p_sys->i_socket, pv,
455 sz_rfbProtocolVersionMsg) )
457 msg_Err( p_filter, "Could not write version message" );
458 return false;
461 msg_Dbg( p_filter, "Reading authentication scheme" );
462 uint32_t i_authScheme;
463 if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authScheme, 4 ) )
465 msg_Err( p_filter, "Could not read authentication scheme" );
466 return false;
468 i_authScheme = htonl(i_authScheme);
470 msg_Dbg( p_filter, "Authentication scheme = %x", i_authScheme );
471 if ( i_authScheme == rfbConnFailed )
473 msg_Err( p_filter, "Connection rejected by server" );
474 return false;
476 if (i_authScheme == rfbVncAuth)
478 unsigned char challenge[CHALLENGESIZE];
479 if ( !read_exact( p_filter, p_sys->i_socket,
480 (char*)challenge, CHALLENGESIZE ) )
482 msg_Err( p_filter, "Could not read password challenge" );
483 return false;
486 vnc_encrypt_bytes( challenge, p_sys->psz_passwd );
488 if( !write_exact(p_filter, p_sys->i_socket,
489 (char*)challenge, CHALLENGESIZE ) )
491 msg_Err( p_filter, "Could not write password" );
492 return false;
494 uint32_t i_authResult;
495 if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authResult, 4 ) )
497 msg_Err( p_filter, "Could not read authentication result" );
498 return false;
500 i_authResult = htonl(i_authResult);
501 if (i_authResult != rfbVncAuthOK)
503 msg_Err( p_filter, "VNC authentication failed" );
504 return false;
508 msg_Dbg( p_filter, "Writing client init message" );
509 rfbClientInitMsg ci;
510 ci.shared = 1;
511 if( !write_exact( p_filter, p_sys->i_socket,
512 (char*)&ci, sz_rfbClientInitMsg ) )
514 msg_Err( p_filter, "Could not write client init message" );
515 return false;
518 msg_Dbg( p_filter, "Reading server init message" );
519 rfbServerInitMsg si;
520 if( !read_exact( p_filter, p_sys->i_socket,
521 (char*)&si, sz_rfbServerInitMsg ) )
523 msg_Err( p_filter, "Could not read server init message" );
524 return false;
526 si.framebufferWidth = htons(si.framebufferWidth);
527 si.framebufferHeight = htons(si.framebufferHeight);
528 si.format.redMax = htons(si.format.redMax);
529 si.format.greenMax = htons(si.format.greenMax);
530 si.format.blueMax = htons(si.format.blueMax);
532 p_sys->i_vnc_width = si.framebufferWidth;
533 p_sys->i_vnc_height = si.framebufferHeight;
535 msg_Dbg( p_filter, "Servers preferred pixelformat: "
536 "%ux%u, R(%u),G(%u),B(%u), %u bit, depht=%u, %s",
537 si.framebufferWidth,
538 si.framebufferHeight,
539 si.format.redMax,
540 si.format.greenMax,
541 si.format.blueMax,
542 si.format.bitsPerPixel,
543 si.format.depth,
544 si.format.trueColour ? "TrueColor" : "Not-TrueColor");
546 uint32_t i_nameLength = htonl(si.nameLength);
547 if( i_nameLength > MAX_VNC_SERVER_NAME_LENGTH )
549 msg_Err( p_filter, "Server name too long" );
550 return false;
552 char s_ServerName[MAX_VNC_SERVER_NAME_LENGTH+1];
554 msg_Dbg( p_filter, "Reading server name with size = %u", i_nameLength );
555 if( !read_exact( p_filter, p_sys->i_socket, s_ServerName, i_nameLength ) )
557 msg_Err( p_filter, "Could not read server name" );
558 return false;
560 s_ServerName[i_nameLength] = '\0';
562 if( strcmp( s_ServerName, "VDR-OSD") == 0 )
564 msg_Dbg( p_filter, "Server is a VDR" );
565 p_sys->b_alpha_from_vnc = true;
567 else
569 msg_Dbg( p_filter, "Server is a normal VNC" );
570 p_sys->b_alpha_from_vnc = false;
574 msg_Dbg( p_filter, "Server init message read properly" );
575 msg_Dbg( p_filter, "Server name is %s", s_ServerName );
577 msg_Dbg( p_filter, "Writing SetPixelFormat message" );
579 rfbSetPixelFormatMsg sp;
580 sp.type = rfbSetPixelFormat;
581 sp.pad1 = sp.pad2 = 0;
582 sp.format.bitsPerPixel = 8;
583 sp.format.depth = 8 ;
584 sp.format.bigEndian = 1;
585 sp.format.trueColour = 0;
586 sp.format.redMax = htons(31);
587 sp.format.greenMax = htons(31);
588 sp.format.blueMax = htons(31);
589 sp.format.redShift = 10;
590 sp.format.greenShift = 5;
591 sp.format.blueShift = 0;
592 sp.format.pad1 = sp.format.pad2 = 0;
594 if( !write_exact( p_filter, p_sys->i_socket,
595 (char*)&sp, sz_rfbSetPixelFormatMsg) )
597 msg_Err( p_filter, "Could not write SetPixelFormat message" );
598 return false;
601 msg_Dbg( p_filter, "Writing SetEncodings message" );
603 rfbSetEncodingsMsg se;
604 se.type = rfbSetEncodings;
605 se.pad = 0;
606 se.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
608 if( !write_exact( p_filter, p_sys->i_socket,
609 (char*)&se, sz_rfbSetEncodingsMsg) )
611 msg_Err( p_filter, "Could not write SetEncodings message begin" );
612 return false;
615 uint32_t i_encoding;
617 msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingCopyRect" );
618 i_encoding = htonl(rfbEncodingCopyRect);
619 if( !write_exact( p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
621 msg_Err( p_filter, "Could not write encoding type rfbEncodingCopyRect." );
622 return false;
625 msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingRRE" );
626 i_encoding = htonl(rfbEncodingRRE);
627 if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
629 msg_Err( p_filter, "Could not write encoding type rfbEncodingRRE." );
630 return false;
633 if( p_sys->b_alpha_from_vnc )
635 msg_Dbg( p_filter, "Writing SetEncodings rfbEncSpecialUseAlpha" );
636 i_encoding = 0x00F0FFFF; /* rfbEncSpecialUseAlpha is 0xFFFFF000
637 * before we swap it */
638 if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
640 msg_Err( p_filter, "Could not write encoding type rfbEncSpecialUseAlpha." );
641 return false;
644 return true;
648 static void* vnc_worker_thread( vlc_object_t *p_thread_obj )
650 filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
651 filter_sys_t *p_sys = p_filter->p_sys;
652 vlc_object_t *p_update_request_thread;
653 int canc = vlc_savecancel ();
655 msg_Dbg( p_filter, "VNC worker thread started" );
657 if( !open_vnc_connection ( p_filter ) )
659 msg_Err( p_filter, "Could not connect to vnc host" );
660 goto exit;
663 if( !handshaking ( p_filter ) )
665 msg_Err( p_filter, "Error occured while handshaking vnc host" );
666 goto exit;
669 p_sys->b_connection_active = true; /* to enable sending key
670 * and mouse events to host */
672 /* Create an empty picture for VNC the data */
673 vlc_mutex_lock( &p_sys->lock );
674 p_sys->p_pic = picture_New( VLC_CODEC_YUVA,
675 p_sys->i_vnc_width, p_sys->i_vnc_height, 1, 1 );
676 if( !p_sys->p_pic )
678 vlc_mutex_unlock( &p_sys->lock );
679 goto exit;
681 p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
683 vlc_mutex_unlock( &p_sys->lock );
685 /* create the update request thread */
686 p_update_request_thread = vlc_object_create( p_filter,
687 sizeof( vlc_object_t ) );
688 vlc_object_attach( p_update_request_thread, p_filter );
689 if( vlc_thread_create( p_update_request_thread,
690 update_request_thread, VLC_THREAD_PRIORITY_LOW ) )
692 vlc_object_release( p_update_request_thread );
693 msg_Err( p_filter, "cannot spawn vnc update request thread" );
694 goto exit;
697 /* connection is initialized, now read and handle server messages */
698 while( vlc_object_alive( p_thread_obj ) )
700 rfbServerToClientMsg msg;
701 int i_msgSize;
703 memset( &msg, 0, sizeof(msg) );
705 if( !read_exact(p_filter, p_sys->i_socket, (char*)&msg, 1 ) )
707 msg_Err( p_filter, "Error while waiting for next server message");
708 break;
710 switch (msg.type)
712 case rfbFramebufferUpdate:
713 i_msgSize = sz_rfbFramebufferUpdateMsg;
714 break;
715 case rfbSetColourMapEntries:
716 i_msgSize = sz_rfbSetColourMapEntriesMsg;
717 break;
718 case rfbBell:
719 i_msgSize = sz_rfbBellMsg;
720 break;
721 case rfbServerCutText:
722 i_msgSize = sz_rfbServerCutTextMsg;
723 break;
724 case rfbReSizeFrameBuffer:
725 i_msgSize = sz_rfbReSizeFrameBufferMsg;
726 break;
727 default:
728 i_msgSize = 0;
729 msg_Err( p_filter, "Invalid message %u received", msg.type );
730 break;
733 if( i_msgSize <= 0 )
734 break;
736 if( --i_msgSize > 0 )
738 if ( !read_exact( p_filter, p_sys->i_socket,
739 ((char*)&msg)+1, i_msgSize ) )
741 msg_Err( p_filter, "Error while reading message of type %u",
742 msg.type );
743 break;
746 process_server_message( p_filter, &msg);
749 msg_Dbg( p_filter, "joining update_request_thread" );
750 vlc_object_kill( p_update_request_thread );
751 vlc_thread_join( p_update_request_thread );
752 vlc_object_release( p_update_request_thread );
753 msg_Dbg( p_filter, "released update_request_thread" );
755 exit:
757 vlc_mutex_lock( &p_sys->lock );
758 p_sys->b_connection_active = false;
760 if (p_sys->i_socket >= 0)
761 net_Close(p_sys->i_socket);
763 if( p_sys->p_pic )
764 picture_Release( p_sys->p_pic );
766 /* It will hide the subtitle */
767 p_sys->b_continue = false;
768 p_sys->b_need_update = true;
769 vlc_mutex_unlock( &p_sys->lock );
771 msg_Dbg( p_filter, "VNC message reader thread ended" );
772 vlc_restorecancel (canc);
773 return NULL;
776 static void* update_request_thread( vlc_object_t *p_thread_obj )
778 filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
779 filter_sys_t *p_sys = p_filter->p_sys;
780 int canc = vlc_savecancel ();
782 msg_Dbg( p_filter, "VNC update request thread started" );
784 rfbFramebufferUpdateRequestMsg udr;
785 udr.type = rfbFramebufferUpdateRequest;
786 udr.incremental = 0;
787 udr.x = 0;
788 udr.y = 0;
789 udr.w = htons(p_sys->i_vnc_width);
790 udr.h = htons(p_sys->i_vnc_height);
792 if( !write_exact(p_filter, p_sys->i_socket, (char*)&udr,
793 sz_rfbFramebufferUpdateRequestMsg) )
795 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
796 p_sys->b_continue = false;
797 return NULL;
800 udr.incremental = 1;
801 mtime_t i_poll_interval_microsec = p_sys->i_vnc_poll_interval * 1000;
803 if( p_sys->b_vnc_poll)
805 while( vlc_object_alive( p_thread_obj ) )
807 msleep( i_poll_interval_microsec );
808 if( !write_exact(p_filter, p_sys->i_socket, (char*)&udr,
809 sz_rfbFramebufferUpdateRequestMsg))
811 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
812 break;
815 p_sys->b_continue = false;
817 else
819 msg_Dbg( p_filter, "VNC polling disabled." );
822 msg_Dbg( p_filter, "VNC update request thread ended" );
823 vlc_restorecancel (canc);
824 return NULL;
827 static bool process_server_message ( filter_t *p_filter,
828 rfbServerToClientMsg *msg )
830 filter_sys_t *p_sys = p_filter->p_sys;
832 switch (msg->type)
834 case rfbFramebufferUpdate:
836 msg->fu.nRects = htons(msg->fu.nRects);
837 rfbFramebufferUpdateRectHeader hdr;
839 for (int i_rect = 0; i_rect < msg->fu.nRects; i_rect++)
841 if (!read_exact(p_filter, p_sys->i_socket, (char*)&hdr,
842 sz_rfbFramebufferUpdateRectHeader ) )
844 msg_Err( p_filter, "Could not read FrameBufferUpdate header" );
845 return false;
847 hdr.r.x = htons(hdr.r.x);
848 hdr.r.y = htons(hdr.r.y);
849 hdr.r.w = htons(hdr.r.w);
850 hdr.r.h = htons(hdr.r.h);
851 hdr.encoding = htonl(hdr.encoding);
853 switch (hdr.encoding)
855 case rfbEncodingRaw:
857 int i_line;
858 for (i_line = 0; i_line < hdr.r.h; i_line++)
860 if ( !read_exact( p_filter, p_sys->i_socket,
861 p_sys->read_buffer, hdr.r.w ) )
863 msg_Err( p_filter,
864 "Could not read FrameBufferUpdate line data" );
865 return false;
867 vlc_mutex_lock( &p_sys->lock );
868 if ( !raw_line( p_sys, hdr.r.x,
869 hdr.r.y + i_line,
870 hdr.r.w ) )
872 msg_Err( p_filter, "raw_line failed." );
873 vlc_mutex_unlock( &p_sys->lock );
874 return false;
876 vlc_mutex_unlock( &p_sys->lock );
879 break;
881 case rfbEncodingCopyRect:
883 rfbCopyRect rect;
885 if ( !read_exact( p_filter, p_sys->i_socket,
886 (char*)&rect,
887 sz_rfbCopyRect ) )
889 msg_Err( p_filter, "Could not read rfbCopyRect" );
890 return false;
892 rect.srcX = htons( rect.srcX );
893 rect.srcY = htons( rect.srcY );
895 vlc_mutex_lock( &p_sys->lock );
896 if ( !copy_rect( p_sys,
897 hdr.r.x, hdr.r.y,
898 hdr.r.w, hdr.r.h,
899 rect.srcX, rect.srcY ) )
901 msg_Err( p_filter, "copy_rect failed." );
902 vlc_mutex_unlock( &p_sys->lock );
903 return false;
905 vlc_mutex_unlock( &p_sys->lock );
907 break;
909 case rfbEncodingRRE:
911 rfbRREHeader rrehdr;
912 if ( !read_exact( p_filter, p_sys->i_socket,
913 (char*)&rrehdr,
914 sz_rfbRREHeader ) )
916 msg_Err( p_filter, "Could not read rfbRREHeader" );
917 return false;
919 uint8_t i_pixcolor;
920 if ( !read_exact(p_filter, p_sys->i_socket,
921 (char*)&i_pixcolor, 1 ) )
923 msg_Err( p_filter, "Could not read RRE pixcolor" );
924 return false;
927 vlc_mutex_lock( &p_sys->lock );
928 if ( !fill_rect( p_sys,
929 hdr.r.x, hdr.r.y,
930 hdr.r.w, hdr.r.h,
931 i_pixcolor) )
933 msg_Err( p_filter, "main fill_rect failed." );
934 vlc_mutex_unlock( &p_sys->lock );
935 return false;
937 vlc_mutex_unlock( &p_sys->lock );
939 rrehdr.nSubrects = htonl(rrehdr.nSubrects);
941 int i_datasize = rrehdr.nSubrects *
942 ( sizeof(i_pixcolor) + sz_rfbRectangle ) ;
943 if ( i_datasize > READ_BUFFER_SIZE )
945 msg_Err( p_filter, "Buffer too small, "
946 "need %u bytes", i_datasize );
947 return false;
949 if ( !read_exact( p_filter, p_sys->i_socket,
950 p_sys->read_buffer, i_datasize ) )
952 msg_Err( p_filter,
953 "Could not read RRE subrect data" );
954 return false;
957 uint32_t i_subrect;
958 rfbRectangle* p_subrect;
959 int i_offset = 0;
960 vlc_mutex_lock( &p_sys->lock );
961 for ( i_subrect = 0;
962 i_subrect < rrehdr.nSubrects; i_subrect++)
964 i_pixcolor = p_sys->read_buffer[i_offset];
965 i_offset += sizeof(i_pixcolor);
966 p_subrect =
967 (rfbRectangle*)(p_sys->read_buffer + i_offset);
968 i_offset += sz_rfbRectangle;
970 if (!fill_rect( p_sys,
971 htons(p_subrect->x) + hdr.r.x,
972 htons(p_subrect->y) + hdr.r.y,
973 htons(p_subrect->w),
974 htons(p_subrect->h),
975 i_pixcolor) )
977 msg_Err( p_filter,
978 "subrect %u fill_rect failed.", i_subrect );
979 vlc_mutex_unlock( &p_sys->lock );
980 return false;
983 vlc_mutex_unlock( &p_sys->lock );
985 break;
990 vlc_mutex_lock( &p_sys->lock );
991 p_sys->b_need_update = true;
992 vlc_mutex_unlock( &p_sys->lock );
994 return true;
996 case rfbSetColourMapEntries:
998 msg->scme.nColours = htons(msg->scme.nColours);
999 msg->scme.firstColour = htons(msg->scme.firstColour);
1000 int i_datasize;
1001 if ( p_sys->b_alpha_from_vnc )
1003 i_datasize = 2 * msg->scme.nColours * 4;
1005 else
1007 i_datasize = 2 * msg->scme.nColours * 3;
1009 if ( i_datasize > READ_BUFFER_SIZE )
1011 msg_Err( p_filter, "Buffer too small, need %u bytes",
1012 i_datasize );
1013 return false;
1016 if ( !read_exact( p_filter, p_sys->i_socket,
1017 p_sys->read_buffer, i_datasize ) )
1019 msg_Err( p_filter, "Could not read color map data" );
1020 return false;
1023 uint8_t i_red, i_green, i_blue, i_alpha, i_color_index;
1024 uint16_t i_offset = 0;
1025 i_alpha = 255;
1027 for (int i = 0; i < msg->scme.nColours; i++)
1029 i_color_index = i+msg->scme.firstColour;
1030 if ( p_sys->b_alpha_from_vnc )
1032 i_alpha = p_sys->read_buffer[i_offset];
1033 i_offset += 2;
1035 i_red = p_sys->read_buffer[i_offset];
1036 i_offset += 2;
1037 i_green = p_sys->read_buffer[i_offset];
1038 i_offset += 2;
1039 i_blue = p_sys->read_buffer[i_offset];
1040 i_offset += 2;
1041 rgb_to_yuv( &p_sys->ar_color_table_yuv[i_color_index][0],
1042 &p_sys->ar_color_table_yuv[i_color_index][1],
1043 &p_sys->ar_color_table_yuv[i_color_index][2],
1044 i_red,
1045 i_green,
1046 i_blue );
1047 p_sys->ar_color_table_yuv[i][3] = i_alpha;
1050 return true;
1052 case rfbBell:
1053 msg_Err( p_filter, "rfbBell received" );
1054 return true;
1056 case rfbServerCutText:
1057 msg->sct.length = htons(msg->sct.length);
1058 if ( msg->sct.length > READ_BUFFER_SIZE )
1060 msg_Err( p_filter, "Buffer too small, need %u bytes", msg->sct.length );
1061 return false;
1063 if ( !read_exact(p_filter, p_sys->i_socket,
1064 p_sys->read_buffer, msg->sct.length ) )
1066 msg_Err( p_filter, "Could not read Reading rfbServerCutText data" );
1067 return false;
1069 return true;
1071 case rfbReSizeFrameBuffer:
1072 msg_Err( p_filter, "Reading rfbReSizeFrameBuffer not implemented" );
1073 return false;
1075 default:
1076 msg_Err( p_filter, "Invalid message %u received", msg->type );
1077 return false;
1079 return false;
1082 /****************************************************************************
1083 * Filter: the whole thing
1084 ****************************************************************************
1085 * This function outputs subpictures at regular time intervals.
1086 ****************************************************************************/
1087 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
1089 filter_sys_t *p_sys = p_filter->p_sys;
1090 subpicture_t *p_spu;
1091 subpicture_region_t *p_region;
1092 video_format_t fmt;
1093 picture_t *p_pic;
1095 if( !p_sys->b_need_update )
1097 return NULL;
1100 vlc_mutex_lock( &p_sys->lock );
1102 p_pic = p_sys->p_pic;
1104 if( !p_pic )
1106 vlc_mutex_unlock( &p_sys->lock );
1107 return NULL;
1110 /* Allocate the subpicture internal data. */
1111 p_spu = filter_NewSubpicture( p_filter );
1112 if( !p_spu )
1114 vlc_mutex_unlock( &p_sys->lock );
1115 return NULL;
1118 p_spu->b_absolute = false;
1119 p_spu->i_start = date;
1120 p_spu->i_stop = 0;
1121 p_spu->b_ephemer = true;
1123 if( !p_sys->b_continue )
1124 p_spu->i_stop = p_spu->i_start + 1;
1126 /* Create new SPU region */
1127 memset( &fmt, 0, sizeof(video_format_t) );
1128 fmt.i_chroma = VLC_CODEC_YUVA;
1129 fmt.i_sar_num = fmt.i_sar_den = 1;
1130 fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
1131 fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
1132 fmt.i_x_offset = fmt.i_y_offset = 0;
1133 p_region = subpicture_region_New( &fmt );
1134 if( !p_region )
1136 msg_Err( p_filter, "cannot allocate SPU region" );
1137 p_filter->pf_sub_buffer_del( p_filter, p_spu );
1138 vlc_mutex_unlock( &p_sys->lock );
1139 return NULL;
1142 /* FIXME the copy is probably not needed anymore */
1143 picture_Copy( p_region->p_picture, p_pic );
1145 p_sys->b_need_update = false;
1147 vlc_mutex_unlock( &p_sys->lock );
1149 /* set to one of the 9 relative locations */
1150 p_region->i_align = 0; /* Center */
1151 p_spu->b_absolute = false;
1154 p_spu->i_original_picture_width = 0; /*Let vout core do the horizontal scaling */
1155 p_spu->i_original_picture_height = fmt.i_height;
1157 p_spu->p_region = p_region;
1159 p_spu->i_alpha = ( p_sys->i_alpha );
1161 return p_spu;
1165 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
1166 int r, int g, int b )
1168 *y = ( ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16 );
1169 *u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
1170 *v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ;
1173 static inline bool fill_rect( filter_sys_t* p_sys,
1174 uint16_t i_x, uint16_t i_y,
1175 uint16_t i_w, uint16_t i_h,
1176 uint8_t i_color)
1178 plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1179 plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1180 plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1181 plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1182 int i_pitch = p_outY->i_pitch;
1183 int i_lines = p_outY->i_lines;
1184 if ( i_x + i_w > i_pitch)
1185 return false;
1186 if ( i_y + i_h > i_lines)
1187 return false;
1188 int i_line_offset = i_y * i_pitch;
1189 uint8_t i_yuv_y = p_sys->ar_color_table_yuv[i_color][0];
1190 uint8_t i_yuv_u = p_sys->ar_color_table_yuv[i_color][1];
1191 uint8_t i_yuv_v = p_sys->ar_color_table_yuv[i_color][2];
1192 uint8_t i_alpha = p_sys->ar_color_table_yuv[i_color][3];
1193 for( int i_line = 0; i_line < i_h; i_line++ )
1195 for( int i_column = 0; i_column < i_w; i_column++ )
1197 int i_total_offset = i_line_offset + i_x + i_column;
1198 p_outY->p_pixels[ i_total_offset ] = i_yuv_y;
1199 p_outU->p_pixels[ i_total_offset ] = i_yuv_u;
1200 p_outV->p_pixels[ i_total_offset ] = i_yuv_v;
1201 p_outA->p_pixels[ i_total_offset ] = i_alpha;
1203 i_line_offset += i_pitch;
1205 return true;
1208 static inline bool copy_rect( filter_sys_t* p_sys,
1209 uint16_t i_x, uint16_t i_y,
1210 uint16_t i_w, uint16_t i_h,
1211 uint16_t i_sx, uint16_t i_sy )
1213 plane_t *p_Y = p_sys->p_pic->p+Y_PLANE;
1214 plane_t *p_U = p_sys->p_pic->p+U_PLANE;
1215 plane_t *p_V = p_sys->p_pic->p+V_PLANE;
1216 plane_t *p_A = p_sys->p_pic->p+A_PLANE;
1218 int i_pitch = p_Y->i_pitch;
1219 int i_lines = p_Y->i_lines;
1221 fprintf( stderr, "copy_rect: (%d,%d)+(%d,%d) -> (%d,%d)\n", i_x, i_y, i_w, i_h, i_sx, i_sy );
1223 if( i_x + i_w > i_pitch || i_sx + i_w > i_pitch )
1224 return false;
1225 if( i_y + i_h > i_lines || i_sy + i_h > i_lines)
1226 return false;
1228 if( i_w <= 0 || i_h <= 0 )
1229 return true;
1231 uint8_t *pb_buffer = calloc( i_w * i_h, 4 );
1232 if( !pb_buffer )
1233 return false;
1235 for( int i_line = 0; i_line < i_h; i_line++ )
1237 for( int i_column = 0; i_column < i_w; i_column++ )
1239 const int i_src_offset = ( i_sy + i_line ) * i_pitch + i_sx + i_column;
1240 const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
1242 pb_buffer[4*i_tmp_offset + 0] = p_Y->p_pixels[i_src_offset];
1243 pb_buffer[4*i_tmp_offset + 1] = p_U->p_pixels[i_src_offset];
1244 pb_buffer[4*i_tmp_offset + 2] = p_V->p_pixels[i_src_offset];
1245 pb_buffer[4*i_tmp_offset + 3] = p_A->p_pixels[i_src_offset];
1249 for( int i_line = 0; i_line < i_h; i_line++ )
1251 for( int i_column = 0; i_column < i_w; i_column++ )
1253 const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
1254 const int i_dst_offset = ( i_y + i_line ) * i_pitch + i_x + i_column;
1256 p_Y->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 0];
1257 p_U->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 1];
1258 p_V->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 2];
1259 p_A->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 3];
1262 free( pb_buffer );
1263 return true;
1267 static inline bool raw_line( filter_sys_t* p_sys,
1268 uint16_t i_x, uint16_t i_y,
1269 uint16_t i_w )
1271 plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1272 plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1273 plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1274 plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1275 int i_pitch = p_outY->i_pitch;
1276 int i_lines = p_outY->i_lines;
1277 if ( i_x + i_w > i_pitch)
1278 return false;
1279 if ( i_y > i_lines)
1280 return false;
1282 int i_line_offset = i_y * i_pitch + i_x;
1284 for( int i_column = 0; i_column < i_w; i_column++ )
1286 int i_offset = i_line_offset + i_column;
1287 uint8_t i_color = p_sys->read_buffer[i_column];
1288 p_outY->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][0];
1289 p_outU->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][1];
1290 p_outV->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][2];
1291 p_outA->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][3];
1294 return true;
1298 /*****************************************************************************
1299 * MouseEvent: callback for mouse events
1300 *****************************************************************************/
1301 static int MouseEvent( filter_t *p_filter,
1302 const vlc_mouse_t *p_old,
1303 const vlc_mouse_t *p_new,
1304 const video_format_t *p_fmt )
1306 filter_sys_t *p_sys = p_filter->p_sys;
1307 VLC_UNUSED(p_old);
1309 if( !p_sys->b_vnc_mouse_events )
1310 return VLC_SUCCESS;
1312 int i_v = p_new->i_pressed;
1313 int i_x = p_new->i_x;
1314 int i_y = p_new->i_y;
1316 vlc_mutex_lock( &p_sys->lock );
1318 const int v_h = p_fmt->i_visible_height;
1319 const int v_w = p_sys->i_vnc_width * v_h / p_sys->i_vnc_height;
1320 const int v_x = (p_fmt->i_visible_width-v_w)/2;
1322 i_x -= v_x;
1324 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
1326 vlc_mutex_unlock( &p_sys->lock );
1327 msg_Dbg( p_filter, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
1328 return VLC_SUCCESS;
1330 if( !p_sys->b_connection_active )
1332 vlc_mutex_unlock( &p_sys->lock );
1333 return VLC_SUCCESS;
1336 #ifdef VNC_DEBUG
1337 msg_Dbg( p_filter, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
1338 #endif
1340 /* */
1341 i_x = i_x * p_sys->i_vnc_width / v_w;
1342 i_y = i_y * p_sys->i_vnc_height / v_h;
1344 /* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
1345 rfbPointerEventMsg ev;
1346 ev.type = rfbPointerEvent;
1347 ev.buttonMask = i_v;
1348 ev.x = htons(i_x);
1349 ev.y = htons(i_y);
1351 write_exact( p_filter, p_sys->i_socket,
1352 (char*)&ev, sz_rfbPointerEventMsg);
1354 vlc_mutex_unlock( &p_sys->lock );
1356 return VLC_EGENERIC;
1359 /*****************************************************************************
1360 * KeyEvent: callback for keyboard events
1361 *****************************************************************************/
1362 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
1363 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1365 VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
1367 filter_t *p_filter = (filter_t *)p_data;
1368 filter_sys_t *p_sys = p_filter->p_sys;
1370 if( !p_sys->b_vnc_key_events )
1371 return VLC_SUCCESS;
1373 msg_Dbg( p_this, "key pressed (%"PRId64") ", newval.i_int );
1375 if ( !newval.i_int )
1377 msg_Err( p_this, "Received invalid key event 0" );
1378 return VLC_EGENERIC;
1381 vlc_mutex_lock( &p_sys->lock );
1382 if( !p_sys->b_connection_active )
1384 vlc_mutex_unlock( &p_sys->lock );
1385 return VLC_SUCCESS;
1388 uint32_t i_key32 = newval.i_int;
1389 i_key32 = htonl(i_key32);
1390 rfbKeyEventMsg ev;
1392 ev.type = rfbKeyEvent;
1393 ev.down = 1;
1394 ev.pad = 0;
1396 /* first key-down for modifier-keys */
1397 if (newval.i_int & KEY_MODIFIER_CTRL)
1399 ev.key = 0xffe3;
1400 write_exact( p_filter, p_sys->i_socket,
1401 (char*)&ev, sz_rfbKeyEventMsg);
1403 if (newval.i_int & KEY_MODIFIER_SHIFT)
1405 ev.key = 0xffe1;
1406 write_exact( p_filter, p_sys->i_socket,
1407 (char*)&ev, sz_rfbKeyEventMsg);
1409 if (newval.i_int & KEY_MODIFIER_ALT)
1411 ev.key = 0xffe9;
1412 write_exact( p_filter, p_sys->i_socket,
1413 (char*)&ev, sz_rfbKeyEventMsg);
1416 /* then key-down for the pressed key */
1417 ev.key = i_key32;
1418 write_exact( p_filter, p_sys->i_socket,
1419 (char*)&ev, sz_rfbKeyEventMsg);
1421 ev.down = 0;
1423 /* then key-up for the pressed key */
1424 write_exact( p_filter, p_sys->i_socket,
1425 (char*)&ev, sz_rfbKeyEventMsg);
1427 /* last key-down for modifier-keys */
1428 if (newval.i_int & KEY_MODIFIER_CTRL)
1430 ev.key = 0xffe3;
1431 write_exact( p_filter, p_sys->i_socket,
1432 (char*)&ev, sz_rfbKeyEventMsg);
1434 if (newval.i_int & KEY_MODIFIER_SHIFT)
1436 ev.key = 0xffe1;
1437 write_exact( p_filter, p_sys->i_socket,
1438 (char*)&ev, sz_rfbKeyEventMsg);
1440 if (newval.i_int & KEY_MODIFIER_ALT)
1442 ev.key = 0xffe9;
1443 write_exact( p_filter, p_sys->i_socket,
1444 (char*)&ev, sz_rfbKeyEventMsg);
1446 vlc_mutex_unlock( &p_sys->lock );
1448 return VLC_SUCCESS;
1451 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
1453 unsigned char key[8];
1454 unsigned int i;
1456 for (i = 0; i < 8; i++)
1457 key[i] = i < strlen( passwd ) ? passwd[i] : '\0';
1459 gcry_cipher_hd_t ctx;
1460 gcry_cipher_open( &ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB,0);
1462 /* reverse bits of the key */
1463 for( i = 0 ; i < 8 ; i ++ )
1464 key[i] =
1465 (key[i] >> 7) +
1466 (((key[i] >> 6) & 0x01 ) << 1 ) +
1467 (((key[i] >> 5) & 0x01 ) << 2 ) +
1468 (((key[i] >> 4) & 0x01 ) << 3 ) +
1469 (((key[i] >> 3) & 0x01 ) << 4 ) +
1470 (((key[i] >> 2) & 0x01 ) << 5 ) +
1471 (((key[i] >> 1) & 0x01 ) << 6 ) +
1472 ((key[i] & 0x01) << 7 );
1474 gcry_cipher_setkey( ctx, key, 8 );
1475 gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
1476 gcry_cipher_close( ctx );