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