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