1 /* GStreamer
2  * Copyright (C) <2007> Thijs Vermeir <thijsvermeir@gmail.com>
3  * Copyright (C) <2006> Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
4  * Copyright (C) <2004> David A. Schleef <ds@schleef.org>
5  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "gstrfbsrc.h"
28 
29 #include <gst/video/video.h>
30 
31 #include <string.h>
32 #include <stdlib.h>
33 #ifdef HAVE_X11
34 #include <X11/Xlib.h>
35 #endif
36 
37 enum
38 {
39   PROP_0,
40   PROP_HOST,
41   PROP_PORT,
42   PROP_VERSION,
43   PROP_PASSWORD,
44   PROP_OFFSET_X,
45   PROP_OFFSET_Y,
46   PROP_WIDTH,
47   PROP_HEIGHT,
48   PROP_INCREMENTAL,
49   PROP_USE_COPYRECT,
50   PROP_SHARED,
51   PROP_VIEWONLY
52 };
53 
54 GST_DEBUG_CATEGORY_STATIC (rfbsrc_debug);
55 GST_DEBUG_CATEGORY (rfbdecoder_debug);
56 #define GST_CAT_DEFAULT rfbsrc_debug
57 
58 static GstStaticPadTemplate gst_rfb_src_template =
59     GST_STATIC_PAD_TEMPLATE ("src",
60     GST_PAD_SRC,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")
63         "; " GST_VIDEO_CAPS_MAKE ("BGR")
64         "; " GST_VIDEO_CAPS_MAKE ("RGBx")
65         "; " GST_VIDEO_CAPS_MAKE ("BGRx")
66         "; " GST_VIDEO_CAPS_MAKE ("xRGB")
67         "; " GST_VIDEO_CAPS_MAKE ("xBGR")));
68 
69 static void gst_rfb_src_finalize (GObject * object);
70 static void gst_rfb_src_set_property (GObject * object, guint prop_id,
71     const GValue * value, GParamSpec * pspec);
72 static void gst_rfb_src_get_property (GObject * object, guint prop_id,
73     GValue * value, GParamSpec * pspec);
74 
75 static gboolean gst_rfb_src_negotiate (GstBaseSrc * bsrc);
76 static gboolean gst_rfb_src_stop (GstBaseSrc * bsrc);
77 static gboolean gst_rfb_src_event (GstBaseSrc * bsrc, GstEvent * event);
78 static gboolean gst_rfb_src_unlock (GstBaseSrc * bsrc);
79 static gboolean gst_rfb_src_decide_allocation (GstBaseSrc * bsrc,
80     GstQuery * query);
81 static GstFlowReturn gst_rfb_src_fill (GstPushSrc * psrc, GstBuffer * outbuf);
82 
83 #define gst_rfb_src_parent_class parent_class
84 G_DEFINE_TYPE (GstRfbSrc, gst_rfb_src, GST_TYPE_PUSH_SRC);
85 
86 static void
gst_rfb_src_class_init(GstRfbSrcClass * klass)87 gst_rfb_src_class_init (GstRfbSrcClass * klass)
88 {
89   GObjectClass *gobject_class;
90   GstBaseSrcClass *gstbasesrc_class;
91   GstElementClass *gstelement_class;
92   GstPushSrcClass *gstpushsrc_class;
93 
94 
95   GST_DEBUG_CATEGORY_INIT (rfbsrc_debug, "rfbsrc", 0, "rfb src element");
96   GST_DEBUG_CATEGORY_INIT (rfbdecoder_debug, "rfbdecoder", 0, "rfb decoder");
97 
98   gobject_class = (GObjectClass *) klass;
99   gstbasesrc_class = (GstBaseSrcClass *) klass;
100   gstpushsrc_class = (GstPushSrcClass *) klass;
101 
102   gobject_class->finalize = gst_rfb_src_finalize;
103   gobject_class->set_property = gst_rfb_src_set_property;
104   gobject_class->get_property = gst_rfb_src_get_property;
105 
106   g_object_class_install_property (gobject_class, PROP_HOST,
107       g_param_spec_string ("host", "Host to connect to", "Host to connect to",
108           "127.0.0.1", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
109   g_object_class_install_property (gobject_class, PROP_PORT,
110       g_param_spec_int ("port", "Port", "Port",
111           1, 65535, 5900, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
112   g_object_class_install_property (gobject_class, PROP_VERSION,
113       g_param_spec_string ("version", "RFB protocol version",
114           "RFB protocol version", "3.3",
115           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
116   g_object_class_install_property (gobject_class, PROP_PASSWORD,
117       g_param_spec_string ("password", "Password for authentication",
118           "Password for authentication", "",
119           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
120   g_object_class_install_property (gobject_class, PROP_OFFSET_X,
121       g_param_spec_int ("offset-x", "x offset for screen scrapping",
122           "x offset for screen scrapping", 0, 65535, 0,
123           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
124   g_object_class_install_property (gobject_class, PROP_OFFSET_Y,
125       g_param_spec_int ("offset-y", "y offset for screen scrapping",
126           "y offset for screen scrapping", 0, 65535, 0,
127           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
128   g_object_class_install_property (gobject_class, PROP_WIDTH,
129       g_param_spec_int ("width", "width of screen", "width of screen", 0, 65535,
130           0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
131   g_object_class_install_property (gobject_class, PROP_HEIGHT,
132       g_param_spec_int ("height", "height of screen", "height of screen", 0,
133           65535, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
134   g_object_class_install_property (gobject_class, PROP_INCREMENTAL,
135       g_param_spec_boolean ("incremental", "Incremental updates",
136           "Incremental updates", TRUE,
137           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
138   g_object_class_install_property (gobject_class, PROP_USE_COPYRECT,
139       g_param_spec_boolean ("use-copyrect", "Use copyrect encoding",
140           "Use copyrect encoding", FALSE,
141           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
142   g_object_class_install_property (gobject_class, PROP_SHARED,
143       g_param_spec_boolean ("shared", "Share desktop with other clients",
144           "Share desktop with other clients", TRUE,
145           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146   g_object_class_install_property (gobject_class, PROP_VIEWONLY,
147       g_param_spec_boolean ("view-only", "Only view the desktop",
148           "only view the desktop", FALSE,
149           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
150 
151   gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_rfb_src_negotiate);
152   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rfb_src_stop);
153   gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_rfb_src_event);
154   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_rfb_src_unlock);
155   gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_rfb_src_fill);
156   gstbasesrc_class->decide_allocation =
157       GST_DEBUG_FUNCPTR (gst_rfb_src_decide_allocation);
158 
159   gstelement_class = GST_ELEMENT_CLASS (klass);
160 
161   gst_element_class_add_static_pad_template (gstelement_class,
162       &gst_rfb_src_template);
163 
164   gst_element_class_set_static_metadata (gstelement_class, "Rfb source",
165       "Source/Video",
166       "Creates a rfb video stream",
167       "David A. Schleef <ds@schleef.org>, "
168       "Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>, "
169       "Thijs Vermeir <thijsvermeir@gmail.com>");
170 }
171 
172 static void
gst_rfb_src_init(GstRfbSrc * src)173 gst_rfb_src_init (GstRfbSrc * src)
174 {
175   GstBaseSrc *bsrc = GST_BASE_SRC (src);
176 
177   gst_pad_use_fixed_caps (GST_BASE_SRC_PAD (bsrc));
178   gst_base_src_set_live (bsrc, TRUE);
179   gst_base_src_set_format (bsrc, GST_FORMAT_TIME);
180 
181   src->host = g_strdup ("127.0.0.1");
182   src->port = 5900;
183   src->version_major = 3;
184   src->version_minor = 3;
185 
186   src->incremental_update = TRUE;
187 
188   src->view_only = FALSE;
189 
190   src->decoder = rfb_decoder_new ();
191 }
192 
193 static void
gst_rfb_src_finalize(GObject * object)194 gst_rfb_src_finalize (GObject * object)
195 {
196   GstRfbSrc *src = GST_RFB_SRC (object);
197 
198   g_free (src->host);
199 
200   if (src->decoder) {
201     rfb_decoder_free (src->decoder);
202     src->decoder = NULL;
203   }
204 
205   G_OBJECT_CLASS (parent_class)->finalize (object);
206 }
207 
208 static void
gst_rfb_property_set_version(GstRfbSrc * src,gchar * value)209 gst_rfb_property_set_version (GstRfbSrc * src, gchar * value)
210 {
211   gchar *major;
212   gchar *minor;
213 
214   g_return_if_fail (src != NULL);
215   g_return_if_fail (value != NULL);
216 
217   major = g_strdup (value);
218   minor = g_strrstr (value, ".");
219 
220   g_return_if_fail (minor != NULL);
221 
222   *minor++ = 0;
223 
224   g_return_if_fail (g_ascii_isdigit (*major) == TRUE);
225   g_return_if_fail (g_ascii_isdigit (*minor) == TRUE);
226 
227   src->version_major = g_ascii_digit_value (*major);
228   src->version_minor = g_ascii_digit_value (*minor);
229 
230   GST_DEBUG ("Version major : %d", src->version_major);
231   GST_DEBUG ("Version minor : %d", src->version_minor);
232 
233   g_free (major);
234   g_free (value);
235 }
236 
237 static gchar *
gst_rfb_property_get_version(GstRfbSrc * src)238 gst_rfb_property_get_version (GstRfbSrc * src)
239 {
240   return g_strdup_printf ("%d.%d", src->version_major, src->version_minor);
241 }
242 
243 static void
gst_rfb_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)244 gst_rfb_src_set_property (GObject * object, guint prop_id,
245     const GValue * value, GParamSpec * pspec)
246 {
247   GstRfbSrc *src = GST_RFB_SRC (object);
248 
249   switch (prop_id) {
250     case PROP_HOST:
251       src->host = g_strdup (g_value_get_string (value));
252       break;
253     case PROP_PORT:
254       src->port = g_value_get_int (value);
255       break;
256     case PROP_VERSION:
257       gst_rfb_property_set_version (src, g_strdup (g_value_get_string (value)));
258       break;
259     case PROP_PASSWORD:
260       g_free (src->decoder->password);
261       src->decoder->password = g_strdup (g_value_get_string (value));
262       break;
263     case PROP_OFFSET_X:
264       src->decoder->offset_x = g_value_get_int (value);
265       break;
266     case PROP_OFFSET_Y:
267       src->decoder->offset_y = g_value_get_int (value);
268       break;
269     case PROP_WIDTH:
270       src->decoder->rect_width = g_value_get_int (value);
271       break;
272     case PROP_HEIGHT:
273       src->decoder->rect_height = g_value_get_int (value);
274       break;
275     case PROP_INCREMENTAL:
276       src->incremental_update = g_value_get_boolean (value);
277       break;
278     case PROP_USE_COPYRECT:
279       src->decoder->use_copyrect = g_value_get_boolean (value);
280       break;
281     case PROP_SHARED:
282       src->decoder->shared_flag = g_value_get_boolean (value);
283       break;
284     case PROP_VIEWONLY:
285       src->view_only = g_value_get_boolean (value);
286       break;
287     default:
288       break;
289   }
290 }
291 
292 static void
gst_rfb_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)293 gst_rfb_src_get_property (GObject * object, guint prop_id,
294     GValue * value, GParamSpec * pspec)
295 {
296   GstRfbSrc *src = GST_RFB_SRC (object);
297   gchar *version;
298 
299   switch (prop_id) {
300     case PROP_HOST:
301       g_value_set_string (value, src->host);
302       break;
303     case PROP_PORT:
304       g_value_set_int (value, src->port);
305       break;
306     case PROP_VERSION:
307       version = gst_rfb_property_get_version (src);
308       g_value_set_string (value, version);
309       g_free (version);
310       break;
311     case PROP_OFFSET_X:
312       g_value_set_int (value, src->decoder->offset_x);
313       break;
314     case PROP_OFFSET_Y:
315       g_value_set_int (value, src->decoder->offset_y);
316       break;
317     case PROP_WIDTH:
318       g_value_set_int (value, src->decoder->rect_width);
319       break;
320     case PROP_HEIGHT:
321       g_value_set_int (value, src->decoder->rect_height);
322       break;
323     case PROP_INCREMENTAL:
324       g_value_set_boolean (value, src->incremental_update);
325       break;
326     case PROP_USE_COPYRECT:
327       g_value_set_boolean (value, src->decoder->use_copyrect);
328       break;
329     case PROP_SHARED:
330       g_value_set_boolean (value, src->decoder->shared_flag);
331       break;
332     case PROP_VIEWONLY:
333       g_value_set_boolean (value, src->view_only);
334       break;
335     default:
336       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
337       break;
338   }
339 }
340 
341 static gboolean
gst_rfb_src_decide_allocation(GstBaseSrc * bsrc,GstQuery * query)342 gst_rfb_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query)
343 {
344   GstBufferPool *pool = NULL;
345   guint size, min = 1, max = 0;
346   GstStructure *config;
347   GstCaps *caps;
348   GstVideoInfo info;
349   gboolean ret;
350 
351   gst_query_parse_allocation (query, &caps, NULL);
352 
353   if (!caps || !gst_video_info_from_caps (&info, caps))
354     return FALSE;
355 
356   while (gst_query_get_n_allocation_pools (query) > 0) {
357     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
358 
359     /* TODO We restrict to the exact size as we don't support strides or
360      * special padding */
361     if (size == info.size)
362       break;
363 
364     gst_query_remove_nth_allocation_pool (query, 0);
365     gst_object_unref (pool);
366     pool = NULL;
367   }
368 
369   if (pool == NULL) {
370     /* we did not get a pool, make one ourselves then */
371     pool = gst_video_buffer_pool_new ();
372     size = info.size;
373     min = 1;
374     max = 0;
375 
376     if (gst_query_get_n_allocation_pools (query) > 0)
377       gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
378     else
379       gst_query_add_allocation_pool (query, pool, size, min, max);
380   }
381 
382   config = gst_buffer_pool_get_config (pool);
383   gst_buffer_pool_config_set_params (config, caps, size, min, max);
384 
385   ret = gst_buffer_pool_set_config (pool, config);
386   gst_object_unref (pool);
387 
388   return ret;
389 }
390 
391 static gboolean
gst_rfb_src_negotiate(GstBaseSrc * bsrc)392 gst_rfb_src_negotiate (GstBaseSrc * bsrc)
393 {
394   GstRfbSrc *src = GST_RFB_SRC (bsrc);
395   RfbDecoder *decoder;
396   GstCaps *caps;
397   GstVideoInfo vinfo;
398   GstVideoFormat vformat;
399   guint32 red_mask, green_mask, blue_mask;
400   gchar *stream_id = NULL;
401   GstEvent *stream_start = NULL;
402 
403   decoder = src->decoder;
404 
405   if (decoder->inited)
406     return TRUE;
407 
408   GST_DEBUG_OBJECT (src, "connecting to host %s on port %d",
409       src->host, src->port);
410   if (!rfb_decoder_connect_tcp (decoder, src->host, src->port)) {
411     if (decoder->error != NULL) {
412       GST_ELEMENT_ERROR (src, RESOURCE, READ,
413           ("Could not connect to VNC server %s on port %d: %s", src->host,
414               src->port, decoder->error->message), (NULL));
415     } else {
416       GST_ELEMENT_ERROR (src, RESOURCE, READ,
417           ("Could not connect to VNC server %s on port %d", src->host,
418               src->port), (NULL));
419     }
420     return FALSE;
421   }
422 
423   while (!decoder->inited) {
424     if (!rfb_decoder_iterate (decoder)) {
425       if (decoder->error != NULL) {
426         GST_ELEMENT_ERROR (src, RESOURCE, READ,
427             ("Failed to setup VNC connection to host %s on port %d: %s",
428                 src->host, src->port, decoder->error->message), (NULL));
429       } else {
430         GST_ELEMENT_ERROR (src, RESOURCE, READ,
431             ("Failed to setup VNC connection to host %s on port %d", src->host,
432                 src->port), (NULL));
433       }
434       return FALSE;
435     }
436   }
437 
438   stream_id = gst_pad_create_stream_id_printf (GST_BASE_SRC_PAD (bsrc),
439       GST_ELEMENT (src), "%s:%d", src->host, src->port);
440   stream_start = gst_event_new_stream_start (stream_id);
441   g_free (stream_id);
442   gst_pad_push_event (GST_BASE_SRC_PAD (bsrc), stream_start);
443 
444   decoder->rect_width =
445       (decoder->rect_width ? decoder->rect_width : decoder->width);
446   decoder->rect_height =
447       (decoder->rect_height ? decoder->rect_height : decoder->height);
448 
449   decoder->decoder_private = src;
450 
451   /* calculate some many used values */
452   decoder->bytespp = decoder->bpp / 8;
453   decoder->line_size = decoder->rect_width * decoder->bytespp;
454 
455   GST_DEBUG_OBJECT (src, "setting caps width to %d and height to %d",
456       decoder->rect_width, decoder->rect_height);
457 
458   red_mask = decoder->red_max << decoder->red_shift;
459   green_mask = decoder->green_max << decoder->green_shift;
460   blue_mask = decoder->blue_max << decoder->blue_shift;
461 
462   vformat = gst_video_format_from_masks (decoder->depth, decoder->bpp,
463       decoder->big_endian ? G_BIG_ENDIAN : G_LITTLE_ENDIAN,
464       red_mask, green_mask, blue_mask, 0);
465 
466   gst_video_info_init (&vinfo);
467 
468   gst_video_info_set_format (&vinfo, vformat, decoder->rect_width,
469       decoder->rect_height);
470 
471   decoder->frame = g_malloc (vinfo.size);
472   if (decoder->use_copyrect)
473     decoder->prev_frame = g_malloc (vinfo.size);
474 
475   caps = gst_video_info_to_caps (&vinfo);
476 
477   gst_base_src_set_caps (bsrc, caps);
478 
479   gst_caps_unref (caps);
480 
481   return TRUE;
482 }
483 
484 static gboolean
gst_rfb_src_stop(GstBaseSrc * bsrc)485 gst_rfb_src_stop (GstBaseSrc * bsrc)
486 {
487   GstRfbSrc *src = GST_RFB_SRC (bsrc);
488 
489   rfb_decoder_disconnect (src->decoder);
490 
491   if (src->decoder->frame) {
492     g_free (src->decoder->frame);
493     src->decoder->frame = NULL;
494   }
495 
496   if (src->decoder->prev_frame) {
497     g_free (src->decoder->prev_frame);
498     src->decoder->prev_frame = NULL;
499   }
500 
501   return TRUE;
502 }
503 
504 static GstFlowReturn
gst_rfb_src_fill(GstPushSrc * psrc,GstBuffer * outbuf)505 gst_rfb_src_fill (GstPushSrc * psrc, GstBuffer * outbuf)
506 {
507   GstRfbSrc *src = GST_RFB_SRC (psrc);
508   RfbDecoder *decoder = src->decoder;
509   GstMapInfo info;
510 
511   rfb_decoder_send_update_request (decoder, src->incremental_update,
512       decoder->offset_x, decoder->offset_y, decoder->rect_width,
513       decoder->rect_height);
514 
515   while (decoder->state != NULL) {
516     if (!rfb_decoder_iterate (decoder)) {
517       if (decoder->error != NULL) {
518         GST_ELEMENT_ERROR (src, RESOURCE, READ,
519             ("Error on VNC connection to host %s on port %d: %s",
520                 src->host, src->port, decoder->error->message), (NULL));
521       } else {
522         GST_ELEMENT_ERROR (src, RESOURCE, READ,
523             ("Error on setup VNC connection to host %s on port %d", src->host,
524                 src->port), (NULL));
525       }
526       return GST_FLOW_ERROR;
527     }
528   }
529 
530   if (!gst_buffer_map (outbuf, &info, GST_MAP_WRITE)) {
531     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
532         ("Could not map the output frame"), (NULL));
533     return GST_FLOW_ERROR;
534   }
535 
536   memcpy (info.data, decoder->frame, info.size);
537 
538   GST_BUFFER_PTS (outbuf) =
539       gst_clock_get_time (GST_ELEMENT_CLOCK (src)) -
540       GST_ELEMENT_CAST (src)->base_time;
541 
542   gst_buffer_unmap (outbuf, &info);
543 
544   return GST_FLOW_OK;
545 }
546 
547 static gboolean
gst_rfb_src_event(GstBaseSrc * bsrc,GstEvent * event)548 gst_rfb_src_event (GstBaseSrc * bsrc, GstEvent * event)
549 {
550   GstRfbSrc *src = GST_RFB_SRC (bsrc);
551   gdouble x, y;
552   gint button;
553   const GstStructure *structure;
554   const gchar *event_type;
555   gboolean key_event, key_press;
556 
557   key_event = FALSE;
558 
559   switch (GST_EVENT_TYPE (event)) {
560     case GST_EVENT_NAVIGATION:
561 
562       /* if in view_only mode ignore the navigation event */
563       if (src->view_only)
564         break;
565 
566       structure = gst_event_get_structure (event);
567       event_type = gst_structure_get_string (structure, "event");
568 
569       if (strcmp (event_type, "key-press") == 0) {
570         key_event = key_press = TRUE;
571       } else if (strcmp (event_type, "key-release") == 0) {
572         key_event = TRUE;
573         key_press = FALSE;
574       }
575 
576       if (key_event) {
577 #ifdef HAVE_X11
578         const gchar *key;
579         KeySym key_sym;
580 
581         key = gst_structure_get_string (structure, "key");
582         key_sym = XStringToKeysym (key);
583 
584         if (key_sym != NoSymbol)
585           rfb_decoder_send_key_event (src->decoder, key_sym, key_press);
586 #endif
587         break;
588       }
589 
590       gst_structure_get_double (structure, "pointer_x", &x);
591       gst_structure_get_double (structure, "pointer_y", &y);
592       gst_structure_get_int (structure, "button", &button);
593 
594       /* we need to take care of the offset's */
595       x += src->decoder->offset_x;
596       y += src->decoder->offset_y;
597 
598       if (strcmp (event_type, "mouse-move") == 0) {
599         GST_LOG_OBJECT (src, "sending mouse-move event "
600             "button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y);
601         rfb_decoder_send_pointer_event (src->decoder, src->button_mask,
602             (gint) x, (gint) y);
603       } else if (strcmp (event_type, "mouse-button-release") == 0) {
604         src->button_mask &= ~(1 << (button - 1));
605         GST_LOG_OBJECT (src, "sending mouse-button-release event "
606             "button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y);
607         rfb_decoder_send_pointer_event (src->decoder, src->button_mask,
608             (gint) x, (gint) y);
609       } else if (strcmp (event_type, "mouse-button-press") == 0) {
610         src->button_mask |= (1 << (button - 1));
611         GST_LOG_OBJECT (src, "sending mouse-button-press event "
612             "button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y);
613         rfb_decoder_send_pointer_event (src->decoder, src->button_mask,
614             (gint) x, (gint) y);
615       }
616       break;
617     default:
618       break;
619   }
620 
621   return TRUE;
622 }
623 
624 static gboolean
gst_rfb_src_unlock(GstBaseSrc * bsrc)625 gst_rfb_src_unlock (GstBaseSrc * bsrc)
626 {
627   GstRfbSrc *src = GST_RFB_SRC (bsrc);
628   g_cancellable_cancel (src->decoder->cancellable);
629   return TRUE;
630 }
631 
632 static gboolean
plugin_init(GstPlugin * plugin)633 plugin_init (GstPlugin * plugin)
634 {
635   return gst_element_register (plugin, "rfbsrc", GST_RANK_NONE,
636       GST_TYPE_RFB_SRC);
637 }
638 
639 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
640     GST_VERSION_MINOR,
641     rfbsrc,
642     "Connects to a VNC server and decodes RFB stream",
643     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
644