]> git.sesse.net Git - vlc/blob - modules/video_filter/remoteosd.c
Qt: Reusing the titles from the simple preferences submenu as tooltip for the simple...
[vlc] / modules / video_filter / remoteosd.c
1 /*****************************************************************************
2  * remoteosd.c: remote osd over vnc filter module
3  *****************************************************************************
4  * Copyright (C) 2007-2008 Matthias Bauer
5  * $Id$
6  *
7  * Authors: Matthias Bauer <matthias dot bauer #_at_# gmx dot ch>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
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.
27  *
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.
34  *
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  *****************************************************************************/
42
43 //#define VNC_DEBUG
44
45 /*****************************************************************************
46  * Preamble
47  *****************************************************************************/
48
49 #ifdef HAVE_CONFIG_H
50 # include "config.h"
51 #endif
52
53 #include <vlc_common.h>
54 #include <vlc_plugin.h>
55
56 #include <vlc_filter.h>
57 #include <vlc_keys.h>                  /* KEY_MODIFIER_CTRL */
58
59 #include <vlc_network.h>               /* net_*, htonl */
60 #include <gcrypt.h>                    /* to encrypt password */
61 #include <vlc_gcrypt.h>
62
63 #include "remoteosd_rfbproto.h" /* type definitions of the RFB protocol for VNC */
64
65 /*****************************************************************************
66  * Module descriptor
67  *****************************************************************************/
68 #define READ_BUFFER_SIZE 1000000
69
70 #define RMTOSD_HOST_TEXT N_("VNC Host")
71 #define RMTOSD_HOST_LONGTEXT N_( \
72     "VNC hostname or IP address." )
73
74 #define RMTOSD_PORT_TEXT N_("VNC Port")
75 #define RMTOSD_PORT_LONGTEXT N_( \
76     "VNC port number." )
77
78 #define RMTOSD_PASSWORD_TEXT N_("VNC Password")
79 #define RMTOSD_PASSWORD_LONGTEXT N_( \
80     "VNC password." )
81
82 #define RMTOSD_UPDATE_TEXT N_("VNC poll interval" )
83 #define RMTOSD_UPDATE_LONGTEXT N_( \
84     "In this interval an update from VNC is requested, default every 300 ms. ")
85
86 #define RMTOSD_POLL_TEXT N_("VNC polling")
87 #define RMTOSD_POLL_LONGTEXT N_( \
88     "Activate VNC polling. Do NOT activate for use as VDR ffnetdev client." )
89
90 #define RMTOSD_MOUSE_TEXT N_("Mouse events")
91 #define RMTOSD_MOUSE_LONGTEXT N_( \
92     "Send mouse events to VNC host. Not needed for use as VDR ffnetdev client." )
93
94 #define RMTOSD_KEYS_TEXT N_("Key events")
95 #define RMTOSD_KEYS_LONGTEXT N_( \
96     "Send key events to VNC host." )
97
98 #define RMTOSD_ALPHA_TEXT N_("Alpha transparency value (default 255)")
99 #define RMTOSD_ALPHA_LONGTEXT N_( \
100     "The transparency of the OSD VNC can be changed by giving a value " \
101     "between 0 and 255. A lower value specifies more transparency a higher " \
102     "means less transparency. The default is being not transparent " \
103     "(value 255) the minimum is fully transparent (value 0)." )
104
105 #define RMTOSD_CFG "rmtosd-"
106
107 #define RMTOSD_UPDATE_MIN     200
108 #define RMTOSD_UPDATE_DEFAULT 1000
109 #define RMTOSD_UPDATE_MAX     300
110
111 static int  CreateFilter ( vlc_object_t * );
112 static void DestroyFilter( vlc_object_t * );
113
114 vlc_module_begin ()
115     set_description( N_("Remote-OSD over VNC") )
116     set_capability( "sub source", 100 )
117     set_shortname( N_("Remote-OSD") )
118     set_category( CAT_VIDEO )
119     set_subcategory( SUBCAT_VIDEO_SUBPIC )
120     add_shortcut( "rmtosd" )
121     set_callbacks( CreateFilter, DestroyFilter )
122
123     add_string( RMTOSD_CFG "host", "myvdr", RMTOSD_HOST_TEXT,
124         RMTOSD_HOST_LONGTEXT, false )
125     add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF,
126         RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false )
127     add_password( RMTOSD_CFG "password", "", RMTOSD_PASSWORD_TEXT,
128         RMTOSD_PASSWORD_LONGTEXT, false )
129     add_integer_with_range( RMTOSD_CFG "update", RMTOSD_UPDATE_DEFAULT,
130         RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, RMTOSD_UPDATE_TEXT,
131         RMTOSD_UPDATE_LONGTEXT, true )
132     add_bool( RMTOSD_CFG "vnc-polling", false,
133               RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false )
134     add_bool( RMTOSD_CFG "mouse-events", false,
135               RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false )
136     add_bool( RMTOSD_CFG "key-events", false,
137               RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false )
138     add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255,
139         RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true )
140
141 vlc_module_end ()
142
143
144 /*****************************************************************************
145  * Local prototypes
146  *****************************************************************************/
147 #define CHALLENGESIZE 16
148 #define MAX_VNC_SERVER_NAME_LENGTH 255
149
150 /* subsource functions */
151 static subpicture_t *Filter( filter_t *, mtime_t );
152
153 static int MouseEvent( filter_t *,
154                        const vlc_mouse_t *,
155                        const vlc_mouse_t *,
156                        const video_format_t * );
157
158 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
159                      vlc_value_t oldval, vlc_value_t newval, void *p_data );
160
161 static void stop_osdvnc ( filter_t *p_filter );
162
163 static void* vnc_worker_thread ( void * );
164
165 static void* update_request_thread( void * );
166
167 static bool open_vnc_connection ( filter_t *p_filter );
168
169 static bool handshaking ( filter_t *p_filter );
170
171 static bool process_server_message ( filter_t *p_filter,
172                                      rfbServerToClientMsg *msg );
173
174 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
175                                int r, int g, int b );
176
177 static inline bool fill_rect( filter_sys_t* p_sys,
178                               uint16_t i_x, uint16_t i_y,
179                               uint16_t i_w, uint16_t i_h,
180                               uint8_t i_color );
181 static inline bool copy_rect( filter_sys_t* p_sys,
182                               uint16_t i_x, uint16_t i_y,
183                               uint16_t i_w, uint16_t i_h,
184                               uint16_t i_sx, uint16_t i_sy );
185
186
187 static inline bool raw_line(  filter_sys_t* p_sys,
188                               uint16_t i_x, uint16_t i_y,
189                               uint16_t i_w );
190
191 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
192
193
194 /*****************************************************************************
195  * Sub source code
196  *****************************************************************************/
197
198 /*****************************************************************************
199  * Local prototypes
200  *****************************************************************************/
201 struct filter_sys_t
202 {
203     bool          b_need_update;       /* VNC picture is updated, do update the OSD*/
204     mtime_t       i_vnc_poll_interval; /* Update the OSD menu every n ms */
205
206     uint8_t       i_alpha;             /* alpha transparency value */
207
208     char          *psz_host;           /* VNC host */
209     int           i_port;
210
211     char          *psz_passwd;         /* VNC password */
212
213     bool          b_vnc_poll;          /* Activate VNC polling ? */
214     bool          b_vnc_mouse_events;  /* Send MouseEvents ? */
215     bool          b_vnc_key_events;    /* Send KeyEvents ? */
216
217     bool          b_connection_active; /* Handshaking finished ? */
218
219     vlc_mutex_t   lock;                /* To lock for read/write on picture */
220
221     picture_t     *p_pic;              /* The picture with OSD data from VNC */
222
223     int           i_socket;            /* Socket used for VNC */
224
225     uint16_t      i_vnc_width;          /* The with of the VNC screen */
226     uint16_t      i_vnc_height;         /* The height of the VNC screen */
227     uint32_t      i_vnc_pixels;         /* The pixels of the VNC screen */
228
229     bool    b_alpha_from_vnc;    /* Special ffnetdev alpha feature enabled ? */
230
231     char          read_buffer[READ_BUFFER_SIZE];
232
233     bool          b_continue;
234
235     vlc_thread_t  worker_thread;
236
237     uint8_t       ar_color_table_yuv[256][4];
238 };
239
240 /*****************************************************************************
241  * CreateFilter: Create the filter and open the definition file
242  *****************************************************************************/
243 static int CreateFilter ( vlc_object_t *p_this )
244 {
245     filter_t *p_filter = (filter_t *)p_this;
246     filter_sys_t *p_sys = NULL;
247
248     msg_Dbg( p_filter, "Creating vnc osd filter..." );
249
250     p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
251     if( !p_filter->p_sys )
252         return VLC_ENOMEM;
253
254     /* Populating struct */
255     vlc_mutex_init( &p_sys->lock );
256     p_sys->b_continue = true;
257     p_sys->i_socket = -1;
258     p_sys->p_pic = NULL;
259
260     p_sys->psz_host = var_CreateGetString( p_this, RMTOSD_CFG "host" );
261     if( EMPTY_STR(p_sys->psz_host) )
262     {
263         msg_Err( p_filter, "unable to get vnc host" );
264         goto error;
265     }
266
267     p_sys->psz_passwd = var_CreateGetString( p_this, RMTOSD_CFG "password" );
268     if( !p_sys->psz_passwd )
269     {
270         msg_Err( p_filter, "unable to get vnc password" );
271         goto error;
272     }
273
274     p_sys->i_port = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "port" );
275
276     p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "alpha" );
277
278     /* in milliseconds, 0 disables polling, should not be lower than 100 */
279     p_sys->i_vnc_poll_interval  = var_CreateGetIntegerCommand( p_this,
280                                                        RMTOSD_CFG "update" );
281     if ( p_sys->i_vnc_poll_interval < 100)
282     {
283        p_sys->i_vnc_poll_interval = 100;
284     }
285
286     for ( int i = 0; i < 256; i++ )
287     {
288         p_sys->ar_color_table_yuv[i][0] = 255;
289         p_sys->ar_color_table_yuv[i][1] = 255;
290         p_sys->ar_color_table_yuv[i][2] = 255;
291         p_sys->ar_color_table_yuv[i][3] = 255;
292     }
293
294     p_sys->b_vnc_poll = var_CreateGetBoolCommand( p_this,
295                                             RMTOSD_CFG "vnc-polling" );
296     p_sys->b_vnc_mouse_events = var_CreateGetBoolCommand( p_this,
297                                             RMTOSD_CFG "mouse-events" );
298     p_sys->b_vnc_key_events = var_CreateGetBoolCommand( p_this,
299                                             RMTOSD_CFG "key-events" );
300
301     /* Keep track of OSD Events */
302     p_sys->b_need_update  = false;
303
304     /* Attach subpicture source callback */
305     p_filter->pf_sub_source = Filter;
306     p_filter->pf_sub_mouse  = MouseEvent;
307
308     var_AddCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
309
310     es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_CODEC_SPU );
311     p_filter->fmt_out.i_priority = 0;
312
313     vlc_gcrypt_init();
314
315     /* create the vnc worker thread */
316     if( vlc_clone( &p_sys->worker_thread,
317                    vnc_worker_thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
318     {
319         msg_Err( p_filter, "cannot spawn vnc message reader thread" );
320         goto error;
321     }
322
323     msg_Dbg( p_filter, "osdvnc filter started" );
324
325     return VLC_SUCCESS;
326
327 error:
328     msg_Err( p_filter, "osdvnc filter discarded" );
329
330     stop_osdvnc( p_filter );
331
332     vlc_mutex_destroy( &p_sys->lock );
333     free( p_sys->psz_host );
334     free( p_sys->psz_passwd );
335     free( p_sys );
336
337     return VLC_EGENERIC;
338 }
339
340 /*****************************************************************************
341  * DestroyFilter: Make a clean exit of this plugin
342  *****************************************************************************/
343 static void DestroyFilter( vlc_object_t *p_this )
344 {
345     filter_t     *p_filter = (filter_t*)p_this;
346     filter_sys_t *p_sys = p_filter->p_sys;
347
348     msg_Dbg( p_filter, "DestroyFilter called." );
349
350     stop_osdvnc( p_filter );
351
352     var_DelCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
353
354     var_Destroy( p_this, RMTOSD_CFG "host" );
355     var_Destroy( p_this, RMTOSD_CFG "port" );
356     var_Destroy( p_this, RMTOSD_CFG "password" );
357     var_Destroy( p_this, RMTOSD_CFG "update" );
358     var_Destroy( p_this, RMTOSD_CFG "vnc-polling" );
359     var_Destroy( p_this, RMTOSD_CFG "mouse-events" );
360     var_Destroy( p_this, RMTOSD_CFG "key-events" );
361     var_Destroy( p_this, RMTOSD_CFG "alpha" );
362
363     vlc_mutex_destroy( &p_sys->lock );
364     free( p_sys->psz_host );
365     free( p_sys->psz_passwd );
366     free( p_sys );
367 }
368
369 static void stop_osdvnc ( filter_t *p_filter )
370 {
371     filter_sys_t *p_sys = p_filter->p_sys;
372
373     /* It will unlock socket reading */
374     vlc_object_kill( p_filter );
375
376     /* */
377     msg_Dbg( p_filter, "joining worker_thread" );
378     vlc_cancel( p_sys->worker_thread );
379     vlc_join( p_sys->worker_thread, NULL );
380     msg_Dbg( p_filter, "released worker_thread" );
381
382     msg_Dbg( p_filter, "osdvnc stopped" );
383 }
384
385 static bool read_exact( filter_t *p_filter,
386                         int i_socket,
387                         char* p_readbuf,
388                         int i_bytes )
389 {
390     return i_bytes == net_Read( p_filter, i_socket, NULL,
391                                   (unsigned char*)p_readbuf,
392                                   i_bytes, true );
393 }
394
395
396 static bool write_exact( filter_t *p_filter,
397                          int i_socket,
398                          char* p_writebuf,
399                          int i_bytes )
400 {
401     return i_bytes == net_Write( p_filter, i_socket, NULL,
402                                   (unsigned char*)p_writebuf, i_bytes );
403 }
404
405 static bool open_vnc_connection ( filter_t *p_filter )
406 {
407     filter_sys_t *p_sys = p_filter->p_sys;
408
409     msg_Dbg( p_filter, "Open socket to vnc server on %s:%u.",
410               p_sys->psz_host, p_sys->i_port );
411
412     p_sys->i_socket = net_ConnectTCP( p_filter, p_sys->psz_host, p_sys->i_port );
413
414     if( p_sys->i_socket < 0 )
415     {
416         msg_Err( p_filter, "Could not open socket" );
417         return false;
418     }
419
420     msg_Dbg( p_filter, "socket is open." );
421
422     return true;
423 }
424
425 static bool handshaking ( filter_t *p_filter )
426 {
427     filter_sys_t *p_sys = p_filter->p_sys;
428
429     msg_Dbg( p_filter, "Reading protocol version" );
430
431     rfbProtocolVersionMsg pv;
432     if ( !read_exact( p_filter, p_sys->i_socket, pv,
433                       sz_rfbProtocolVersionMsg ) )
434     {
435         msg_Err( p_filter, "Could not read version message" );
436         return false;
437     }
438     pv[sz_rfbProtocolVersionMsg] = '\0'; /* pv size is sz_rfbProtocolVersionMsg+1 */
439
440     msg_Dbg( p_filter, "Server version is %s", pv );
441
442     strncpy(pv, "RFB 003.003\n", sz_rfbProtocolVersionMsg);
443
444     if( !write_exact(p_filter, p_sys->i_socket, pv,
445                      sz_rfbProtocolVersionMsg) )
446     {
447         msg_Err( p_filter, "Could not write version message" );
448         return false;
449     }
450
451     msg_Dbg( p_filter, "Reading authentication scheme" );
452     uint32_t i_authScheme;
453     if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authScheme, 4 ) )
454     {
455         msg_Err( p_filter, "Could not read authentication scheme" );
456         return false;
457     }
458     i_authScheme = htonl(i_authScheme);
459
460     msg_Dbg( p_filter, "Authentication scheme = %x", i_authScheme );
461     if ( i_authScheme == rfbConnFailed )
462     {
463         msg_Err( p_filter, "Connection rejected by server" );
464         return false;
465     }
466     if (i_authScheme == rfbVncAuth)
467     {
468         unsigned char challenge[CHALLENGESIZE];
469         if ( !read_exact( p_filter, p_sys->i_socket,
470                           (char*)challenge, CHALLENGESIZE ) )
471         {
472             msg_Err( p_filter, "Could not read password challenge" );
473             return false;
474         }
475
476         vnc_encrypt_bytes( challenge, p_sys->psz_passwd );
477
478         if( !write_exact(p_filter, p_sys->i_socket,
479                          (char*)challenge, CHALLENGESIZE ) )
480         {
481             msg_Err( p_filter, "Could not write password" );
482             return false;
483         }
484         uint32_t i_authResult;
485         if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authResult, 4 ) )
486         {
487             msg_Err( p_filter, "Could not read authentication result" );
488             return false;
489         }
490         i_authResult = htonl(i_authResult);
491         if (i_authResult != rfbVncAuthOK)
492         {
493             msg_Err( p_filter, "VNC authentication failed" );
494             return false;
495         }
496     }
497
498     msg_Dbg( p_filter, "Writing client init message" );
499     rfbClientInitMsg ci;
500     ci.shared = 1;
501     if( !write_exact( p_filter, p_sys->i_socket,
502                       (char*)&ci, sz_rfbClientInitMsg ) )
503     {
504         msg_Err( p_filter, "Could not write client init message" );
505         return false;
506     }
507
508     msg_Dbg( p_filter, "Reading server init message" );
509     rfbServerInitMsg si;
510     if( !read_exact( p_filter, p_sys->i_socket,
511                      (char*)&si, sz_rfbServerInitMsg ) )
512     {
513         msg_Err( p_filter, "Could not read server init message" );
514         return false;
515     }
516     si.framebufferWidth = htons(si.framebufferWidth);
517     si.framebufferHeight = htons(si.framebufferHeight);
518     si.format.redMax = htons(si.format.redMax);
519     si.format.greenMax = htons(si.format.greenMax);
520     si.format.blueMax = htons(si.format.blueMax);
521
522     p_sys->i_vnc_width = si.framebufferWidth;
523     p_sys->i_vnc_height = si.framebufferHeight;
524
525     msg_Dbg( p_filter, "Servers preferred pixelformat: "
526                         "%ux%u, R(%u),G(%u),B(%u), %u bit, depht=%u, %s",
527                         si.framebufferWidth,
528                         si.framebufferHeight,
529                         si.format.redMax,
530                         si.format.greenMax,
531                         si.format.blueMax,
532                         si.format.bitsPerPixel,
533                         si.format.depth,
534                         si.format.trueColour ? "TrueColor" : "Not-TrueColor");
535
536     uint32_t i_nameLength = htonl(si.nameLength);
537     if( i_nameLength > MAX_VNC_SERVER_NAME_LENGTH )
538     {
539         msg_Err( p_filter, "Server name too long" );
540         return false;
541     }
542     char s_ServerName[MAX_VNC_SERVER_NAME_LENGTH+1];
543
544     msg_Dbg( p_filter, "Reading server name with size = %u", i_nameLength );
545     if( !read_exact( p_filter, p_sys->i_socket, s_ServerName, i_nameLength ) )
546     {
547         msg_Err( p_filter, "Could not read server name" );
548         return false;
549     }
550     s_ServerName[i_nameLength] = '\0';
551
552     if( strcmp( s_ServerName, "VDR-OSD") == 0 )
553     {
554         msg_Dbg( p_filter, "Server is a VDR" );
555         p_sys->b_alpha_from_vnc = true;
556     }
557     else
558     {
559         msg_Dbg( p_filter, "Server is a normal VNC" );
560         p_sys->b_alpha_from_vnc = false;
561     }
562
563
564     msg_Dbg( p_filter, "Server init message read properly" );
565     msg_Dbg( p_filter, "Server name is %s", s_ServerName );
566
567     msg_Dbg( p_filter, "Writing SetPixelFormat message" );
568
569     rfbSetPixelFormatMsg sp;
570     sp.type = rfbSetPixelFormat;
571     sp.pad1 = sp.pad2 = 0;
572     sp.format.bitsPerPixel = 8;
573     sp.format.depth = 8 ;
574     sp.format.bigEndian = 1;
575     sp.format.trueColour = 0;
576     sp.format.redMax = htons(31);
577     sp.format.greenMax = htons(31);
578     sp.format.blueMax = htons(31);
579     sp.format.redShift = 10;
580     sp.format.greenShift = 5;
581     sp.format.blueShift = 0;
582     sp.format.pad1 = sp.format.pad2 = 0;
583
584     if( !write_exact( p_filter, p_sys->i_socket,
585                       (char*)&sp, sz_rfbSetPixelFormatMsg) )
586     {
587         msg_Err( p_filter, "Could not write SetPixelFormat message" );
588         return false;
589     }
590
591     msg_Dbg( p_filter, "Writing SetEncodings message" );
592
593     rfbSetEncodingsMsg se;
594     se.type = rfbSetEncodings;
595     se.pad = 0;
596     se.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
597
598     if( !write_exact( p_filter, p_sys->i_socket,
599                       (char*)&se, sz_rfbSetEncodingsMsg) )
600     {
601         msg_Err( p_filter, "Could not write SetEncodings message begin" );
602         return false;
603     }
604
605     uint32_t i_encoding;
606
607     msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingCopyRect" );
608     i_encoding = htonl(rfbEncodingCopyRect);
609     if( !write_exact( p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
610     {
611         msg_Err( p_filter, "Could not write encoding type rfbEncodingCopyRect." );
612         return false;
613     }
614
615     msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingRRE" );
616     i_encoding = htonl(rfbEncodingRRE);
617     if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
618     {
619         msg_Err( p_filter, "Could not write encoding type rfbEncodingRRE." );
620         return false;
621     }
622
623     if( p_sys->b_alpha_from_vnc )
624     {
625         msg_Dbg( p_filter, "Writing SetEncodings rfbEncSpecialUseAlpha" );
626         i_encoding = 0x00F0FFFF; /* rfbEncSpecialUseAlpha is 0xFFFFF000
627                                   * before we swap it */
628         if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
629         {
630             msg_Err( p_filter, "Could not write encoding type rfbEncSpecialUseAlpha." );
631             return false;
632         }
633     }
634     return true;
635
636 }
637
638 static void* vnc_worker_thread( void *obj )
639 {
640     filter_t* p_filter = (filter_t*)obj;
641     filter_sys_t *p_sys = p_filter->p_sys;
642     vlc_thread_t update_request_thread_handle;
643     int canc = vlc_savecancel ();
644
645     msg_Dbg( p_filter, "VNC worker thread started" );
646
647     if( !open_vnc_connection ( p_filter ) )
648     {
649         msg_Err( p_filter, "Could not connect to vnc host" );
650         goto exit;
651     }
652
653     if( !handshaking ( p_filter ) )
654     {
655         msg_Err( p_filter, "Error occured while handshaking vnc host" );
656         goto exit;
657     }
658
659     p_sys->b_connection_active = true; /* to enable sending key
660                                             * and mouse events to host */
661
662     /* Create an empty picture for VNC the data */
663     vlc_mutex_lock( &p_sys->lock );
664     p_sys->p_pic = picture_New( VLC_CODEC_YUVA,
665                                 p_sys->i_vnc_width, p_sys->i_vnc_height, 1, 1 );
666     if( !p_sys->p_pic )
667     {
668         vlc_mutex_unlock( &p_sys->lock );
669         goto exit;
670     }
671     p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
672
673     vlc_mutex_unlock( &p_sys->lock );
674
675     /* create the update request thread */
676     if( vlc_clone( &update_request_thread_handle,
677                    update_request_thread, p_filter,
678                    VLC_THREAD_PRIORITY_LOW ) )
679     {
680         msg_Err( p_filter, "cannot spawn vnc update request thread" );
681         goto exit;
682     }
683
684     /* connection is initialized, now read and handle server messages */
685     vlc_restorecancel (canc);
686     for( ;; )
687     {
688         rfbServerToClientMsg msg;
689         int i_msgSize;
690
691         memset( &msg, 0, sizeof(msg) );
692
693         if( !read_exact(p_filter, p_sys->i_socket, (char*)&msg, 1 ) )
694         {
695             msg_Err( p_filter, "Error while waiting for next server message");
696             break;
697         }
698         switch (msg.type)
699         {
700         case rfbFramebufferUpdate:
701             i_msgSize = sz_rfbFramebufferUpdateMsg;
702             break;
703         case rfbSetColourMapEntries:
704             i_msgSize = sz_rfbSetColourMapEntriesMsg;
705             break;
706         case rfbBell:
707             i_msgSize = sz_rfbBellMsg;
708             break;
709         case rfbServerCutText:
710             i_msgSize = sz_rfbServerCutTextMsg;
711             break;
712         case rfbReSizeFrameBuffer:
713             i_msgSize = sz_rfbReSizeFrameBufferMsg;
714             break;
715         default:
716             i_msgSize = 0;
717             msg_Err( p_filter, "Invalid message %u received", msg.type );
718             break;
719         }
720
721         if( i_msgSize <= 0 )
722             break;
723
724         if( --i_msgSize > 0 )
725         {
726             if ( !read_exact( p_filter, p_sys->i_socket,
727                               ((char*)&msg)+1, i_msgSize ) )
728             {
729                 msg_Err( p_filter, "Error while reading message of type %u",
730                          msg.type );
731                 break;
732             }
733         }
734
735         canc = vlc_savecancel ();
736         process_server_message( p_filter, &msg);
737         vlc_restorecancel (canc);
738     }
739     canc = vlc_savecancel ();
740
741     msg_Dbg( p_filter, "joining update_request_thread" );
742     vlc_cancel( update_request_thread_handle );
743     vlc_join( update_request_thread_handle, NULL );
744     msg_Dbg( p_filter, "released update_request_thread" );
745
746 exit:
747
748     vlc_mutex_lock( &p_sys->lock );
749     p_sys->b_connection_active = false;
750
751     if (p_sys->i_socket >= 0)
752         net_Close(p_sys->i_socket);
753
754     if( p_sys->p_pic )
755         picture_Release( p_sys->p_pic );
756
757     /* It will hide the subtitle */
758     p_sys->b_continue = false;
759     p_sys->b_need_update = true;
760     vlc_mutex_unlock( &p_sys->lock );
761
762     msg_Dbg( p_filter, "VNC message reader thread ended" );
763     vlc_restorecancel (canc);
764     return NULL;
765 }
766
767 static void update_request_thread_cleanup( void *obj )
768 {
769     filter_t* p_filter = (filter_t*)obj;
770
771     p_filter->p_sys->b_continue = false;
772 }
773
774 static void* update_request_thread( void *obj )
775 {
776     filter_t* p_filter = (filter_t*)obj;
777     filter_sys_t *p_sys = p_filter->p_sys;
778
779     msg_Dbg( p_filter, "VNC update request thread started" );
780
781     rfbFramebufferUpdateRequestMsg udr;
782     udr.type = rfbFramebufferUpdateRequest;
783     udr.incremental = 0;
784     udr.x = 0;
785     udr.y = 0;
786     udr.w = htons(p_sys->i_vnc_width);
787     udr.h = htons(p_sys->i_vnc_height);
788
789     int w;
790     vlc_cleanup_push( update_request_thread_cleanup, p_filter );
791     w = write_exact(p_filter, p_sys->i_socket, (char*)&udr,
792                     sz_rfbFramebufferUpdateRequestMsg);
793     vlc_cleanup_pop();
794
795     if( !w )
796     {
797         msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
798         update_request_thread_cleanup( p_filter );
799         return NULL;
800     }
801
802     udr.incremental = 1;
803
804     if( p_sys->b_vnc_poll)
805     {
806         vlc_cleanup_push( update_request_thread_cleanup, p_filter );
807         for( ;; )
808         {
809             msleep( p_sys->i_vnc_poll_interval * 1000 );
810             if( !write_exact(p_filter, p_sys->i_socket, (char*)&udr,
811                              sz_rfbFramebufferUpdateRequestMsg))
812             {
813                 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
814                 break;
815             }
816         }
817         vlc_cleanup_run();
818     }
819     else
820     {
821         msg_Dbg( p_filter, "VNC polling disabled." );
822     }
823
824     msg_Dbg( p_filter, "VNC update request thread ended" );
825     return NULL;
826 }
827
828 static bool process_server_message ( filter_t *p_filter,
829                                      rfbServerToClientMsg *msg )
830 {
831     filter_sys_t *p_sys = p_filter->p_sys;
832
833     switch (msg->type)
834     {
835     case rfbFramebufferUpdate:
836         {
837             msg->fu.nRects = htons(msg->fu.nRects);
838             rfbFramebufferUpdateRectHeader hdr;
839
840             for (int i_rect = 0; i_rect < msg->fu.nRects; i_rect++)
841             {
842                 if (!read_exact(p_filter, p_sys->i_socket, (char*)&hdr,
843                     sz_rfbFramebufferUpdateRectHeader ) )
844                 {
845                     msg_Err( p_filter, "Could not read FrameBufferUpdate header" );
846                     return false;
847                 }
848                 hdr.r.x = htons(hdr.r.x);
849                 hdr.r.y = htons(hdr.r.y);
850                 hdr.r.w = htons(hdr.r.w);
851                 hdr.r.h = htons(hdr.r.h);
852                 hdr.encoding = htonl(hdr.encoding);
853
854                 switch (hdr.encoding)
855                 {
856                 case rfbEncodingRaw:
857                     {
858                         int i_line;
859                         for (i_line = 0; i_line < hdr.r.h; i_line++)
860                         {
861                             if ( !read_exact( p_filter, p_sys->i_socket,
862                                     p_sys->read_buffer, hdr.r.w ) )
863                             {
864                                 msg_Err( p_filter,
865                                  "Could not read FrameBufferUpdate line data" );
866                                return false;
867                             }
868                             vlc_mutex_lock( &p_sys->lock );
869                             if ( !raw_line( p_sys, hdr.r.x,
870                                             hdr.r.y + i_line,
871                                             hdr.r.w ) )
872                             {
873                                 msg_Err( p_filter, "raw_line failed." );
874                                 vlc_mutex_unlock( &p_sys->lock );
875                                 return false;
876                             }
877                             vlc_mutex_unlock( &p_sys->lock );
878                         }
879                     }
880                     break;
881
882                 case rfbEncodingCopyRect:
883                     {
884                         rfbCopyRect rect;
885
886                         if ( !read_exact( p_filter, p_sys->i_socket,
887                                           (char*)&rect,
888                                           sz_rfbCopyRect ) )
889                         {
890                             msg_Err( p_filter, "Could not read rfbCopyRect" );
891                             return false;
892                         }
893                         rect.srcX = htons( rect.srcX );
894                         rect.srcY = htons( rect.srcY );
895
896                         vlc_mutex_lock( &p_sys->lock );
897                         if ( !copy_rect( p_sys,
898                                          hdr.r.x,   hdr.r.y,
899                                          hdr.r.w,   hdr.r.h,
900                                          rect.srcX, rect.srcY ) )
901                         {
902                             msg_Err( p_filter, "copy_rect failed." );
903                             vlc_mutex_unlock( &p_sys->lock );
904                             return false;
905                         }
906                         vlc_mutex_unlock( &p_sys->lock );
907                     }
908                     break;
909
910                 case rfbEncodingRRE:
911                     {
912                         rfbRREHeader rrehdr;
913                         if ( !read_exact( p_filter, p_sys->i_socket,
914                                           (char*)&rrehdr,
915                                           sz_rfbRREHeader ) )
916                         {
917                             msg_Err( p_filter, "Could not read rfbRREHeader" );
918                             return false;
919                         }
920                         uint8_t i_pixcolor;
921                         if ( !read_exact(p_filter, p_sys->i_socket,
922                                          (char*)&i_pixcolor, 1 ) )
923                         {
924                             msg_Err( p_filter, "Could not read RRE pixcolor" );
925                             return false;
926                         }
927
928                         vlc_mutex_lock( &p_sys->lock );
929                         if ( !fill_rect( p_sys,
930                                         hdr.r.x, hdr.r.y,
931                                         hdr.r.w, hdr.r.h,
932                                         i_pixcolor) )
933                         {
934                             msg_Err( p_filter, "main fill_rect failed." );
935                             vlc_mutex_unlock( &p_sys->lock );
936                             return false;
937                         }
938                         vlc_mutex_unlock( &p_sys->lock );
939
940                         rrehdr.nSubrects = htonl(rrehdr.nSubrects);
941
942                         int i_datasize = rrehdr.nSubrects *
943                                      ( sizeof(i_pixcolor) + sz_rfbRectangle ) ;
944                         if ( i_datasize > READ_BUFFER_SIZE )
945                         {
946                             msg_Err( p_filter, "Buffer too small, "
947                                      "need %u bytes", i_datasize );
948                             return false;
949                         }
950                         if ( !read_exact( p_filter, p_sys->i_socket,
951                                        p_sys->read_buffer, i_datasize ) )
952                         {
953                             msg_Err( p_filter,
954                                      "Could not read RRE subrect data" );
955                             return false;
956                         }
957
958                         uint32_t i_subrect;
959                         rfbRectangle* p_subrect;
960                         int i_offset = 0;
961                         vlc_mutex_lock( &p_sys->lock );
962                         for ( i_subrect = 0;
963                               i_subrect < rrehdr.nSubrects; i_subrect++)
964                         {
965                             i_pixcolor = p_sys->read_buffer[i_offset];
966                             i_offset += sizeof(i_pixcolor);
967                             p_subrect =
968                                (rfbRectangle*)(p_sys->read_buffer + i_offset);
969                             i_offset += sz_rfbRectangle;
970
971                             if (!fill_rect( p_sys,
972                                             htons(p_subrect->x) + hdr.r.x,
973                                             htons(p_subrect->y) + hdr.r.y,
974                                             htons(p_subrect->w),
975                                             htons(p_subrect->h),
976                                             i_pixcolor) )
977                             {
978                                 msg_Err( p_filter,
979                                     "subrect %u fill_rect failed.", i_subrect );
980                                 vlc_mutex_unlock( &p_sys->lock );
981                                 return false;
982                             }
983                         }
984                         vlc_mutex_unlock( &p_sys->lock );
985                     }
986                     break;
987
988                 }
989
990             }
991             vlc_mutex_lock( &p_sys->lock );
992             p_sys->b_need_update = true;
993             vlc_mutex_unlock( &p_sys->lock );
994         }
995         return true;
996
997     case rfbSetColourMapEntries:
998         {
999             msg->scme.nColours = htons(msg->scme.nColours);
1000             msg->scme.firstColour = htons(msg->scme.firstColour);
1001             int i_datasize;
1002             if ( p_sys->b_alpha_from_vnc )
1003             {
1004                 i_datasize = 2 * msg->scme.nColours * 4;
1005             }
1006             else
1007             {
1008                 i_datasize = 2 * msg->scme.nColours * 3;
1009             }
1010             if ( i_datasize > READ_BUFFER_SIZE )
1011             {
1012                 msg_Err( p_filter, "Buffer too small, need %u bytes",
1013                                    i_datasize );
1014                 return false;
1015             }
1016
1017             if ( !read_exact( p_filter, p_sys->i_socket,
1018                               p_sys->read_buffer, i_datasize ) )
1019             {
1020                 msg_Err( p_filter, "Could not read color map data" );
1021                 return false;
1022             }
1023
1024             uint8_t i_red, i_green, i_blue, i_alpha, i_color_index;
1025             uint16_t i_offset = 0;
1026             i_alpha = 255;
1027
1028             for (int i = 0; i < msg->scme.nColours; i++)
1029             {
1030                 i_color_index = i+msg->scme.firstColour;
1031                 if ( p_sys->b_alpha_from_vnc )
1032                 {
1033                     i_alpha = p_sys->read_buffer[i_offset];
1034                     i_offset += 2;
1035                 }
1036                 i_red   = p_sys->read_buffer[i_offset];
1037                 i_offset += 2;
1038                 i_green = p_sys->read_buffer[i_offset];
1039                 i_offset += 2;
1040                 i_blue  = p_sys->read_buffer[i_offset];
1041                 i_offset += 2;
1042                 rgb_to_yuv( &p_sys->ar_color_table_yuv[i_color_index][0],
1043                             &p_sys->ar_color_table_yuv[i_color_index][1],
1044                             &p_sys->ar_color_table_yuv[i_color_index][2],
1045                             i_red,
1046                             i_green,
1047                             i_blue );
1048                 p_sys->ar_color_table_yuv[i][3] = i_alpha;
1049             }
1050         }
1051         return true;
1052
1053     case rfbBell:
1054         msg_Err( p_filter, "rfbBell received" );
1055         return true;
1056
1057     case rfbServerCutText:
1058         msg->sct.length = htons(msg->sct.length);
1059         if ( msg->sct.length > READ_BUFFER_SIZE )
1060         {
1061             msg_Err( p_filter, "Buffer too small, need %u bytes", msg->sct.length );
1062             return false;
1063         }
1064         if ( !read_exact(p_filter, p_sys->i_socket,
1065                          p_sys->read_buffer, msg->sct.length ) )
1066         {
1067             msg_Err( p_filter, "Could not read Reading rfbServerCutText data" );
1068             return false;
1069         }
1070         return true;
1071
1072     case rfbReSizeFrameBuffer:
1073         msg_Err( p_filter, "Reading rfbReSizeFrameBuffer not implemented" );
1074         return false;
1075
1076     default:
1077         msg_Err( p_filter, "Invalid message %u received", msg->type );
1078         return false;
1079     }
1080     return false;
1081 }
1082
1083 /****************************************************************************
1084  * Filter: the whole thing
1085  ****************************************************************************
1086  * This function outputs subpictures at regular time intervals.
1087  ****************************************************************************/
1088 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
1089 {
1090     filter_sys_t *p_sys = p_filter->p_sys;
1091     subpicture_t *p_spu;
1092     subpicture_region_t *p_region;
1093     video_format_t fmt;
1094     picture_t *p_pic;
1095
1096     if( !p_sys->b_need_update )
1097     {
1098         return NULL;
1099     }
1100
1101     vlc_mutex_lock( &p_sys->lock );
1102
1103     p_pic = p_sys->p_pic;
1104
1105     if( !p_pic )
1106     {
1107         vlc_mutex_unlock( &p_sys->lock );
1108         return NULL;
1109     }
1110
1111     /* Allocate the subpicture internal data. */
1112     p_spu = filter_NewSubpicture( p_filter );
1113     if( !p_spu )
1114     {
1115         vlc_mutex_unlock( &p_sys->lock );
1116         return NULL;
1117     }
1118
1119     p_spu->b_absolute = false;
1120     p_spu->i_start = date;
1121     p_spu->i_stop = 0;
1122     p_spu->b_ephemer = true;
1123
1124     if( !p_sys->b_continue )
1125         p_spu->i_stop = p_spu->i_start + 1;
1126
1127     /* Create new SPU region */
1128     memset( &fmt, 0, sizeof(video_format_t) );
1129     fmt.i_chroma = VLC_CODEC_YUVA;
1130     fmt.i_sar_num = fmt.i_sar_den = 1;
1131     fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
1132     fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
1133     fmt.i_x_offset = fmt.i_y_offset = 0;
1134     p_region = subpicture_region_New( &fmt );
1135     if( !p_region )
1136     {
1137         msg_Err( p_filter, "cannot allocate SPU region" );
1138         p_filter->pf_sub_buffer_del( p_filter, p_spu );
1139         vlc_mutex_unlock( &p_sys->lock );
1140         return NULL;
1141     }
1142
1143     /* FIXME the copy is probably not needed anymore */
1144     picture_Copy( p_region->p_picture, p_pic );
1145
1146     p_sys->b_need_update = false;
1147
1148     vlc_mutex_unlock( &p_sys->lock );
1149
1150     /* set to one of the 9 relative locations */
1151     p_region->i_align = 0; /* Center */
1152     p_spu->b_absolute = false;
1153
1154
1155     p_spu->i_original_picture_width = 0; /*Let vout core do the horizontal scaling */
1156     p_spu->i_original_picture_height = fmt.i_height;
1157
1158     p_spu->p_region = p_region;
1159
1160     p_spu->i_alpha = ( p_sys->i_alpha );
1161
1162     return p_spu;
1163 }
1164
1165
1166 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
1167                                int r, int g, int b )
1168 {
1169     *y = ( ( (  66 * r + 129 * g +  25 * b + 128 ) >> 8 ) + 16 );
1170     *u =   ( ( -38 * r -  74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
1171     *v =   ( ( 112 * r -  94 * g -  18 * b + 128 ) >> 8 ) + 128 ;
1172 }
1173
1174 static inline bool fill_rect( filter_sys_t* p_sys,
1175                               uint16_t i_x, uint16_t i_y,
1176                               uint16_t i_w, uint16_t i_h,
1177                               uint8_t i_color)
1178 {
1179     plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1180     plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1181     plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1182     plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1183     int i_pitch = p_outY->i_pitch;
1184     int i_lines = p_outY->i_lines;
1185     if ( i_x + i_w > i_pitch)
1186         return false;
1187     if ( i_y + i_h > i_lines)
1188         return false;
1189     int i_line_offset = i_y * i_pitch;
1190     uint8_t i_yuv_y = p_sys->ar_color_table_yuv[i_color][0];
1191     uint8_t i_yuv_u = p_sys->ar_color_table_yuv[i_color][1];
1192     uint8_t i_yuv_v = p_sys->ar_color_table_yuv[i_color][2];
1193     uint8_t i_alpha = p_sys->ar_color_table_yuv[i_color][3];
1194     for( int i_line = 0; i_line < i_h; i_line++ )
1195     {
1196         for( int i_column = 0; i_column < i_w; i_column++ )
1197         {
1198             int i_total_offset = i_line_offset + i_x + i_column;
1199             p_outY->p_pixels[ i_total_offset ] = i_yuv_y;
1200             p_outU->p_pixels[ i_total_offset ] = i_yuv_u;
1201             p_outV->p_pixels[ i_total_offset ] = i_yuv_v;
1202             p_outA->p_pixels[ i_total_offset ] = i_alpha;
1203         }
1204         i_line_offset += i_pitch;
1205     }
1206     return true;
1207 }
1208
1209 static inline bool copy_rect( filter_sys_t* p_sys,
1210                               uint16_t i_x, uint16_t i_y,
1211                               uint16_t i_w, uint16_t i_h,
1212                               uint16_t i_sx, uint16_t i_sy )
1213 {
1214     plane_t *p_Y = p_sys->p_pic->p+Y_PLANE;
1215     plane_t *p_U = p_sys->p_pic->p+U_PLANE;
1216     plane_t *p_V = p_sys->p_pic->p+V_PLANE;
1217     plane_t *p_A = p_sys->p_pic->p+A_PLANE;
1218
1219     int i_pitch = p_Y->i_pitch;
1220     int i_lines = p_Y->i_lines;
1221
1222     fprintf( stderr, "copy_rect: (%d,%d)+(%d,%d) -> (%d,%d)\n", i_x, i_y, i_w, i_h, i_sx, i_sy );
1223
1224     if( i_x + i_w > i_pitch || i_sx + i_w > i_pitch )
1225         return false;
1226     if( i_y + i_h > i_lines || i_sy + i_h > i_lines)
1227         return false;
1228
1229     if( i_w <= 0 || i_h <= 0 )
1230         return true;
1231
1232     uint8_t *pb_buffer = calloc( i_w * i_h, 4 );
1233     if( !pb_buffer )
1234         return false;
1235
1236     for( int i_line = 0; i_line < i_h; i_line++ )
1237     {
1238         for( int i_column = 0; i_column < i_w; i_column++ )
1239         {
1240             const int i_src_offset = ( i_sy + i_line ) * i_pitch + i_sx + i_column;
1241             const int i_tmp_offset = (    0 + i_line ) *     i_w +    0 + i_column;
1242
1243             pb_buffer[4*i_tmp_offset + 0] = p_Y->p_pixels[i_src_offset];
1244             pb_buffer[4*i_tmp_offset + 1] = p_U->p_pixels[i_src_offset];
1245             pb_buffer[4*i_tmp_offset + 2] = p_V->p_pixels[i_src_offset];
1246             pb_buffer[4*i_tmp_offset + 3] = p_A->p_pixels[i_src_offset];
1247         }
1248     }
1249
1250     for( int i_line = 0; i_line < i_h; i_line++ )
1251     {
1252         for( int i_column = 0; i_column < i_w; i_column++ )
1253         {
1254             const int i_tmp_offset = (   0 + i_line ) *     i_w +   0 + i_column;
1255             const int i_dst_offset = ( i_y + i_line ) * i_pitch + i_x + i_column;
1256
1257             p_Y->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 0];
1258             p_U->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 1];
1259             p_V->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 2];
1260             p_A->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 3];
1261         }
1262     }
1263     free( pb_buffer );
1264     return true;
1265
1266 }
1267
1268 static inline bool raw_line(  filter_sys_t* p_sys,
1269                               uint16_t i_x, uint16_t i_y,
1270                               uint16_t i_w )
1271 {
1272     plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1273     plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1274     plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1275     plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1276     int i_pitch = p_outY->i_pitch;
1277     int i_lines = p_outY->i_lines;
1278     if ( i_x + i_w > i_pitch)
1279         return false;
1280     if ( i_y > i_lines)
1281         return false;
1282
1283     int i_line_offset = i_y * i_pitch + i_x;
1284
1285     for( int i_column = 0; i_column < i_w; i_column++ )
1286     {
1287         int i_offset = i_line_offset + i_column;
1288         uint8_t i_color = p_sys->read_buffer[i_column];
1289         p_outY->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][0];
1290         p_outU->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][1];
1291         p_outV->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][2];
1292         p_outA->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][3];
1293     }
1294
1295     return true;
1296 }
1297
1298
1299 /*****************************************************************************
1300  * MouseEvent: callback for mouse events
1301  *****************************************************************************/
1302 static int MouseEvent( filter_t *p_filter,
1303                        const vlc_mouse_t *p_old,
1304                        const vlc_mouse_t *p_new,
1305                        const video_format_t *p_fmt )
1306 {
1307     filter_sys_t *p_sys = p_filter->p_sys;
1308     VLC_UNUSED(p_old);
1309
1310     if( !p_sys->b_vnc_mouse_events )
1311         return VLC_SUCCESS;
1312
1313     int i_v = p_new->i_pressed;
1314     int i_x = p_new->i_x;
1315     int i_y = p_new->i_y;
1316
1317     vlc_mutex_lock( &p_sys->lock );
1318
1319     const int v_h = p_fmt->i_visible_height;
1320     const int v_w = p_sys->i_vnc_width * v_h / p_sys->i_vnc_height;
1321     const int v_x = (p_fmt->i_visible_width-v_w)/2;
1322
1323     i_x -= v_x;
1324
1325     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
1326     {
1327         vlc_mutex_unlock( &p_sys->lock );
1328         msg_Dbg( p_filter, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
1329         return VLC_SUCCESS;
1330     }
1331     if( !p_sys->b_connection_active )
1332     {
1333         vlc_mutex_unlock( &p_sys->lock );
1334         return VLC_SUCCESS;
1335     }
1336
1337 #ifdef VNC_DEBUG
1338     msg_Dbg( p_filter, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
1339 #endif
1340
1341     /* */
1342     i_x = i_x * p_sys->i_vnc_width / v_w;
1343     i_y = i_y * p_sys->i_vnc_height / v_h;
1344
1345     /* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
1346     rfbPointerEventMsg ev;
1347     ev.type = rfbPointerEvent;
1348     ev.buttonMask = i_v;
1349     ev.x = htons(i_x);
1350     ev.y = htons(i_y);
1351
1352     write_exact( p_filter, p_sys->i_socket,
1353                  (char*)&ev, sz_rfbPointerEventMsg);
1354
1355     vlc_mutex_unlock( &p_sys->lock );
1356
1357     return VLC_EGENERIC;
1358 }
1359
1360 /*****************************************************************************
1361  * KeyEvent: callback for keyboard events
1362  *****************************************************************************/
1363 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
1364                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1365 {
1366     VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
1367
1368     filter_t *p_filter = (filter_t *)p_data;
1369     filter_sys_t *p_sys = p_filter->p_sys;
1370
1371     if( !p_sys->b_vnc_key_events )
1372         return VLC_SUCCESS;
1373
1374     msg_Dbg( p_this, "key pressed (%"PRId64") ", newval.i_int );
1375
1376     if ( !newval.i_int )
1377     {
1378         msg_Err( p_this, "Received invalid key event 0" );
1379         return VLC_EGENERIC;
1380     }
1381
1382     vlc_mutex_lock( &p_sys->lock );
1383     if( !p_sys->b_connection_active )
1384     {
1385         vlc_mutex_unlock( &p_sys->lock );
1386         return VLC_SUCCESS;
1387     }
1388
1389     uint32_t i_key32 = newval.i_int;
1390     i_key32 = htonl(i_key32);
1391     rfbKeyEventMsg ev;
1392
1393     ev.type = rfbKeyEvent;
1394     ev.down = 1;
1395     ev.pad = 0;
1396
1397     /* first key-down for modifier-keys */
1398     if (newval.i_int & KEY_MODIFIER_CTRL)
1399     {
1400         ev.key = 0xffe3;
1401         write_exact( p_filter, p_sys->i_socket,
1402                      (char*)&ev, sz_rfbKeyEventMsg);
1403     }
1404     if (newval.i_int & KEY_MODIFIER_SHIFT)
1405     {
1406         ev.key = 0xffe1;
1407         write_exact( p_filter, p_sys->i_socket,
1408                      (char*)&ev, sz_rfbKeyEventMsg);
1409     }
1410     if (newval.i_int & KEY_MODIFIER_ALT)
1411     {
1412         ev.key = 0xffe9;
1413         write_exact( p_filter, p_sys->i_socket,
1414                      (char*)&ev, sz_rfbKeyEventMsg);
1415     }
1416
1417     /* then key-down for the pressed key */
1418     ev.key = i_key32;
1419     write_exact( p_filter, p_sys->i_socket,
1420                  (char*)&ev, sz_rfbKeyEventMsg);
1421
1422     ev.down = 0;
1423
1424     /* then key-up for the pressed key */
1425     write_exact( p_filter, p_sys->i_socket,
1426                  (char*)&ev, sz_rfbKeyEventMsg);
1427
1428     /* last key-down for modifier-keys */
1429     if (newval.i_int & KEY_MODIFIER_CTRL)
1430     {
1431         ev.key = 0xffe3;
1432         write_exact( p_filter, p_sys->i_socket,
1433                      (char*)&ev, sz_rfbKeyEventMsg);
1434     }
1435     if (newval.i_int & KEY_MODIFIER_SHIFT)
1436     {
1437         ev.key = 0xffe1;
1438         write_exact( p_filter, p_sys->i_socket,
1439                      (char*)&ev, sz_rfbKeyEventMsg);
1440     }
1441     if (newval.i_int & KEY_MODIFIER_ALT)
1442     {
1443        ev.key = 0xffe9;
1444        write_exact( p_filter, p_sys->i_socket,
1445                     (char*)&ev, sz_rfbKeyEventMsg);
1446     }
1447     vlc_mutex_unlock( &p_sys->lock );
1448
1449     return VLC_SUCCESS;
1450 }
1451
1452 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
1453 {
1454     unsigned char key[8];
1455     unsigned int i;
1456
1457     for (i = 0; i < 8; i++)
1458         key[i] = i < strlen( passwd ) ? passwd[i] : '\0';
1459
1460     gcry_cipher_hd_t ctx;
1461     gcry_cipher_open( &ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB,0);
1462
1463     /* reverse bits of the key */
1464     for( i = 0 ; i < 8 ; i ++ )
1465         key[i] =
1466             (key[i] >> 7) +
1467             (((key[i] >> 6) & 0x01 ) << 1 ) +
1468             (((key[i] >> 5) & 0x01 ) << 2 ) +
1469             (((key[i] >> 4) & 0x01 ) << 3 ) +
1470             (((key[i] >> 3) & 0x01 ) << 4 ) +
1471             (((key[i] >> 2) & 0x01 ) << 5 ) +
1472             (((key[i] >> 1) & 0x01 ) << 6 ) +
1473             ((key[i] & 0x01) << 7 );
1474
1475     gcry_cipher_setkey( ctx, key, 8 );
1476     gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
1477     gcry_cipher_close( ctx );
1478 }
1479