]> git.sesse.net Git - vlc/blobdiff - modules/video_filter/remoteosd.c
String removal
[vlc] / modules / video_filter / remoteosd.c
index 34c86dc122ceee432df63c0b5eadd345152eadc2..09593c2a66b1a9142d50bc30670efe3efec36b14 100644 (file)
@@ -6,19 +6,19 @@
  *
  * Authors: Matthias Bauer <matthias dot bauer #_at_# gmx dot ch>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implid warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
 
 #include <vlc_common.h>
 #include <vlc_plugin.h>
-#include <vlc_vout.h>
 
-#include "vlc_filter.h"
-#include "filter_common.h"
-#include "vlc_image.h"
-#include "vlc_osd.h"
-#include "vlc_keys.h"
+#include <vlc_filter.h>
+#include <vlc_keys.h>                  /* KEY_MODIFIER_CTRL */
 
-#include <vlc_network.h>
-#include <gcrypt.h>              /* to encrypt password */
+#include <vlc_network.h>               /* net_*, htonl */
+#include <gcrypt.h>                    /* to encrypt password */
 #include <vlc_gcrypt.h>
 
-#define CHALLENGESIZE 16
-#define MAX_VNC_SERVER_NAME_LENGTH 255
-
 #include "remoteosd_rfbproto.h" /* type definitions of the RFB protocol for VNC */
 
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-/* subfilter functions */
-static int  CreateFilter ( vlc_object_t * );
-static void DestroyFilter( vlc_object_t * );
-static subpicture_t *Filter( filter_t *, mtime_t );
-
-static int MouseEvent ( vlc_object_t *p_this, char const *psz_var,
-                        vlc_value_t oldval, vlc_value_t newval, void *p_data );
-
-static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
-                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
-
-static void stop_osdvnc ( filter_t *p_filter );
-
-static void vnc_worker_thread ( vlc_object_t *p_thread_obj );
-
-static void update_request_thread( vlc_object_t *p_thread_obj );
-
-static bool open_vnc_connection ( filter_t *p_filter );
-
-static bool handshaking ( filter_t *p_filter );
-
-static bool process_server_message ( filter_t *p_filter,
-                                     rfbServerToClientMsg *msg );
-
-static bool read_exact( filter_t *p_filter,
-                        int i_socket,
-                        char* p_readbuf,
-                        int i_bytes );
-
-static bool write_exact( filter_t *p_filter,
-                         int i_socket,
-                         char* p_writebuf,
-                         int i_bytes );
-
-static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
-                               int r, int g, int b );
-
-static inline bool fill_rect( filter_sys_t* p_sys,
-                              uint16_t i_x, uint16_t i_y,
-                              uint16_t i_w, uint16_t i_h,
-                              uint8_t i_color );
-
-static inline bool raw_line(  filter_sys_t* p_sys,
-                              uint16_t i_x, uint16_t i_y,
-                              uint16_t i_w );
-
-static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
-
-
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
@@ -132,7 +73,7 @@ static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
 
 #define RMTOSD_PORT_TEXT N_("VNC Port")
 #define RMTOSD_PORT_LONGTEXT N_( \
-    "VNC portnumber." )
+    "VNC port number." )
 
 #define RMTOSD_PASSWORD_TEXT N_("VNC Password")
 #define RMTOSD_PASSWORD_LONGTEXT N_( \
@@ -167,38 +108,91 @@ static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
 #define RMTOSD_UPDATE_DEFAULT 1000
 #define RMTOSD_UPDATE_MAX     300
 
+static int  CreateFilter ( vlc_object_t * );
+static void DestroyFilter( vlc_object_t * );
 
