]> git.sesse.net Git - vlc/blob - modules/video_filter/remoteosd.c
5f2ed84a212a9948c26d24b02851ce4bd4be403e
[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
184 static inline bool raw_line(  filter_sys_t* p_sys,
185                               uint16_t i_x, uint16_t i_y,
186                               uint16_t i_w );
187
188 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
189
190
191 /*****************************************************************************
192  * Sub filter code
193  *****************************************************************************/
194
195 /*****************************************************************************
196  * Local prototypes
197  *****************************************************************************/
198 struct filter_sys_t
199 {
200     VLC_COMMON_MEMBERS
201
202     bool          b_need_update;       /* VNC picture is updated, do update the OSD*/
203     mtime_t       i_vnc_poll_interval; /* Update the OSD menu every n ms */
204
205     uint8_t       i_alpha;             /* alpha transparency value */
206
207     char          *psz_host;           /* VNC host */
208     int           i_port;
209
210     char          *psz_passwd;         /* VNC password */
211
212     bool          b_vnc_poll;          /* Activate VNC polling ? */
213     bool          b_vnc_mouse_events;  /* Send MouseEvents ? */
214     bool          b_vnc_key_events;    /* Send KeyEvents ? */
215
216     bool          b_connection_active; /* Handshaking finished ? */
217
218     vlc_mutex_t   lock;                /* To lock for read/write on picture */
219
220     picture_t     *p_pic;              /* The picture with OSD data from VNC */
221
222     vout_thread_t *p_vout;             /* Pointer to video-out thread */
223
224     int           i_socket;            /* Socket used for VNC */
225
226     uint16_t      i_vnc_width;          /* The with of the VNC screen */
227     uint16_t      i_vnc_height;         /* The height of the VNC screen */
228         uint32_t      i_vnc_pixels;         /* The pixels of the VNC screen */
229
230     bool    b_alpha_from_vnc;    /* Special ffnetdev alpha feature enabled ? */
231
232     char          read_buffer[READ_BUFFER_SIZE];
233
234     bool          b_continue;
235
236     vlc_object_t* p_worker_thread;
237
238     uint8_t       ar_color_table_yuv[256][4];
239 };
240
241 /*****************************************************************************
242  * CreateFilter: Create the filter and open the definition file
243  *****************************************************************************/
244 static int CreateFilter ( vlc_object_t *p_this )
245 {
246     filter_t *p_filter = (filter_t *)p_this;
247     filter_sys_t *p_sys = NULL;
248
249     msg_Dbg( p_filter, "Creating vnc osd filter..." );
250
251     p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
252     if( !p_filter->p_sys )
253         return VLC_ENOMEM;
254     memset( p_sys, 0, sizeof(*p_sys) );
255
256     /* Populating struct */
257     vlc_mutex_init( &p_sys->lock );
258     p_sys->b_continue = true;
259     p_sys->i_socket = -1;
260     p_sys->p_pic = NULL;
261
262     p_sys->psz_host = var_CreateGetString( p_this, RMTOSD_CFG "host" );
263     if( EMPTY_STR(p_sys->psz_host) )
264     {
265         msg_Err( p_filter, "unable to get vnc host" );
266         goto error;
267     }
268
269     p_sys->psz_passwd = var_CreateGetString( p_this, RMTOSD_CFG "password" );
270     if( !p_sys->psz_passwd )
271     {
272         msg_Err( p_filter, "unable to get vnc password" );
273         goto error;
274     }
275
276     p_sys->i_port = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "port" );
277
278     p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "alpha" );
279
280     /* in miliseconds, 0 disables polling, should not be lower than 100 */
281     p_sys->i_vnc_poll_interval  = var_CreateGetIntegerCommand( p_this,
282                                                        RMTOSD_CFG "update" );
283     if ( p_sys->i_vnc_poll_interval < 100)
284     {
285        p_sys->i_vnc_poll_interval = 100;
286     }
287
288     for ( int i = 0; i < 256; i++ )
289     {
290         p_sys->ar_color_table_yuv[i][0] = 255;
291         p_sys->ar_color_table_yuv[i][1] = 255;
292         p_sys->ar_color_table_yuv[i][2] = 255;
293         p_sys->ar_color_table_yuv[i][3] = 255;
294     }
295
296     p_sys->b_vnc_poll = var_CreateGetBoolCommand( p_this,
297                                             RMTOSD_CFG "vnc-polling" );
298     p_sys->b_vnc_mouse_events = var_CreateGetBoolCommand( p_this,
299                                             RMTOSD_CFG "mouse-events" );
300     p_sys->b_vnc_key_events = var_CreateGetBoolCommand( p_this,
301                                             RMTOSD_CFG "key-events" );
302
303     /* Keep track of OSD Events */
304     p_sys->b_need_update  = false;
305
306     /* Attach subpicture filter callback */
307     p_filter->pf_sub_filter = Filter;
308
309     p_sys->p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_PARENT );
310
311     if( p_sys->p_vout )
312     {
313         var_AddCallback( p_sys->p_vout, "mouse-moved",
314                          MouseEvent, p_this );
315         var_AddCallback( p_sys->p_vout, "mouse-button-down",
316                          MouseEvent, p_this );
317         var_AddCallback( p_sys->p_vout->p_libvlc, "key-pressed",
318                          KeyEvent, p_this );
319     }
320
321     es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
322     p_filter->fmt_out.i_priority = 0;
323
324     vlc_gcrypt_init();
325
326     /* create the vnc worker thread */
327     p_sys->p_worker_thread = vlc_object_create( p_this,
328                                                 sizeof( vlc_object_t ) );
329     vlc_object_attach( p_sys->p_worker_thread, p_this );
330     if( vlc_thread_create( p_sys->p_worker_thread, "vnc worker thread",
331                            vnc_worker_thread,
332                            VLC_THREAD_PRIORITY_LOW, false ) )
333     {
334         vlc_object_detach( p_sys->p_worker_thread );
335         vlc_object_release( p_sys->p_worker_thread );
336         msg_Err( p_filter, "cannot spawn vnc message reader thread" );
337         goto error;
338     }
339
340     msg_Dbg( p_filter, "osdvnc filter started" );
341
342     return VLC_SUCCESS;
343
344 error:
345     msg_Err( p_filter, "osdvnc filter discarded" );
346
347     stop_osdvnc( p_filter );
348
349     vlc_mutex_destroy( &p_sys->lock );
350     free( p_sys->psz_host );
351     free( p_sys->psz_passwd );
352     free( p_sys );
353
354     return VLC_EGENERIC;
355 }
356
357 /*****************************************************************************
358  * DestroyFilter: Make a clean exit of this plugin
359  *****************************************************************************/
360 static void DestroyFilter( vlc_object_t *p_this )
361 {
362     filter_t     *p_filter = (filter_t*)p_this;
363     filter_sys_t *p_sys = p_filter->p_sys;
364
365     msg_Dbg( p_filter, "DestroyFilter called." );
366
367     stop_osdvnc( p_filter );
368
369     if( p_sys->p_vout )
370     {
371         var_DelCallback( p_sys->p_vout, "mouse-moved",
372                          MouseEvent, p_this );
373         var_DelCallback( p_sys->p_vout, "mouse-button-down",
374                          MouseEvent, p_this );
375         var_DelCallback( p_sys->p_vout->p_libvlc, "key-pressed",
376                          KeyEvent, p_this );
377
378         vlc_object_release( p_sys->p_vout );
379     }
380
381     var_Destroy( p_this, RMTOSD_CFG "host" );
382     var_Destroy( p_this, RMTOSD_CFG "port" );
383     var_Destroy( p_this, RMTOSD_CFG "password" );
384     var_Destroy( p_this, RMTOSD_CFG "update" );
385     var_Destroy( p_this, RMTOSD_CFG "vnc-polling" );
386     var_Destroy( p_this, RMTOSD_CFG "mouse-events" );
387     var_Destroy( p_this, RMTOSD_CFG "key-events" );
388     var_Destroy( p_this, RMTOSD_CFG "alpha" );
389
390     vlc_mutex_destroy( &p_sys->lock );
391     free( p_sys->psz_host );
392     free( p_sys->psz_passwd );
393     free( p_sys );
394 }
395
396 static void stop_osdvnc ( filter_t *p_filter )
397 {
398     filter_sys_t *p_sys = p_filter->p_sys;
399
400     /* It will unlock socket reading */
401     vlc_object_kill( p_filter );
402
403     /* */
404     if( p_sys->p_worker_thread )
405     {
406         msg_Dbg( p_filter, "joining worker_thread" );
407         vlc_object_kill( p_sys->p_worker_thread );
408         vlc_thread_join( p_sys->p_worker_thread );
409         vlc_object_detach( p_sys->p_worker_thread );
410         vlc_object_release( p_sys->p_worker_thread );
411         msg_Dbg( p_filter, "released worker_thread" );
412     }
413
414     msg_Dbg( p_filter, "osdvnc stopped" );
415 }
416
417 static bool read_exact( filter_t *p_filter,
418                         int i_socket,
419                         char* p_readbuf,
420                         int i_bytes )
421 {
422     return i_bytes == net_Read( p_filter, i_socket, NULL,
423                                   (unsigned char*)p_readbuf,
424                                   i_bytes, true );
425 }
426
427
428 static bool write_exact( filter_t *p_filter,
429                          int i_socket,
430                          char* p_writebuf,
431                          int i_bytes )
432 {
433     return i_bytes == net_Write( p_filter, i_socket, NULL,
434                                   (unsigned char*)p_writebuf, i_bytes );
435 }
436
437 static bool open_vnc_connection ( filter_t *p_filter )
438 {
439     filter_sys_t *p_sys = p_filter->p_sys;
440
441     msg_Dbg( p_filter, "Open socket to vnc server on %s:%u.",
442               p_sys->psz_host, p_sys->i_port );
443
444     p_sys->i_socket = net_ConnectTCP( p_filter, p_sys->psz_host, p_sys->i_port );
445
446     if( p_sys->i_socket < 0 )
447     {
448         msg_Err( p_filter, "Could not open socket" );
449         return false;
450     }
451
452     msg_Dbg( p_filter, "socket is open." );
453
454     return true;
455 }
456
457 static bool handshaking ( filter_t *p_filter )
458 {
459     filter_sys_t *p_sys = p_filter->p_sys;
460
461     msg_Dbg( p_filter, "Reading protocol version" );
462
463     rfbProtocolVersionMsg pv;
464     if ( !read_exact( p_filter, p_sys->i_socket, pv,
465                       sz_rfbProtocolVersionMsg ) )
466     {
467         msg_Err( p_filter, "Could not read version message" );
468         return false;
469     }
470     pv[sz_rfbProtocolVersionMsg] = '\0'; /* pv size is sz_rfbProtocolVersionMsg+1 */
471
472     msg_Dbg( p_filter, "Server version is %s", pv );
473
474     strncpy(pv, "RFB 003.003\n", sz_rfbProtocolVersionMsg);
475
476     if( !write_exact(p_filter, p_sys->i_socket, pv,
477                      sz_rfbProtocolVersionMsg) )
478     {
479         msg_Err( p_filter, "Could not write version message" );
480         return false;
481     }
482
483     msg_Dbg( p_filter, "Reading authentication scheme" );
484     uint32_t i_authScheme;
485     if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authScheme, 4 ) )
486     {
487         msg_Err( p_filter, "Could not read authentication scheme" );
488         return false;
489     }
490     i_authScheme = htonl(i_authScheme);
491
492     msg_Dbg( p_filter, "Authentication scheme = %x", i_authScheme );
493     if ( i_authScheme == rfbConnFailed )
494     {
495         msg_Err( p_filter, "Connection rejected by server" );
496         return false;
497     }
498     if (i_authScheme == rfbVncAuth)
499     {
500         unsigned char challenge[CHALLENGESIZE];
501         if ( !read_exact( p_filter, p_sys->i_socket,
502                           (char*)challenge, CHALLENGESIZE ) )
503         {
504             msg_Err( p_filter, "Could not read password challenge" );
505             return false;
506         }
507
508         vnc_encrypt_bytes( challenge, p_sys->psz_passwd );
509
510         if( !write_exact(p_filter, p_sys->i_socket,
511                          (char*)challenge, CHALLENGESIZE ) )
512         {
513             msg_Err( p_filter, "Could not write password" );
514             return false;
515         }
516         uint32_t i_authResult;
517         if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authResult, 4 ) )
518         {
519             msg_Err( p_filter, "Could not read authentication result" );
520             return false;
521         }
522         i_authResult = htonl(i_authResult);
523         if (i_authResult != rfbVncAuthOK)
524         {
525             msg_Err( p_filter, "VNC authentication failed" );
526             return false;
527         }
528     }
529
530     msg_Dbg( p_filter, "Writing client init message" );
531     rfbClientInitMsg ci;
532     ci.shared = 1;
533     if( !write_exact( p_filter, p_sys->i_socket,
534                       (char*)&ci, sz_rfbClientInitMsg ) )
535     {
536         msg_Err( p_filter, "Could not write client init message" );
537         return false;
538     }
539
540     msg_Dbg( p_filter, "Reading server init message" );
541     rfbServerInitMsg si;
542     if( !read_exact( p_filter, p_sys->i_socket,
543                      (char*)&si, sz_rfbServerInitMsg ) )
544     {
545         msg_Err( p_filter, "Could not read server init message" );
546         return false;
547     }
548     si.framebufferWidth = htons(si.framebufferWidth);
549     si.framebufferHeight = htons(si.framebufferHeight);
550     si.format.redMax = htons(si.format.redMax);
551     si.format.greenMax = htons(si.format.greenMax);
552     si.format.blueMax = htons(si.format.blueMax);
553
554     p_sys->i_vnc_width = si.framebufferWidth;
555     p_sys->i_vnc_height = si.framebufferHeight;
556
557     msg_Dbg( p_filter, "Servers preferred pixelformat: "
558                         "%ux%u, R(%u),G(%u),B(%u), %u bit, depht=%u, %s",
559                         si.framebufferWidth,
560                         si.framebufferHeight,
561                         si.format.redMax,
562                         si.format.greenMax,
563                         si.format.blueMax,
564                         si.format.bitsPerPixel,
565                         si.format.depth,
566                         si.format.trueColour ? "TrueColor" : "Not-TrueColor");
567
568     uint32_t i_nameLength = htonl(si.nameLength);
569     if( i_nameLength > MAX_VNC_SERVER_NAME_LENGTH )
570     {
571         msg_Err( p_filter, "Server name too long" );
572         return false;
573     }
574     char s_ServerName[MAX_VNC_SERVER_NAME_LENGTH+1];
575
576     msg_Dbg( p_filter, "Reading server name with size = %u", i_nameLength );
577     if( !read_exact( p_filter, p_sys->i_socket, s_ServerName, i_nameLength ) )
578     {
579         msg_Err( p_filter, "Could not read server name" );
580         return false;
581     }
582     s_ServerName[i_nameLength] = '\0';
583
584     if( strcmp( s_ServerName, "VDR-OSD") == 0 )
585     {
586         msg_Dbg( p_filter, "Server is a VDR" );
587         p_sys->b_alpha_from_vnc = true;
588     }
589     else
590     {
591         msg_Dbg( p_filter, "Server is a normal VNC" );
592         p_sys->b_alpha_from_vnc = false;
593     }
594
595
596     msg_Dbg( p_filter, "Server init message read properly" );
597     msg_Dbg( p_filter, "Server name is %s", s_ServerName );
598
599     msg_Dbg( p_filter, "Writing SetPixelFormat message" );
600
601     rfbSetPixelFormatMsg sp;
602     sp.type = rfbSetPixelFormat;
603     sp.format.bitsPerPixel = 8;
604     sp.format.depth = 8 ;
605     sp.format.bigEndian = 1;
606     sp.format.trueColour = 0;
607     sp.format.redMax = htons(31);
608     sp.format.greenMax = htons(31);
609     sp.format.blueMax = htons(31);
610     sp.format.redShift = 10;
611     sp.format.greenShift = 5;
612     sp.format.blueShift = 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.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
626
627     if( !write_exact( p_filter, p_sys->i_socket,
628                       (char*)&se, sz_rfbSetEncodingsMsg) )
629     {
630         msg_Err( p_filter, "Could not write SetEncodings message begin" );
631         return false;
632     }
633
634     uint32_t i_encoding;
635
636     msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingCopyRect" );
637     i_encoding = htonl(rfbEncodingCopyRect);
638     if( !write_exact( p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
639     {
640         msg_Err( p_filter, "Could not write encoding type rfbEncodingCopyRect." );
641         return false;
642     }
643
644     msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingRRE" );
645     i_encoding = htonl(rfbEncodingRRE);
646     if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
647     {
648         msg_Err( p_filter, "Could not write encoding type rfbEncodingRRE." );
649         return false;
650     }
651
652     if( p_sys->b_alpha_from_vnc )
653     {
654         msg_Dbg( p_filter, "Writing SetEncodings rfbEncSpecialUseAlpha" );
655         i_encoding = 0x00F0FFFF; /* rfbEncSpecialUseAlpha is 0xFFFFF000
656                                   * before we swap it */
657         if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
658         {
659             msg_Err( p_filter, "Could not write encoding type rfbEncSpecialUseAlpha." );
660             return false;
661         }
662     }
663     return true;
664
665 }
666
667 static void vnc_worker_thread( vlc_object_t *p_thread_obj )
668 {
669     filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
670     filter_sys_t *p_sys = p_filter->p_sys;
671     vlc_object_t *p_update_request_thread;
672
673     msg_Dbg( p_filter, "VNC worker thread started" );
674
675     if( !open_vnc_connection ( p_filter ) )
676     {
677         msg_Err( p_filter, "Could not connect to vnc host" );
678         goto exit;
679     }
680
681     if( !handshaking ( p_filter ) )
682     {
683         msg_Err( p_filter, "Error occured while handshaking vnc host" );
684         goto exit;
685     }
686
687     p_sys->b_connection_active = true; /* to enable sending key
688                                             * and mouse events to host */
689
690     /* Create an empty picture for VNC the data */
691     vlc_mutex_lock( &p_sys->lock );
692     p_sys->p_pic = picture_New( VLC_FOURCC('Y','U','V','A'),
693                                 p_sys->i_vnc_width, p_sys->i_vnc_height, VOUT_ASPECT_FACTOR );
694     if( !p_sys->p_pic )
695     {
696         vlc_mutex_unlock( &p_sys->lock );
697         goto exit;
698     }
699         p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
700
701     vlc_mutex_unlock( &p_sys->lock );
702
703     /* create the update request thread */
704     p_update_request_thread = vlc_object_create( p_filter,
705                                                  sizeof( vlc_object_t ) );
706     vlc_object_attach( p_update_request_thread, p_filter );
707     if( vlc_thread_create( p_update_request_thread,
708                            "vnc update request thread",
709                            update_request_thread,
710                            VLC_THREAD_PRIORITY_LOW, false ) )
711     {
712         vlc_object_detach( p_update_request_thread );
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_detach( p_update_request_thread );
774     vlc_object_release( p_update_request_thread );
775     msg_Dbg( p_filter, "released update_request_thread" );
776
777 exit:
778
779     vlc_mutex_lock( &p_sys->lock );
780     p_sys->b_connection_active = false;
781
782     if (p_sys->i_socket >= 0)
783         net_Close(p_sys->i_socket);
784
785     if( p_sys->p_pic )
786         picture_Release( p_sys->p_pic );
787
788     /* It will hide the subtitle */
789     p_sys->b_continue = false;
790     p_sys->b_need_update = true;
791     vlc_mutex_unlock( &p_sys->lock );
792
793     msg_Dbg( p_filter, "VNC message reader thread ended" );
794 }
795
796 static void update_request_thread( vlc_object_t *p_thread_obj )
797 {
798     filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
799     filter_sys_t *p_sys = p_filter->p_sys;
800
801     msg_Dbg( p_filter, "VNC update request thread started" );
802
803     rfbFramebufferUpdateRequestMsg udr;
804     udr.type = rfbFramebufferUpdateRequest;
805     udr.incremental = 0;
806     udr.x = 0;
807     udr.y = 0;
808     udr.w = htons(p_sys->i_vnc_width);
809     udr.h = htons(p_sys->i_vnc_height);
810
811     if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
812            sz_rfbFramebufferUpdateRequestMsg) == false)
813     {
814         msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
815         p_sys->b_continue = false;
816         return;
817     }
818
819     udr.incremental = 1;
820     mtime_t i_poll_interval_microsec = p_sys->i_vnc_poll_interval * 1000;
821
822     if( p_sys->b_vnc_poll)
823     {
824         while( vlc_object_alive( p_thread_obj ) )
825         {
826             msleep( i_poll_interval_microsec );
827             if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
828                    sz_rfbFramebufferUpdateRequestMsg) == false)
829             {
830                 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
831                 break;
832             }
833         }
834         p_sys->b_continue = false;
835     }
836     else
837     {
838         msg_Dbg( p_filter, "VNC polling disabled." );
839     }
840
841     msg_Dbg( p_filter, "VNC update request thread ended" );
842 }
843
844 static bool process_server_message ( filter_t *p_filter,
845                                      rfbServerToClientMsg *msg )
846 {
847     filter_sys_t *p_sys = p_filter->p_sys;
848
849     switch (msg->type)
850     {
851     case rfbFramebufferUpdate:
852         {
853             msg->fu.nRects = htons(msg->fu.nRects);
854             rfbFramebufferUpdateRectHeader hdr;
855
856             for (int i_rect = 0; i_rect < msg->fu.nRects; i_rect++)
857             {
858                 if (!read_exact(p_filter, p_sys->i_socket, (char*)&hdr,
859                     sz_rfbFramebufferUpdateRectHeader ) )
860                 {
861                     msg_Err( p_filter, "Could not read FrameBufferUpdate header" );
862                     return false;
863                 }
864                 hdr.r.x = htons(hdr.r.x);
865                 hdr.r.y = htons(hdr.r.y);
866                 hdr.r.w = htons(hdr.r.w);
867                 hdr.r.h = htons(hdr.r.h);
868                 hdr.encoding = htonl(hdr.encoding);
869
870                 switch (hdr.encoding)
871                 {
872                 case rfbEncodingRaw:
873                     {
874                         int i_line;
875                         for (i_line = 0; i_line < hdr.r.h; i_line++)
876                         {
877                             if ( !read_exact( p_filter, p_sys->i_socket,
878                                     p_sys->read_buffer, hdr.r.w ) )
879                             {
880                                 msg_Err( p_filter,
881                                  "Could not read FrameBufferUpdate line data" );
882                                return false;
883                             }
884                             vlc_mutex_lock( &p_sys->lock );
885                             if ( !raw_line( p_sys, hdr.r.x,
886                                             hdr.r.y + i_line,
887                                             hdr.r.w ) )
888                             {
889                                 msg_Err( p_filter, "raw_line failed." );
890                                 vlc_mutex_unlock( &p_sys->lock );
891                                 return false;
892                             }
893                             vlc_mutex_unlock( &p_sys->lock );
894                         }
895                     }
896                     break;
897
898                 case rfbEncodingCopyRect:
899                     {
900                         msg_Err( p_filter,
901                           "Rect in unsupported encoding rfbEncodingCopyRect" );
902                         return false;
903                     }
904
905                 case rfbEncodingRRE:
906                     {
907                         rfbRREHeader rrehdr;
908                         if ( !read_exact( p_filter, p_sys->i_socket,
909                                           (char*)&rrehdr,
910                                           sz_rfbRREHeader ) )
911                         {
912                             msg_Err( p_filter, "Could not read rfbRREHeader" );
913                             return false;
914                         }
915                         uint8_t i_pixcolor;
916                         if ( !read_exact(p_filter, p_sys->i_socket,
917                                          (char*)&i_pixcolor, 1 ) )
918                         {
919                             msg_Err( p_filter, "Could not read RRE pixcolor" );
920                             return false;
921                         }
922
923                         vlc_mutex_lock( &p_sys->lock );
924                         if ( !fill_rect( p_sys,
925                                         hdr.r.x, hdr.r.y,
926                                         hdr.r.w, hdr.r.h,
927                                         i_pixcolor) )
928                         {
929                             msg_Err( p_filter, "main fill_rect failed." );
930                             vlc_mutex_unlock( &p_sys->lock );
931                             return false;
932                         }
933                         vlc_mutex_unlock( &p_sys->lock );
934
935                         rrehdr.nSubrects = htonl(rrehdr.nSubrects);
936
937                         int i_datasize = rrehdr.nSubrects *
938                                      ( sizeof(i_pixcolor) + sz_rfbRectangle ) ;
939                         if ( i_datasize > READ_BUFFER_SIZE )
940                         {
941                             msg_Err( p_filter, "Buffer too small, "
942                                      "need %u bytes", i_datasize );
943                             return false;
944                         }
945                         if ( !read_exact( p_filter, p_sys->i_socket,
946                                        p_sys->read_buffer, i_datasize ) )
947                         {
948                             msg_Err( p_filter,
949                                      "Could not read RRE subrect data" );
950                             return false;
951                         }
952
953                         uint32_t i_subrect;
954                         rfbRectangle* p_subrect;
955                         int i_offset = 0;
956                         vlc_mutex_lock( &p_sys->lock );
957                         for ( i_subrect = 0;
958                               i_subrect < rrehdr.nSubrects; i_subrect++)
959                         {
960                             i_pixcolor = p_sys->read_buffer[i_offset];
961                             i_offset += sizeof(i_pixcolor);
962                             p_subrect =
963                                (rfbRectangle*)(p_sys->read_buffer + i_offset);
964                             i_offset += sz_rfbRectangle;
965
966                             if (!fill_rect( p_sys,
967                                             htons(p_subrect->x) + hdr.r.x,
968                                             htons(p_subrect->y) + hdr.r.y,
969                                             htons(p_subrect->w),
970                                             htons(p_subrect->h),
971                                             i_pixcolor) )
972                             {
973                                 msg_Err( p_filter,
974                                     "subrect %u fill_rect failed.", i_subrect );
975                                 vlc_mutex_unlock( &p_sys->lock );
976                                 return false;
977                             }
978                         }
979                         vlc_mutex_unlock( &p_sys->lock );
980                     }
981                     break;
982
983                 }
984
985             }
986             vlc_mutex_lock( &p_sys->lock );
987             p_sys->b_need_update = true;
988             vlc_mutex_unlock( &p_sys->lock );
989         }
990         return true;
991
992     case rfbSetColourMapEntries:
993         {
994             msg->scme.nColours = htons(msg->scme.nColours);
995             msg->scme.firstColour = htons(msg->scme.firstColour);
996             int i_datasize;
997             if ( p_sys->b_alpha_from_vnc == true )
998             {
999                 i_datasize = 2 * msg->scme.nColours * 4;
1000             }
1001             else
1002             {
1003                 i_datasize = 2 * msg->scme.nColours * 3;
1004             }
1005             if ( i_datasize > READ_BUFFER_SIZE )
1006             {
1007                 msg_Err( p_filter, "Buffer too small, need %u bytes",
1008                                    i_datasize );
1009                 return false;
1010             }
1011
1012             if ( !read_exact( p_filter, p_sys->i_socket,
1013                               p_sys->read_buffer, i_datasize ) )
1014             {
1015                 msg_Err( p_filter, "Could not read color map data" );
1016                 return false;
1017             }
1018
1019             uint8_t i_red, i_green, i_blue, i_alpha, i_color_index;
1020             uint16_t i_offset = 0;
1021             i_alpha = 255;
1022
1023             for (int i = 0; i < msg->scme.nColours; i++)
1024             {
1025                 i_color_index = i+msg->scme.firstColour;
1026                 if ( p_sys->b_alpha_from_vnc == true )
1027                 {
1028                     i_alpha = p_sys->read_buffer[i_offset];
1029                     i_offset += 2;
1030                 }
1031                 i_red   = p_sys->read_buffer[i_offset];
1032                 i_offset += 2;
1033                 i_green = p_sys->read_buffer[i_offset];
1034                 i_offset += 2;
1035                 i_blue  = p_sys->read_buffer[i_offset];
1036                 i_offset += 2;
1037                 rgb_to_yuv( &p_sys->ar_color_table_yuv[i_color_index][0],
1038                             &p_sys->ar_color_table_yuv[i_color_index][1],
1039                             &p_sys->ar_color_table_yuv[i_color_index][2],
1040                             i_red,
1041                             i_green,
1042                             i_blue );
1043                 p_sys->ar_color_table_yuv[i][3] = i_alpha;
1044             }
1045         }
1046         return true;
1047
1048     case rfbBell:
1049         msg_Err( p_filter, "rfbBell received" );
1050         return true;
1051
1052     case rfbServerCutText:
1053         msg->sct.length = htons(msg->sct.length);
1054         if ( msg->sct.length > READ_BUFFER_SIZE )
1055         {
1056             msg_Err( p_filter, "Buffer too small, need %u bytes", msg->sct.length );
1057             return false;
1058         }
1059         if ( !read_exact(p_filter, p_sys->i_socket,
1060                          p_sys->read_buffer, msg->sct.length ) )
1061         {
1062             msg_Err( p_filter, "Could not read Reading rfbServerCutText data" );
1063             return false;
1064         }
1065         return true;
1066
1067     case rfbReSizeFrameBuffer:
1068         msg_Err( p_filter, "Reading rfbReSizeFrameBuffer not implemented" );
1069         return false;
1070
1071     default:
1072         msg_Err( p_filter, "Invalid message %u received", msg->type );
1073         return false;
1074     }
1075     return false;
1076 }
1077
1078 /****************************************************************************
1079  * Filter: the whole thing
1080  ****************************************************************************
1081  * This function outputs subpictures at regular time intervals.
1082  ****************************************************************************/
1083 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
1084 {
1085     filter_sys_t *p_sys = p_filter->p_sys;
1086     subpicture_t *p_spu;
1087     subpicture_region_t *p_region;
1088     video_format_t fmt;
1089     picture_t *p_pic;
1090
1091     if( !p_sys->b_need_update )
1092     {
1093         return NULL;
1094     }
1095
1096     vlc_mutex_lock( &p_sys->lock );
1097
1098     p_pic = p_sys->p_pic;
1099
1100     if( !p_pic )
1101     {
1102         vlc_mutex_unlock( &p_sys->lock );
1103         return NULL;
1104     }
1105
1106     /* Allocate the subpicture internal data. */
1107     p_spu = filter_NewSubpicture( p_filter );
1108     if( !p_spu )
1109     {
1110         vlc_mutex_unlock( &p_sys->lock );
1111         return NULL;
1112     }
1113
1114     p_spu->b_absolute = false;
1115     p_spu->i_start = date;
1116     p_spu->i_stop = 0;
1117     p_spu->b_ephemer = true;
1118
1119     if( !p_sys->b_continue )
1120         p_spu->i_stop = p_spu->i_start + 1;
1121
1122     /* Create new SPU region */
1123     memset( &fmt, 0, sizeof(video_format_t) );
1124     fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
1125     fmt.i_aspect = VOUT_ASPECT_FACTOR;
1126     fmt.i_sar_num = fmt.i_sar_den = 1;
1127     fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
1128     fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
1129     fmt.i_x_offset = fmt.i_y_offset = 0;
1130     p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
1131     if( !p_region )
1132     {
1133         msg_Err( p_filter, "cannot allocate SPU region" );
1134         p_filter->pf_sub_buffer_del( p_filter, p_spu );
1135         vlc_mutex_unlock( &p_sys->lock );
1136         return NULL;
1137     }
1138
1139     vout_CopyPicture( p_filter, &p_region->picture, p_pic );
1140
1141     p_sys->b_need_update = false;
1142
1143     vlc_mutex_unlock( &p_sys->lock );
1144
1145     /* set to one of the 9 relative locations */
1146     p_region->i_align = 0; //=CENTER
1147     p_spu->b_absolute = false;
1148
1149
1150     p_spu->i_x = 0;
1151     p_spu->i_y = 0;
1152
1153     p_spu->p_region = p_region;
1154
1155     p_spu->i_alpha = ( p_sys->i_alpha );
1156
1157     return p_spu;
1158 }
1159
1160
1161 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
1162                                int r, int g, int b )
1163 {
1164     *y = ( ( (  66 * r + 129 * g +  25 * b + 128 ) >> 8 ) + 16 );
1165     *u =   ( ( -38 * r -  74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
1166     *v =   ( ( 112 * r -  94 * g -  18 * b + 128 ) >> 8 ) + 128 ;
1167 }
1168
1169 static inline bool fill_rect( filter_sys_t* p_sys,
1170                               uint16_t i_x, uint16_t i_y,
1171                               uint16_t i_w, uint16_t i_h,
1172                               uint8_t i_color)
1173 {
1174     plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1175     plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1176     plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1177     plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1178     int i_pitch = p_outY->i_pitch;
1179     int i_lines = p_outY->i_lines;
1180     if ( i_x + i_w > i_pitch)
1181         return false;
1182     if ( i_y + i_h > i_lines)
1183         return false;
1184     int i_line_offset = i_y * i_pitch;
1185     uint8_t i_yuv_y = p_sys->ar_color_table_yuv[i_color][0];
1186     uint8_t i_yuv_u = p_sys->ar_color_table_yuv[i_color][1];
1187     uint8_t i_yuv_v = p_sys->ar_color_table_yuv[i_color][2];
1188     uint8_t i_alpha = p_sys->ar_color_table_yuv[i_color][3];
1189     for( int i_line = 0; i_line < i_h; i_line++ )
1190     {
1191         for( int i_column = 0; i_column < i_w; i_column++ )
1192         {
1193             int i_total_offset = i_line_offset + i_x + i_column;
1194             p_outY->p_pixels[ i_total_offset ] = i_yuv_y;
1195             p_outU->p_pixels[ i_total_offset ] = i_yuv_u;
1196             p_outV->p_pixels[ i_total_offset ] = i_yuv_v;
1197             p_outA->p_pixels[ i_total_offset ] = i_alpha;
1198         }
1199         i_line_offset += i_pitch;
1200     }
1201     return true;
1202 }
1203
1204 static inline bool raw_line(  filter_sys_t* p_sys,
1205                               uint16_t i_x, uint16_t i_y,
1206                               uint16_t i_w )
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_lines)
1217         return false;
1218
1219     int i_line_offset = i_y * i_pitch + i_x;
1220
1221     for( int i_column = 0; i_column < i_w; i_column++ )
1222     {
1223         int i_offset = i_line_offset + i_column;
1224         uint8_t i_color = p_sys->read_buffer[i_column];
1225         p_outY->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][0];
1226         p_outU->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][1];
1227         p_outV->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][2];
1228         p_outA->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][3];
1229     }
1230
1231     return true;
1232 }
1233
1234
1235 /*****************************************************************************
1236  * MouseEvent: callback for mouse events
1237  *****************************************************************************/
1238 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
1239                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1240 {
1241     VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1242
1243     filter_t *p_filter = (filter_t *)p_data;
1244     filter_sys_t *p_sys = p_filter->p_sys;
1245
1246     if( !p_sys->b_vnc_mouse_events )
1247         return VLC_SUCCESS;
1248
1249     vout_thread_t *p_vout = (vout_thread_t*)p_sys->p_vout;
1250     int i_x, i_y;
1251     int i_v;
1252
1253     int v_h = p_vout->output.i_height;
1254     int v_w = p_vout->output.i_width;
1255
1256     i_v = var_GetInteger( p_sys->p_vout, "mouse-button-down" );
1257     i_y = var_GetInteger( p_sys->p_vout, "mouse-y" );
1258     i_x = var_GetInteger( p_sys->p_vout, "mouse-x" );
1259
1260     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
1261     {
1262        msg_Dbg( p_this, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
1263        return VLC_SUCCESS;
1264     }
1265 #ifdef VNC_DEBUG
1266     msg_Dbg( p_this, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
1267 #endif
1268
1269     /* FIXME: calculate x and y coordinates for scaled output */
1270
1271     vlc_mutex_lock( &p_sys->lock );
1272     if( !p_sys->b_connection_active )
1273     {
1274         vlc_mutex_unlock( &p_sys->lock );
1275         return VLC_SUCCESS;
1276     }
1277
1278     /* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
1279     rfbPointerEventMsg ev;
1280     ev.type = rfbPointerEvent;
1281     ev.buttonMask = i_v;
1282     ev.x = htons(i_x);
1283     ev.y = htons(i_y);
1284
1285     write_exact( p_filter, p_sys->i_socket,
1286                  (char*)&ev, sz_rfbPointerEventMsg);
1287
1288     vlc_mutex_unlock( &p_sys->lock );
1289
1290     return VLC_SUCCESS;
1291 }
1292
1293 /*****************************************************************************
1294  * KeyEvent: callback for keyboard events
1295  *****************************************************************************/
1296 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
1297                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1298 {
1299     VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
1300
1301     filter_t *p_filter = (filter_t *)p_data;
1302     filter_sys_t *p_sys = p_filter->p_sys;
1303
1304     if( !p_sys->b_vnc_key_events )
1305         return VLC_SUCCESS;
1306
1307     msg_Dbg( p_this, "key pressed (%d) ", newval.i_int );
1308
1309     if ( !newval.i_int )
1310     {
1311         msg_Err( p_this, "Received invalid key event %d", newval.i_int );
1312         return VLC_EGENERIC;
1313     }
1314
1315     vlc_mutex_lock( &p_sys->lock );
1316     if( !p_sys->b_connection_active )
1317     {
1318         vlc_mutex_unlock( &p_sys->lock );
1319         return VLC_SUCCESS;
1320     }
1321
1322     uint32_t i_key32 = newval.i_int;
1323     i_key32 = htonl(i_key32);
1324     rfbKeyEventMsg ev;
1325     ev.type = rfbKeyEvent;
1326     ev.down = 1;
1327
1328     /* first key-down for modifier-keys */
1329     if (newval.i_int & KEY_MODIFIER_CTRL)
1330     {
1331         ev.key = 0xffe3;
1332         write_exact( p_filter, p_sys->i_socket,
1333                      (char*)&ev, sz_rfbKeyEventMsg);
1334     }
1335     if (newval.i_int & KEY_MODIFIER_SHIFT)
1336     {
1337         ev.key = 0xffe1;
1338         write_exact( p_filter, p_sys->i_socket,
1339                      (char*)&ev, sz_rfbKeyEventMsg);
1340     }
1341     if (newval.i_int & KEY_MODIFIER_ALT)
1342     {
1343         ev.key = 0xffe9;
1344         write_exact( p_filter, p_sys->i_socket,
1345                      (char*)&ev, sz_rfbKeyEventMsg);
1346     }
1347
1348     /* then key-down for the pressed key */
1349     ev.key = i_key32;
1350     write_exact( p_filter, p_sys->i_socket,
1351                  (char*)&ev, sz_rfbKeyEventMsg);
1352
1353     ev.down = 0;
1354
1355     /* then key-up for the pressed key */
1356     write_exact( p_filter, p_sys->i_socket,
1357                  (char*)&ev, sz_rfbKeyEventMsg);
1358
1359     /* last key-down for modifier-keys */
1360     if (newval.i_int & KEY_MODIFIER_CTRL)
1361     {
1362         ev.key = 0xffe3;
1363         write_exact( p_filter, p_sys->i_socket,
1364                      (char*)&ev, sz_rfbKeyEventMsg);
1365     }
1366     if (newval.i_int & KEY_MODIFIER_SHIFT)
1367     {
1368         ev.key = 0xffe1;
1369         write_exact( p_filter, p_sys->i_socket,
1370                      (char*)&ev, sz_rfbKeyEventMsg);
1371     }
1372     if (newval.i_int & KEY_MODIFIER_ALT)
1373     {
1374        ev.key = 0xffe9;
1375        write_exact( p_filter, p_sys->i_socket,
1376                     (char*)&ev, sz_rfbKeyEventMsg);
1377     }
1378     vlc_mutex_unlock( &p_sys->lock );
1379
1380     return VLC_SUCCESS;
1381 }
1382
1383 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
1384 {
1385     unsigned char key[8];
1386     unsigned int i;
1387
1388     for (i = 0; i < 8; i++)
1389         key[i] = i < strlen( passwd ) ? passwd[i] : '\0';
1390
1391     gcry_cipher_hd_t ctx;
1392     gcry_cipher_open( &ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB,0);
1393
1394     /* reverse bits of the key */
1395     for( i = 0 ; i < 8 ; i ++ )
1396         key[i] =
1397             (key[i] >> 7) +
1398             (((key[i] >> 6) & 0x01 ) << 1 ) +
1399             (((key[i] >> 5) & 0x01 ) << 2 ) +
1400             (((key[i] >> 4) & 0x01 ) << 3 ) +
1401             (((key[i] >> 3) & 0x01 ) << 4 ) +
1402             (((key[i] >> 2) & 0x01 ) << 5 ) +
1403             (((key[i] >> 1) & 0x01 ) << 6 ) +
1404             ((key[i] & 0x01) << 7 );
1405
1406     gcry_cipher_setkey( ctx, key, 8 );
1407     gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
1408     gcry_cipher_close( ctx );
1409 }
1410