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