-vlc_module_begin();
-    set_description( N_("Remote-OSD over VNC") );
-    set_capability( "sub filter", 100 );
-    set_shortname( N_("Remote-OSD") );
-    set_category( CAT_VIDEO );
-    set_subcategory( SUBCAT_VIDEO_SUBPIC );
-    add_shortcut( "rmtosd" );
-    set_callbacks( CreateFilter, DestroyFilter );
-
-    add_string( RMTOSD_CFG "host", "myvdr", NULL, RMTOSD_HOST_TEXT,
-        RMTOSD_HOST_LONGTEXT, false );
-    add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF, NULL,
-        RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false );
-    add_password( RMTOSD_CFG "password", "", NULL, RMTOSD_PASSWORD_TEXT,
-        RMTOSD_PASSWORD_LONGTEXT, false );
+vlc_module_begin ()
+    set_description( N_("Remote-OSD over VNC") )
+    set_capability( "sub source", 100 )
+    set_shortname( N_("Remote-OSD") )
+    set_category( CAT_VIDEO )
+    set_subcategory( SUBCAT_VIDEO_SUBPIC )
+    add_shortcut( "rmtosd" )
+    set_callbacks( CreateFilter, DestroyFilter )
+
+    add_string( RMTOSD_CFG "host", "myvdr", RMTOSD_HOST_TEXT,
+        RMTOSD_HOST_LONGTEXT, false )
+    add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF,
+        RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false )
+    add_password( RMTOSD_CFG "password", "", RMTOSD_PASSWORD_TEXT,
+        RMTOSD_PASSWORD_LONGTEXT, false )
     add_integer_with_range( RMTOSD_CFG "update", RMTOSD_UPDATE_DEFAULT,
-        RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, NULL, RMTOSD_UPDATE_TEXT,
-        RMTOSD_UPDATE_LONGTEXT, true );
-    add_bool( RMTOSD_CFG "vnc-polling", 0, NULL,
-              RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false );
-    add_bool( RMTOSD_CFG "mouse-events", 0, NULL,
-              RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false );
-    add_bool( RMTOSD_CFG "key-events", 0, NULL,
-              RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false );
-    add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255, NULL,
-        RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true );
-
-vlc_module_end();
+        RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, RMTOSD_UPDATE_TEXT,
+        RMTOSD_UPDATE_LONGTEXT, true )
+    add_bool( RMTOSD_CFG "vnc-polling", false,
+              RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false )
+    add_bool( RMTOSD_CFG "mouse-events", false,
+              RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false )
+    add_bool( RMTOSD_CFG "key-events", false,
+              RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false )
+    add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255,
+        RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true )
+
+vlc_module_end ()
+
 
 /*****************************************************************************
- * Sub filter code
+ * Local prototypes
+ *****************************************************************************/
+#define CHALLENGESIZE 16
+#define MAX_VNC_SERVER_NAME_LENGTH 255
+
+/* subsource functions */
+static subpicture_t *Filter( filter_t *, mtime_t );
+
+static int MouseEvent( filter_t *,
+                       const vlc_mouse_t *,
+                       const vlc_mouse_t *,
+                       const video_format_t * );
+
+static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
+                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
+
+static void stop_osdvnc ( filter_t *p_filter );
+
+static void* vnc_worker_thread ( void * );
+
+static void* update_request_thread( void * );
+
+static bool open_vnc_connection ( filter_t *p_filter );
+
+static bool handshaking ( filter_t *p_filter );
+
+static bool process_server_message ( filter_t *p_filter,
+                                     rfbServerToClientMsg *msg );
+
+static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
+                               int r, int g, int b );
+
+static inline bool fill_rect( filter_sys_t* p_sys,
+                              uint16_t i_x, uint16_t i_y,
+                              uint16_t i_w, uint16_t i_h,
+                              uint8_t i_color );
+static inline bool copy_rect( filter_sys_t* p_sys,
+                              uint16_t i_x, uint16_t i_y,
+                              uint16_t i_w, uint16_t i_h,
+                              uint16_t i_sx, uint16_t i_sy );
+
+
+static inline bool raw_line(  filter_sys_t* p_sys,
+                              uint16_t i_x, uint16_t i_y,
+                              uint16_t i_w );
+
+static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
+
+
+/*****************************************************************************
+ * Sub source code
  *****************************************************************************/
 
 /*****************************************************************************
@@ -206,8 +200,6 @@ vlc_module_end();
  *****************************************************************************/
 struct filter_sys_t
 {
-    VLC_COMMON_MEMBERS
-
     bool          b_need_update;       /* VNC picture is updated, do update the OSD*/
     mtime_t       i_vnc_poll_interval; /* Update the OSD menu every n ms */
 
@@ -228,13 +220,11 @@ struct filter_sys_t
 
     picture_t     *p_pic;              /* The picture with OSD data from VNC */
 
-    vout_thread_t *p_vout;             /* Pointer to video-out thread */
-
     int           i_socket;            /* Socket used for VNC */
 
     uint16_t      i_vnc_width;          /* The with of the VNC screen */
     uint16_t      i_vnc_height;         /* The height of the VNC screen */
-       uint32_t      i_vnc_pixels;         /* The pixels of the VNC screen */
+    uint32_t      i_vnc_pixels;         /* The pixels of the VNC screen */
 
     bool    b_alpha_from_vnc;    /* Special ffnetdev alpha feature enabled ? */
 
@@ -242,8 +232,7 @@ struct filter_sys_t
 
     bool          b_continue;
 
-    vlc_object_t* p_worker_thread;
-    vlc_object_t* p_update_request_thread;
+    vlc_thread_t  worker_thread;
 
     uint8_t       ar_color_table_yuv[256][4];
 };
@@ -258,15 +247,15 @@ static int CreateFilter ( vlc_object_t *p_this )
 
     msg_Dbg( p_filter, "Creating vnc osd filter..." );
 
-    p_filter->p_sys = p_sys = (filter_sys_t *) malloc( sizeof(filter_sys_t) );
+    p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
     if( !p_filter->p_sys )
         return VLC_ENOMEM;
-    memset( p_sys, 0, sizeof(filter_sys_t) );
 
     /* Populating struct */
     vlc_mutex_init( &p_sys->lock );
     p_sys->b_continue = true;
     p_sys->i_socket = -1;
+    p_sys->p_pic = NULL;
 
     p_sys->psz_host = var_CreateGetString( p_this, RMTOSD_CFG "host" );
     if( EMPTY_STR(p_sys->psz_host) )
@@ -286,7 +275,7 @@ static int CreateFilter ( vlc_object_t *p_this )
 
     p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "alpha" );
 
-    /* in miliseconds, 0 disables polling, should not be lower than 100 */
+    /* in milliseconds, 0 disables polling, should not be lower than 100 */
     p_sys->i_vnc_poll_interval  = var_CreateGetIntegerCommand( p_this,
                                                        RMTOSD_CFG "update" );
     if ( p_sys->i_vnc_poll_interval < 100)
@@ -312,37 +301,21 @@ static int CreateFilter ( vlc_object_t *p_this )
     /* Keep track of OSD Events */
     p_sys->b_need_update  = false;
 
-    /* Attach subpicture filter callback */
-    p_filter->pf_sub_filter = Filter;
-
-    p_sys->p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_PARENT );
+    /* Attach subpicture source callback */
+    p_filter->pf_sub_source = Filter;
+    p_filter->pf_sub_mouse  = MouseEvent;
 
-    if( p_sys->p_vout )
-    {
-        var_AddCallback( p_sys->p_vout, "mouse-moved",
-                         MouseEvent, p_this );
-        var_AddCallback( p_sys->p_vout, "mouse-button-down",
-                         MouseEvent, p_this );
-        var_AddCallback( p_sys->p_vout->p_libvlc, "key-pressed",
-                         KeyEvent, p_this );
-    }
+    var_AddCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
 
-    es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
+    es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_CODEC_SPU );
     p_filter->fmt_out.i_priority = 0;
 
     vlc_gcrypt_init();
 
     /* create the vnc worker thread */
-    p_sys->p_worker_thread = vlc_object_create( p_this,
-                                                sizeof( vlc_object_t ) );
-    vlc_object_attach( p_sys->p_worker_thread, p_this );
-    if( vlc_thread_create( p_sys->p_worker_thread, "vnc worker thread",
-                           vnc_worker_thread,
-                           VLC_THREAD_PRIORITY_LOW, false ) )
+    if( vlc_clone( &p_sys->worker_thread,
+                   vnc_worker_thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
     {
-        vlc_object_detach( p_sys->p_worker_thread );
-        vlc_object_release( p_sys->p_worker_thread );
-        p_sys->p_worker_thread = NULL;
         msg_Err( p_filter, "cannot spawn vnc message reader thread" );
         goto error;
     }
@@ -354,10 +327,9 @@ static int CreateFilter ( vlc_object_t *p_this )
 error:
     msg_Err( p_filter, "osdvnc filter discarded" );
 
-    p_sys->b_continue = false;
-
     stop_osdvnc( p_filter );
 
+    vlc_mutex_destroy( &p_sys->lock );
     free( p_sys->psz_host );
     free( p_sys->psz_passwd );
     free( p_sys );
@@ -377,18 +349,7 @@ static void DestroyFilter( vlc_object_t *p_this )
 
     stop_osdvnc( p_filter );
 
-    if( p_sys->p_vout )
-    {
-        var_DelCallback( p_sys->p_vout, "mouse-moved",
-                         MouseEvent, p_this );
-        var_DelCallback( p_sys->p_vout, "mouse-button-down",
-                         MouseEvent, p_this );
-        var_DelCallback( p_sys->p_vout->p_libvlc, "key-pressed",
-                         KeyEvent, p_this );
-
-        vlc_object_release( p_sys->p_vout );
-        p_sys->p_vout = NULL;
-    }
+    var_DelCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
 
     var_Destroy( p_this, RMTOSD_CFG "host" );
     var_Destroy( p_this, RMTOSD_CFG "port" );
@@ -399,6 +360,7 @@ static void DestroyFilter( vlc_object_t *p_this )
     var_Destroy( p_this, RMTOSD_CFG "key-events" );
     var_Destroy( p_this, RMTOSD_CFG "alpha" );
 
+    vlc_mutex_destroy( &p_sys->lock );
     free( p_sys->psz_host );
     free( p_sys->psz_passwd );
     free( p_sys );
@@ -408,29 +370,10 @@ static void stop_osdvnc ( filter_t *p_filter )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if (p_sys->i_socket >= 0)
-    {
-        net_Close(p_sys->i_socket);
-    }
-    p_sys->b_continue = false; /* this causes the threads to stop */
-
-    if ( p_sys->p_worker_thread )
-    {
-        msg_Dbg( p_filter, "joining worker_thread" );
-        vlc_thread_join( p_sys->p_worker_thread );
-        vlc_object_detach( p_sys->p_worker_thread );
-        vlc_object_release( p_sys->p_worker_thread );
-        msg_Dbg( p_filter, "released worker_thread" );
-    }
-
-    if ( p_sys->p_update_request_thread )
-    {
-        msg_Dbg( p_filter, "joining update_request_thread" );
-        vlc_thread_join( p_sys->p_update_request_thread );
-        vlc_object_detach( p_sys->p_update_request_thread );
-        vlc_object_release( p_sys->p_update_request_thread );
-        msg_Dbg( p_filter, "released update_request_thread" );
-    }
+    msg_Dbg( p_filter, "joining worker_thread" );
+    vlc_cancel( p_sys->worker_thread );
+    vlc_join( p_sys->worker_thread, NULL );
+    msg_Dbg( p_filter, "released worker_thread" );
 
     msg_Dbg( p_filter, "osdvnc stopped" );
 }
@@ -621,6 +564,7 @@ static bool handshaking ( filter_t *p_filter )
 
     rfbSetPixelFormatMsg sp;
     sp.type = rfbSetPixelFormat;
+    sp.pad1 = sp.pad2 = 0;
     sp.format.bitsPerPixel = 8;
     sp.format.depth = 8 ;
     sp.format.bigEndian = 1;
@@ -631,6 +575,7 @@ static bool handshaking ( filter_t *p_filter )
     sp.format.redShift = 10;
     sp.format.greenShift = 5;
     sp.format.blueShift = 0;
+    sp.format.pad1 = sp.format.pad2 = 0;
 
     if( !write_exact( p_filter, p_sys->i_socket,
                       (char*)&sp, sz_rfbSetPixelFormatMsg) )
@@ -643,6 +588,7 @@ static bool handshaking ( filter_t *p_filter )
 
     rfbSetEncodingsMsg se;
     se.type = rfbSetEncodings;
+    se.pad = 0;
     se.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
 
     if( !write_exact( p_filter, p_sys->i_socket,
@@ -685,23 +631,25 @@ static bool handshaking ( filter_t *p_filter )
 
 }
 
-static void vnc_worker_thread( vlc_object_t *p_thread_obj )
+static void* vnc_worker_thread( void *obj )
 {
-    filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
+    filter_t* p_filter = (filter_t*)obj;
     filter_sys_t *p_sys = p_filter->p_sys;
+    vlc_thread_t update_request_thread_handle;
+    int canc = vlc_savecancel ();
 
     msg_Dbg( p_filter, "VNC worker thread started" );
 
-    if ( open_vnc_connection ( p_filter ) == false )
+    if( !open_vnc_connection ( p_filter ) )
     {
         msg_Err( p_filter, "Could not connect to vnc host" );
-        return;
+        goto exit;
     }
 
-    if ( handshaking ( p_filter ) == false )
+    if( !handshaking ( p_filter ) )
     {
-        msg_Err( p_filter, "Error occured while handshaking vnc host" );
-        return;
+        msg_Err( p_filter, "Error occurred while handshaking vnc host" );
+        goto exit;
     }
 
     p_sys->b_connection_active = true; /* to enable sending key
@@ -709,121 +657,119 @@ static void vnc_worker_thread( vlc_object_t *p_thread_obj )
 
     /* Create an empty picture for VNC the data */
     vlc_mutex_lock( &p_sys->lock );
-    p_sys->p_pic = malloc( sizeof(picture_t) );
+    p_sys->p_pic = picture_New( VLC_CODEC_YUVA,
+                                p_sys->i_vnc_width, p_sys->i_vnc_height, 1, 1 );
     if( !p_sys->p_pic )
     {
         vlc_mutex_unlock( &p_sys->lock );
-        return;
-    }
-    vout_AllocatePicture( VLC_OBJECT(p_filter), p_sys->p_pic,
-                          VLC_FOURCC('Y','U','V','A'),
-                          p_sys->i_vnc_width,
-                          p_sys->i_vnc_height,
-                          VOUT_ASPECT_FACTOR );
-    if( !p_sys->p_pic->i_planes )
-    {
-        free( p_sys->p_pic );
-        p_sys->p_pic = NULL;
-        vlc_mutex_unlock( &p_sys->lock );
-        return;
+        goto exit;
     }
-       p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
+    p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
 
     vlc_mutex_unlock( &p_sys->lock );
 
     /* create the update request thread */
-    p_sys->p_update_request_thread = vlc_object_create( p_filter,
-                                                     sizeof( vlc_object_t ) );
-    vlc_object_attach( p_sys->p_update_request_thread, p_filter );
-    if( vlc_thread_create( p_sys->p_update_request_thread,
-                           "vnc update request thread",
-                           update_request_thread,
-                           VLC_THREAD_PRIORITY_LOW, false ) )
+    if( vlc_clone( &update_request_thread_handle,
+                   update_request_thread, p_filter,
+                   VLC_THREAD_PRIORITY_LOW ) )
     {
-        vlc_object_detach( p_sys->p_update_request_thread );
-        vlc_object_release( p_sys->p_update_request_thread );
-        p_sys->p_update_request_thread = NULL;
         msg_Err( p_filter, "cannot spawn vnc update request thread" );
-        return;
+        goto exit;
     }
 
     /* connection is initialized, now read and handle server messages */
+    vlc_restorecancel (canc);
+    for( ;; )
+    {
+        rfbServerToClientMsg msg;
+        int i_msgSize;
 
-    int i_msgSize;
+        memset( &msg, 0, sizeof(msg) );
 
-    rfbServerToClientMsg msg;
+        if( !read_exact(p_filter, p_sys->i_socket, (char*)&msg, 1 ) )
+        {
+            msg_Err( p_filter, "Error while waiting for next server message");
+            break;
+        }
+        switch (msg.type)
+        {
+        case rfbFramebufferUpdate:
+            i_msgSize = sz_rfbFramebufferUpdateMsg;
+            break;
+        case rfbSetColourMapEntries:
+            i_msgSize = sz_rfbSetColourMapEntriesMsg;
+            break;
+        case rfbBell:
+            i_msgSize = sz_rfbBellMsg;
+            break;
+        case rfbServerCutText:
+            i_msgSize = sz_rfbServerCutTextMsg;
+            break;
+        case rfbReSizeFrameBuffer:
+            i_msgSize = sz_rfbReSizeFrameBufferMsg;
+            break;
+        default:
+            i_msgSize = 0;
+            msg_Err( p_filter, "Invalid message %u received", msg.type );
+            break;
+        }
 
-    while (p_sys->b_continue)
-    {
-       msg.type = 0;
-       if ( !read_exact(p_filter, p_sys->i_socket, (char*)&msg, 1 ) )
-       {
-           msg_Err( p_filter, "Error while waiting for next server message");
-           p_sys->b_continue = false;
-       }
-       else
-       {
-           switch (msg.type)
-           {
-              case rfbFramebufferUpdate:
-                i_msgSize = sz_rfbFramebufferUpdateMsg;
-                break;
-              case rfbSetColourMapEntries:
-                i_msgSize = sz_rfbSetColourMapEntriesMsg;
-                break;
-              case rfbBell:
-                i_msgSize = sz_rfbBellMsg;
-                break;
-              case rfbServerCutText:
-                i_msgSize = sz_rfbServerCutTextMsg;
-                break;
-              case rfbReSizeFrameBuffer:
-                i_msgSize = sz_rfbReSizeFrameBufferMsg;
+        if( i_msgSize <= 0 )
+            break;
+
+        if( --i_msgSize > 0 )
+        {
+            if ( !read_exact( p_filter, p_sys->i_socket,
+                              ((char*)&msg)+1, i_msgSize ) )
+            {
+                msg_Err( p_filter, "Error while reading message of type %u",
+                         msg.type );
                 break;
-              default:
-                i_msgSize = 0;
-                msg_Err( p_filter, "Invalid message %u received", msg.type );
-                p_sys->b_continue = false;
-           }
-           if (p_sys->b_continue == true)
-           {
-               if (--i_msgSize > 0)
-               {
-                   if ( !read_exact( p_filter, p_sys->i_socket,
-                                     ((char*)&msg)+1, i_msgSize ) )
-                   {
-                       msg_Err( p_filter, "Error while reading message of type %u",
-                                msg.type );
-                       p_sys->b_continue = false;
-                   }
-                   else
-                   {
-                       process_server_message( p_filter, &msg);
-                   }
-               }
-               else
-               {
-                   process_server_message( p_filter, &msg);
-               }
-           }
-
-       }
-
-       if (!vlc_object_alive (p_sys->p_worker_thread) ||
-           p_sys->p_worker_thread->b_error)
-       {
-           p_sys->b_continue = false;
-       }
+            }
+        }
 
+        canc = vlc_savecancel ();
+        process_server_message( p_filter, &msg);
+        vlc_restorecancel (canc);
     }
+    canc = vlc_savecancel ();
+
+    msg_Dbg( p_filter, "joining update_request_thread" );
+    vlc_cancel( update_request_thread_handle );
+    vlc_join( update_request_thread_handle, NULL );
+    msg_Dbg( p_filter, "released update_request_thread" );
+
+exit:
+
+    vlc_mutex_lock( &p_sys->lock );
+    p_sys->b_connection_active = false;
+
+    if (p_sys->i_socket >= 0)
+        net_Close(p_sys->i_socket);
+
+    if( p_sys->p_pic )
+        picture_Release( p_sys->p_pic );
+
+    /* It will hide the subtitle */
+    p_sys->b_continue = false;
+    p_sys->b_need_update = true;
+    vlc_mutex_unlock( &p_sys->lock );
 
     msg_Dbg( p_filter, "VNC message reader thread ended" );
+    vlc_restorecancel (canc);
+    return NULL;
+}
 
+static void update_request_thread_cleanup( void *obj )
+{
+    filter_t* p_filter = (filter_t*)obj;
+
+    p_filter->p_sys->b_continue = false;
 }
 
-static void update_request_thread( vlc_object_t *p_thread_obj )
+static void* update_request_thread( void *obj )
 {
-    filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
+    filter_t* p_filter = (filter_t*)obj;
     filter_sys_t *p_sys = p_filter->p_sys;
 
     msg_Dbg( p_filter, "VNC update request thread started" );
@@ -836,39 +782,43 @@ static void update_request_thread( vlc_object_t *p_thread_obj )
     udr.w = htons(p_sys->i_vnc_width);
     udr.h = htons(p_sys->i_vnc_height);
 
-    if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
-           sz_rfbFramebufferUpdateRequestMsg) == false)
+    int w;
+    vlc_cleanup_push( update_request_thread_cleanup, p_filter );
+    w = write_exact(p_filter, p_sys->i_socket, (char*)&udr,
+                    sz_rfbFramebufferUpdateRequestMsg);
+    vlc_cleanup_pop();
+
+    if( !w )
     {
         msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
-        p_sys->b_continue = false;
+        update_request_thread_cleanup( p_filter );
+        return NULL;
     }
 
     udr.incremental = 1;
-    mtime_t i_poll_interval_microsec = p_sys->i_vnc_poll_interval * 1000;
 
-    if (p_sys->b_vnc_poll)
+    ifp_sys->b_vnc_poll)
     {
-        while ( p_sys->b_continue == true )
+        vlc_cleanup_push( update_request_thread_cleanup, p_filter );
+        for( ;; )
         {
-            msleep( i_poll_interval_microsec );
-            if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
-                   sz_rfbFramebufferUpdateRequestMsg) == false)
+            msleep( p_sys->i_vnc_poll_interval * 1000 );
+            if( !write_exact(p_filter, p_sys->i_socket, (char*)&udr,
+                             sz_rfbFramebufferUpdateRequestMsg))
             {
                 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
-                p_sys->b_continue = false;
-            }
-            if (!vlc_object_alive (p_sys->p_update_request_thread) ||
-                p_sys->p_update_request_thread->b_error)
-            {
-                p_sys->b_continue = false;
+                break;
             }
         }
+        vlc_cleanup_run();
     }
     else
     {
         msg_Dbg( p_filter, "VNC polling disabled." );
     }
+
     msg_Dbg( p_filter, "VNC update request thread ended" );
+    return NULL;
 }
 
 static bool process_server_message ( filter_t *p_filter,
@@ -927,10 +877,31 @@ static bool process_server_message ( filter_t *p_filter,
 
                 case rfbEncodingCopyRect:
                     {
-                        msg_Err( p_filter,
-                          "Rect in unsupported encoding rfbEncodingCopyRect" );
-                        return false;
+                        rfbCopyRect rect;
+
+                        if ( !read_exact( p_filter, p_sys->i_socket,
+                                          (char*)&rect,
+                                          sz_rfbCopyRect ) )
+                        {
+                            msg_Err( p_filter, "Could not read rfbCopyRect" );
+                            return false;
+                        }
+                        rect.srcX = htons( rect.srcX );
+                        rect.srcY = htons( rect.srcY );
+
+                        vlc_mutex_lock( &p_sys->lock );
+                        if ( !copy_rect( p_sys,
+                                         hdr.r.x,   hdr.r.y,
+                                         hdr.r.w,   hdr.r.h,
+                                         rect.srcX, rect.srcY ) )
+                        {
+                            msg_Err( p_filter, "copy_rect failed." );
+                            vlc_mutex_unlock( &p_sys->lock );
+                            return false;
+                        }
+                        vlc_mutex_unlock( &p_sys->lock );
                     }
+                    break;
 
                 case rfbEncodingRRE:
                     {
@@ -1024,7 +995,7 @@ static bool process_server_message ( filter_t *p_filter,
             msg->scme.nColours = htons(msg->scme.nColours);
             msg->scme.firstColour = htons(msg->scme.firstColour);
             int i_datasize;
-            if ( p_sys->b_alpha_from_vnc == true )
+            if ( p_sys->b_alpha_from_vnc )
             {
                 i_datasize = 2 * msg->scme.nColours * 4;
             }
@@ -1053,7 +1024,7 @@ static bool process_server_message ( filter_t *p_filter,
             for (int i = 0; i < msg->scme.nColours; i++)
             {
                 i_color_index = i+msg->scme.firstColour;
-                if ( p_sys->b_alpha_from_vnc == true )
+                if ( p_sys->b_alpha_from_vnc )
                 {
                     i_alpha = p_sys->read_buffer[i_offset];
                     i_offset += 2;
@@ -1146,15 +1117,17 @@ static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
     p_spu->i_stop = 0;
     p_spu->b_ephemer = true;
 
+    if( !p_sys->b_continue )
+        p_spu->i_stop = p_spu->i_start + 1;
+
     /* Create new SPU region */
     memset( &fmt, 0, sizeof(video_format_t) );
-    fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
-    fmt.i_aspect = VOUT_ASPECT_FACTOR;
+    fmt.i_chroma = VLC_CODEC_YUVA;
     fmt.i_sar_num = fmt.i_sar_den = 1;
     fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
     fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
     fmt.i_x_offset = fmt.i_y_offset = 0;
-    p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
+    p_region = subpicture_region_New( &fmt );
     if( !p_region )
     {
         msg_Err( p_filter, "cannot allocate SPU region" );
@@ -1163,19 +1136,20 @@ static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
         return NULL;
     }
 
-    vout_CopyPicture( p_filter, &p_region->picture, p_pic );
+    /* FIXME the copy is probably not needed anymore */
+    picture_Copy( p_region->p_picture, p_pic );
 
     p_sys->b_need_update = false;
 
     vlc_mutex_unlock( &p_sys->lock );
 
     /* set to one of the 9 relative locations */
-    p_region->i_align = 0; //=CENTER
+    p_region->i_align = 0; /* Center */
     p_spu->b_absolute = false;
 
 
-    p_spu->i_x = 0;
-    p_spu->i_y = 0;
+    p_spu->i_original_picture_width = 0; /*Let vout core do the horizontal scaling */
+    p_spu->i_original_picture_height = fmt.i_height;
 
     p_spu->p_region = p_region;
 
@@ -1228,6 +1202,65 @@ static inline bool fill_rect( filter_sys_t* p_sys,
     return true;
 }
 
+static inline bool copy_rect( filter_sys_t* p_sys,
+                              uint16_t i_x, uint16_t i_y,
+                              uint16_t i_w, uint16_t i_h,
+                              uint16_t i_sx, uint16_t i_sy )
+{
+    plane_t *p_Y = p_sys->p_pic->p+Y_PLANE;
+    plane_t *p_U = p_sys->p_pic->p+U_PLANE;
+    plane_t *p_V = p_sys->p_pic->p+V_PLANE;
+    plane_t *p_A = p_sys->p_pic->p+A_PLANE;
+
+    int i_pitch = p_Y->i_pitch;
+    int i_lines = p_Y->i_lines;
+
+    fprintf( stderr, "copy_rect: (%d,%d)+(%d,%d) -> (%d,%d)\n", i_x, i_y, i_w, i_h, i_sx, i_sy );
+
+    if( i_x + i_w > i_pitch || i_sx + i_w > i_pitch )
+        return false;
+    if( i_y + i_h > i_lines || i_sy + i_h > i_lines)
+        return false;
+
+    if( i_w <= 0 || i_h <= 0 )
+        return true;
+
+    uint8_t *pb_buffer = calloc( i_w * i_h, 4 );
+    if( !pb_buffer )
+        return false;
+
+    for( int i_line = 0; i_line < i_h; i_line++ )
+    {
+        for( int i_column = 0; i_column < i_w; i_column++ )
+        {
+            const int i_src_offset = ( i_sy + i_line ) * i_pitch + i_sx + i_column;
+            const int i_tmp_offset = (    0 + i_line ) *     i_w +    0 + i_column;
+
+            pb_buffer[4*i_tmp_offset + 0] = p_Y->p_pixels[i_src_offset];
+            pb_buffer[4*i_tmp_offset + 1] = p_U->p_pixels[i_src_offset];
+            pb_buffer[4*i_tmp_offset + 2] = p_V->p_pixels[i_src_offset];
+            pb_buffer[4*i_tmp_offset + 3] = p_A->p_pixels[i_src_offset];
+        }
+    }
+
+    for( int i_line = 0; i_line < i_h; i_line++ )
+    {
+        for( int i_column = 0; i_column < i_w; i_column++ )
+        {
+            const int i_tmp_offset = (   0 + i_line ) *     i_w +   0 + i_column;
+            const int i_dst_offset = ( i_y + i_line ) * i_pitch + i_x + i_column;
+
+            p_Y->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 0];
+            p_U->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 1];
+            p_V->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 2];
+            p_A->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 3];
+        }
+    }
+    free( pb_buffer );
+    return true;
+
+}
+
 static inline bool raw_line(  filter_sys_t* p_sys,
                               uint16_t i_x, uint16_t i_y,
                               uint16_t i_w )
@@ -1262,41 +1295,48 @@ static inline bool raw_line(  filter_sys_t* p_sys,
 /*****************************************************************************
  * MouseEvent: callback for mouse events
  *****************************************************************************/
-static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
-                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
+static int MouseEvent( filter_t *p_filter,
+                       const vlc_mouse_t *p_old,
+                       const vlc_mouse_t *p_new,
+                       const video_format_t *p_fmt )
 {
-    VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
-
-    filter_t *p_filter = (filter_t *)p_data;
     filter_sys_t *p_sys = p_filter->p_sys;
-
-    if( !p_sys->b_connection_active )
-        return VLC_SUCCESS;
+    VLC_UNUSED(p_old);
 
     if( !p_sys->b_vnc_mouse_events )
         return VLC_SUCCESS;
 
-    vout_thread_t *p_vout = (vout_thread_t*)p_sys->p_vout;
-    int i_x, i_y;
-    int i_v;
+    int i_v = p_new->i_pressed;
+    int i_x = p_new->i_x;
+    int i_y = p_new->i_y;
+
+    vlc_mutex_lock( &p_sys->lock );
 
-    int v_h = p_vout->output.i_height;
-    int v_w = p_vout->output.i_width;
+    const int v_h = p_fmt->i_visible_height;
+    const int v_w = p_sys->i_vnc_width * v_h / p_sys->i_vnc_height;
+    const int v_x = (p_fmt->i_visible_width-v_w)/2;
 
-    i_v = var_GetInteger( p_sys->p_vout, "mouse-button-down" );
-    i_y = var_GetInteger( p_sys->p_vout, "mouse-y" );
-    i_x = var_GetInteger( p_sys->p_vout, "mouse-x" );
+    i_x -= v_x;
 
     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
     {
-       msg_Dbg( p_this, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
-       return VLC_SUCCESS;
+        vlc_mutex_unlock( &p_sys->lock );
+        msg_Dbg( p_filter, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
+        return VLC_SUCCESS;
+    }
+    if( !p_sys->b_connection_active )
+    {
+        vlc_mutex_unlock( &p_sys->lock );
+        return VLC_SUCCESS;
     }
+
 #ifdef VNC_DEBUG
-    msg_Dbg( p_this, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
+    msg_Dbg( p_filter, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
 #endif
 
-    /* FIXME: calculate x and y coordinates for scaled output */
+    /* */
+    i_x = i_x * p_sys->i_vnc_width / v_w;
+    i_y = i_y * p_sys->i_vnc_height / v_h;
 
     /* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
     rfbPointerEventMsg ev;
@@ -1308,7 +1348,9 @@ static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
     write_exact( p_filter, p_sys->i_socket,
                  (char*)&ev, sz_rfbPointerEventMsg);
 
-    return VLC_SUCCESS;
+    vlc_mutex_unlock( &p_sys->lock );
+
+    return VLC_EGENERIC;
 }
 
 /*****************************************************************************
@@ -1322,25 +1364,31 @@ static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
     filter_t *p_filter = (filter_t *)p_data;
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    if( !p_sys->b_connection_active )
-        return VLC_SUCCESS;
-
     if( !p_sys->b_vnc_key_events )
         return VLC_SUCCESS;
 
-    msg_Dbg( p_this, "key pressed (%d) ", newval.i_int );
+    msg_Dbg( p_this, "key pressed (%"PRId64") ", newval.i_int );
 
     if ( !newval.i_int )
     {
-        msg_Err( p_this, "Received invalid key event %d", newval.i_int );
+        msg_Err( p_this, "Received invalid key event 0" );
         return VLC_EGENERIC;
     }
 
+    vlc_mutex_lock( &p_sys->lock );
+    if( !p_sys->b_connection_active )
+    {
+        vlc_mutex_unlock( &p_sys->lock );
+        return VLC_SUCCESS;
+    }
+
     uint32_t i_key32 = newval.i_int;
     i_key32 = htonl(i_key32);
     rfbKeyEventMsg ev;
+
     ev.type = rfbKeyEvent;
     ev.down = 1;
+    ev.pad = 0;
 
     /* first key-down for modifier-keys */
     if (newval.i_int & KEY_MODIFIER_CTRL)
@@ -1392,6 +1440,7 @@ static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
        write_exact( p_filter, p_sys->i_socket,
                     (char*)&ev, sz_rfbKeyEventMsg);
     }
+    vlc_mutex_unlock( &p_sys->lock );
 
     return VLC_SUCCESS;
 }
@@ -1423,3 +1472,4 @@ static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
     gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
     gcry_cipher_close( ctx );
 }
+