| /* GStreamer |
| * Copyright (C) <2007> Thijs Vermeir <thijsvermeir@gmail.com> |
| * Copyright (C) <2006> Andre Moreira Magalhaes <andre.magalhaes@indt.org.br> |
| * Copyright (C) <2004> David A. Schleef <ds@schleef.org> |
| * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "gstrfbsrc.h" |
| |
| #include <gst/video/video.h> |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #ifdef HAVE_X11 |
| #include <X11/Xlib.h> |
| #endif |
| |
| enum |
| { |
| PROP_0, |
| PROP_HOST, |
| PROP_PORT, |
| PROP_VERSION, |
| PROP_PASSWORD, |
| PROP_OFFSET_X, |
| PROP_OFFSET_Y, |
| PROP_WIDTH, |
| PROP_HEIGHT, |
| PROP_INCREMENTAL, |
| PROP_USE_COPYRECT, |
| PROP_SHARED, |
| PROP_VIEWONLY |
| }; |
| |
| GST_DEBUG_CATEGORY_STATIC (rfbsrc_debug); |
| GST_DEBUG_CATEGORY (rfbdecoder_debug); |
| #define GST_CAT_DEFAULT rfbsrc_debug |
| |
| static GstStaticPadTemplate gst_rfb_src_template = |
| GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB") |
| "; " GST_VIDEO_CAPS_MAKE ("BGR") |
| "; " GST_VIDEO_CAPS_MAKE ("RGBx") |
| "; " GST_VIDEO_CAPS_MAKE ("BGRx") |
| "; " GST_VIDEO_CAPS_MAKE ("xRGB") |
| "; " GST_VIDEO_CAPS_MAKE ("xBGR"))); |
| |
| static void gst_rfb_src_finalize (GObject * object); |
| static void gst_rfb_src_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec); |
| static void gst_rfb_src_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec); |
| |
| static gboolean gst_rfb_src_negotiate (GstBaseSrc * bsrc); |
| static gboolean gst_rfb_src_stop (GstBaseSrc * bsrc); |
| static gboolean gst_rfb_src_event (GstBaseSrc * bsrc, GstEvent * event); |
| static gboolean gst_rfb_src_unlock (GstBaseSrc * bsrc); |
| static gboolean gst_rfb_src_decide_allocation (GstBaseSrc * bsrc, |
| GstQuery * query); |
| static GstFlowReturn gst_rfb_src_fill (GstPushSrc * psrc, GstBuffer * outbuf); |
| |
| #define gst_rfb_src_parent_class parent_class |
| G_DEFINE_TYPE (GstRfbSrc, gst_rfb_src, GST_TYPE_PUSH_SRC); |
| |
| static void |
| gst_rfb_src_class_init (GstRfbSrcClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstBaseSrcClass *gstbasesrc_class; |
| GstElementClass *gstelement_class; |
| GstPushSrcClass *gstpushsrc_class; |
| |
| |
| GST_DEBUG_CATEGORY_INIT (rfbsrc_debug, "rfbsrc", 0, "rfb src element"); |
| GST_DEBUG_CATEGORY_INIT (rfbdecoder_debug, "rfbdecoder", 0, "rfb decoder"); |
| |
| gobject_class = (GObjectClass *) klass; |
| gstbasesrc_class = (GstBaseSrcClass *) klass; |
| gstpushsrc_class = (GstPushSrcClass *) klass; |
| |
| gobject_class->finalize = gst_rfb_src_finalize; |
| gobject_class->set_property = gst_rfb_src_set_property; |
| gobject_class->get_property = gst_rfb_src_get_property; |
| |
| g_object_class_install_property (gobject_class, PROP_HOST, |
| g_param_spec_string ("host", "Host to connect to", "Host to connect to", |
| "127.0.0.1", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_PORT, |
| g_param_spec_int ("port", "Port", "Port", |
| 1, 65535, 5900, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_VERSION, |
| g_param_spec_string ("version", "RFB protocol version", |
| "RFB protocol version", "3.3", |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_PASSWORD, |
| g_param_spec_string ("password", "Password for authentication", |
| "Password for authentication", "", |
| G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_OFFSET_X, |
| g_param_spec_int ("offset-x", "x offset for screen scrapping", |
| "x offset for screen scrapping", 0, 65535, 0, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_OFFSET_Y, |
| g_param_spec_int ("offset-y", "y offset for screen scrapping", |
| "y offset for screen scrapping", 0, 65535, 0, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_WIDTH, |
| g_param_spec_int ("width", "width of screen", "width of screen", 0, 65535, |
| 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_HEIGHT, |
| g_param_spec_int ("height", "height of screen", "height of screen", 0, |
| 65535, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_INCREMENTAL, |
| g_param_spec_boolean ("incremental", "Incremental updates", |
| "Incremental updates", TRUE, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_USE_COPYRECT, |
| g_param_spec_boolean ("use-copyrect", "Use copyrect encoding", |
| "Use copyrect encoding", FALSE, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_SHARED, |
| g_param_spec_boolean ("shared", "Share desktop with other clients", |
| "Share desktop with other clients", TRUE, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| g_object_class_install_property (gobject_class, PROP_VIEWONLY, |
| g_param_spec_boolean ("view-only", "Only view the desktop", |
| "only view the desktop", FALSE, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| |
| gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_rfb_src_negotiate); |
| gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rfb_src_stop); |
| gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_rfb_src_event); |
| gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_rfb_src_unlock); |
| gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_rfb_src_fill); |
| gstbasesrc_class->decide_allocation = |
| GST_DEBUG_FUNCPTR (gst_rfb_src_decide_allocation); |
| |
| gstelement_class = GST_ELEMENT_CLASS (klass); |
| |
| gst_element_class_add_static_pad_template (gstelement_class, |
| &gst_rfb_src_template); |
| |
| gst_element_class_set_static_metadata (gstelement_class, "Rfb source", |
| "Source/Video", |
| "Creates a rfb video stream", |
| "David A. Schleef <ds@schleef.org>, " |
| "Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>, " |
| "Thijs Vermeir <thijsvermeir@gmail.com>"); |
| } |
| |
| static void |
| gst_rfb_src_init (GstRfbSrc * src) |
| { |
| GstBaseSrc *bsrc = GST_BASE_SRC (src); |
| |
| gst_pad_use_fixed_caps (GST_BASE_SRC_PAD (bsrc)); |
| gst_base_src_set_live (bsrc, TRUE); |
| gst_base_src_set_format (bsrc, GST_FORMAT_TIME); |
| |
| src->host = g_strdup ("127.0.0.1"); |
| src->port = 5900; |
| src->version_major = 3; |
| src->version_minor = 3; |
| |
| src->incremental_update = TRUE; |
| |
| src->view_only = FALSE; |
| |
| src->decoder = rfb_decoder_new (); |
| } |
| |
| static void |
| gst_rfb_src_finalize (GObject * object) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (object); |
| |
| g_free (src->host); |
| |
| if (src->decoder) { |
| rfb_decoder_free (src->decoder); |
| src->decoder = NULL; |
| } |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static void |
| gst_rfb_property_set_version (GstRfbSrc * src, gchar * value) |
| { |
| gchar *major; |
| gchar *minor; |
| |
| g_return_if_fail (src != NULL); |
| g_return_if_fail (value != NULL); |
| |
| major = g_strdup (value); |
| minor = g_strrstr (value, "."); |
| |
| g_return_if_fail (minor != NULL); |
| |
| *minor++ = 0; |
| |
| g_return_if_fail (g_ascii_isdigit (*major) == TRUE); |
| g_return_if_fail (g_ascii_isdigit (*minor) == TRUE); |
| |
| src->version_major = g_ascii_digit_value (*major); |
| src->version_minor = g_ascii_digit_value (*minor); |
| |
| GST_DEBUG ("Version major : %d", src->version_major); |
| GST_DEBUG ("Version minor : %d", src->version_minor); |
| |
| g_free (major); |
| g_free (value); |
| } |
| |
| static gchar * |
| gst_rfb_property_get_version (GstRfbSrc * src) |
| { |
| return g_strdup_printf ("%d.%d", src->version_major, src->version_minor); |
| } |
| |
| static void |
| gst_rfb_src_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (object); |
| |
| switch (prop_id) { |
| case PROP_HOST: |
| src->host = g_strdup (g_value_get_string (value)); |
| break; |
| case PROP_PORT: |
| src->port = g_value_get_int (value); |
| break; |
| case PROP_VERSION: |
| gst_rfb_property_set_version (src, g_strdup (g_value_get_string (value))); |
| break; |
| case PROP_PASSWORD: |
| g_free (src->decoder->password); |
| src->decoder->password = g_strdup (g_value_get_string (value)); |
| break; |
| case PROP_OFFSET_X: |
| src->decoder->offset_x = g_value_get_int (value); |
| break; |
| case PROP_OFFSET_Y: |
| src->decoder->offset_y = g_value_get_int (value); |
| break; |
| case PROP_WIDTH: |
| src->decoder->rect_width = g_value_get_int (value); |
| break; |
| case PROP_HEIGHT: |
| src->decoder->rect_height = g_value_get_int (value); |
| break; |
| case PROP_INCREMENTAL: |
| src->incremental_update = g_value_get_boolean (value); |
| break; |
| case PROP_USE_COPYRECT: |
| src->decoder->use_copyrect = g_value_get_boolean (value); |
| break; |
| case PROP_SHARED: |
| src->decoder->shared_flag = g_value_get_boolean (value); |
| break; |
| case PROP_VIEWONLY: |
| src->view_only = g_value_get_boolean (value); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void |
| gst_rfb_src_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (object); |
| gchar *version; |
| |
| switch (prop_id) { |
| case PROP_HOST: |
| g_value_set_string (value, src->host); |
| break; |
| case PROP_PORT: |
| g_value_set_int (value, src->port); |
| break; |
| case PROP_VERSION: |
| version = gst_rfb_property_get_version (src); |
| g_value_set_string (value, version); |
| g_free (version); |
| break; |
| case PROP_OFFSET_X: |
| g_value_set_int (value, src->decoder->offset_x); |
| break; |
| case PROP_OFFSET_Y: |
| g_value_set_int (value, src->decoder->offset_y); |
| break; |
| case PROP_WIDTH: |
| g_value_set_int (value, src->decoder->rect_width); |
| break; |
| case PROP_HEIGHT: |
| g_value_set_int (value, src->decoder->rect_height); |
| break; |
| case PROP_INCREMENTAL: |
| g_value_set_boolean (value, src->incremental_update); |
| break; |
| case PROP_USE_COPYRECT: |
| g_value_set_boolean (value, src->decoder->use_copyrect); |
| break; |
| case PROP_SHARED: |
| g_value_set_boolean (value, src->decoder->shared_flag); |
| break; |
| case PROP_VIEWONLY: |
| g_value_set_boolean (value, src->view_only); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static gboolean |
| gst_rfb_src_decide_allocation (GstBaseSrc * bsrc, GstQuery * query) |
| { |
| GstBufferPool *pool = NULL; |
| guint size, min = 1, max = 0; |
| GstStructure *config; |
| GstCaps *caps; |
| GstVideoInfo info; |
| gboolean ret; |
| |
| gst_query_parse_allocation (query, &caps, NULL); |
| |
| if (!caps || !gst_video_info_from_caps (&info, caps)) |
| return FALSE; |
| |
| while (gst_query_get_n_allocation_pools (query) > 0) { |
| gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); |
| |
| /* TODO We restrict to the exact size as we don't support strides or |
| * special padding */ |
| if (size == info.size) |
| break; |
| |
| gst_query_remove_nth_allocation_pool (query, 0); |
| gst_object_unref (pool); |
| pool = NULL; |
| } |
| |
| if (pool == NULL) { |
| /* we did not get a pool, make one ourselves then */ |
| pool = gst_video_buffer_pool_new (); |
| size = info.size; |
| min = 1; |
| max = 0; |
| gst_query_add_allocation_pool (query, pool, size, min, max); |
| } |
| |
| config = gst_buffer_pool_get_config (pool); |
| gst_buffer_pool_config_set_params (config, caps, size, min, max); |
| |
| ret = gst_buffer_pool_set_config (pool, config); |
| gst_object_unref (pool); |
| |
| return ret; |
| } |
| |
| static gboolean |
| gst_rfb_src_negotiate (GstBaseSrc * bsrc) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (bsrc); |
| RfbDecoder *decoder; |
| GstCaps *caps; |
| GstVideoInfo vinfo; |
| GstVideoFormat vformat; |
| guint32 red_mask, green_mask, blue_mask; |
| gchar *stream_id = NULL; |
| GstEvent *stream_start = NULL; |
| |
| decoder = src->decoder; |
| |
| if (decoder->inited) |
| return TRUE; |
| |
| GST_DEBUG_OBJECT (src, "connecting to host %s on port %d", |
| src->host, src->port); |
| if (!rfb_decoder_connect_tcp (decoder, src->host, src->port)) { |
| if (decoder->error != NULL) { |
| GST_ELEMENT_ERROR (src, RESOURCE, READ, |
| ("Could not connect to VNC server %s on port %d: %s", src->host, |
| src->port, decoder->error->message), (NULL)); |
| } else { |
| GST_ELEMENT_ERROR (src, RESOURCE, READ, |
| ("Could not connect to VNC server %s on port %d", src->host, |
| src->port), (NULL)); |
| } |
| return FALSE; |
| } |
| |
| while (!decoder->inited) { |
| if (!rfb_decoder_iterate (decoder)) { |
| if (decoder->error != NULL) { |
| GST_ELEMENT_ERROR (src, RESOURCE, READ, |
| ("Failed to setup VNC connection to host %s on port %d: %s", |
| src->host, src->port, decoder->error->message), (NULL)); |
| } else { |
| GST_ELEMENT_ERROR (src, RESOURCE, READ, |
| ("Failed to setup VNC connection to host %s on port %d", src->host, |
| src->port), (NULL)); |
| } |
| return FALSE; |
| } |
| } |
| |
| stream_id = gst_pad_create_stream_id_printf (GST_BASE_SRC_PAD (bsrc), |
| GST_ELEMENT (src), "%s:%d", src->host, src->port); |
| stream_start = gst_event_new_stream_start (stream_id); |
| g_free (stream_id); |
| gst_pad_push_event (GST_BASE_SRC_PAD (bsrc), stream_start); |
| |
| decoder->rect_width = |
| (decoder->rect_width ? decoder->rect_width : decoder->width); |
| decoder->rect_height = |
| (decoder->rect_height ? decoder->rect_height : decoder->height); |
| |
| decoder->decoder_private = src; |
| |
| /* calculate some many used values */ |
| decoder->bytespp = decoder->bpp / 8; |
| decoder->line_size = decoder->rect_width * decoder->bytespp; |
| |
| GST_DEBUG_OBJECT (src, "setting caps width to %d and height to %d", |
| decoder->rect_width, decoder->rect_height); |
| |
| red_mask = decoder->red_max << decoder->red_shift; |
| green_mask = decoder->green_max << decoder->green_shift; |
| blue_mask = decoder->blue_max << decoder->blue_shift; |
| |
| vformat = gst_video_format_from_masks (decoder->depth, decoder->bpp, |
| decoder->big_endian ? G_BIG_ENDIAN : G_LITTLE_ENDIAN, |
| red_mask, green_mask, blue_mask, 0); |
| |
| gst_video_info_init (&vinfo); |
| |
| gst_video_info_set_format (&vinfo, vformat, decoder->rect_width, |
| decoder->rect_height); |
| |
| decoder->frame = g_malloc (vinfo.size); |
| if (decoder->use_copyrect) |
| decoder->prev_frame = g_malloc (vinfo.size); |
| |
| caps = gst_video_info_to_caps (&vinfo); |
| |
| gst_base_src_set_caps (bsrc, caps); |
| |
| gst_caps_unref (caps); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| gst_rfb_src_stop (GstBaseSrc * bsrc) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (bsrc); |
| |
| rfb_decoder_disconnect (src->decoder); |
| |
| if (src->decoder->frame) { |
| g_free (src->decoder->frame); |
| src->decoder->frame = NULL; |
| } |
| |
| if (src->decoder->prev_frame) { |
| g_free (src->decoder->prev_frame); |
| src->decoder->prev_frame = NULL; |
| } |
| |
| return TRUE; |
| } |
| |
| static GstFlowReturn |
| gst_rfb_src_fill (GstPushSrc * psrc, GstBuffer * outbuf) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (psrc); |
| RfbDecoder *decoder = src->decoder; |
| GstMapInfo info; |
| |
| rfb_decoder_send_update_request (decoder, src->incremental_update, |
| decoder->offset_x, decoder->offset_y, decoder->rect_width, |
| decoder->rect_height); |
| |
| while (decoder->state != NULL) { |
| if (!rfb_decoder_iterate (decoder)) { |
| if (decoder->error != NULL) { |
| GST_ELEMENT_ERROR (src, RESOURCE, READ, |
| ("Error on VNC connection to host %s on port %d: %s", |
| src->host, src->port, decoder->error->message), (NULL)); |
| } else { |
| GST_ELEMENT_ERROR (src, RESOURCE, READ, |
| ("Error on setup VNC connection to host %s on port %d", src->host, |
| src->port), (NULL)); |
| } |
| return GST_FLOW_ERROR; |
| } |
| } |
| |
| if (!gst_buffer_map (outbuf, &info, GST_MAP_WRITE)) { |
| GST_ELEMENT_ERROR (src, RESOURCE, WRITE, |
| ("Could not map the output frame"), (NULL)); |
| return GST_FLOW_ERROR; |
| } |
| |
| memcpy (info.data, decoder->frame, info.size); |
| |
| GST_BUFFER_PTS (outbuf) = |
| gst_clock_get_time (GST_ELEMENT_CLOCK (src)) - |
| GST_ELEMENT_CAST (src)->base_time; |
| |
| gst_buffer_unmap (outbuf, &info); |
| |
| return GST_FLOW_OK; |
| } |
| |
| static gboolean |
| gst_rfb_src_event (GstBaseSrc * bsrc, GstEvent * event) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (bsrc); |
| gdouble x, y; |
| gint button; |
| const GstStructure *structure; |
| const gchar *event_type; |
| gboolean key_event, key_press; |
| |
| key_event = FALSE; |
| |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_NAVIGATION: |
| |
| /* if in view_only mode ignore the navigation event */ |
| if (src->view_only) |
| break; |
| |
| structure = gst_event_get_structure (event); |
| event_type = gst_structure_get_string (structure, "event"); |
| |
| if (strcmp (event_type, "key-press") == 0) { |
| key_event = key_press = TRUE; |
| } else if (strcmp (event_type, "key-release") == 0) { |
| key_event = TRUE; |
| key_press = FALSE; |
| } |
| |
| if (key_event) { |
| #ifdef HAVE_X11 |
| const gchar *key; |
| KeySym key_sym; |
| |
| key = gst_structure_get_string (structure, "key"); |
| key_sym = XStringToKeysym (key); |
| |
| if (key_sym != NoSymbol) |
| rfb_decoder_send_key_event (src->decoder, key_sym, key_press); |
| #endif |
| break; |
| } |
| |
| gst_structure_get_double (structure, "pointer_x", &x); |
| gst_structure_get_double (structure, "pointer_y", &y); |
| gst_structure_get_int (structure, "button", &button); |
| |
| /* we need to take care of the offset's */ |
| x += src->decoder->offset_x; |
| y += src->decoder->offset_y; |
| |
| if (strcmp (event_type, "mouse-move") == 0) { |
| GST_LOG_OBJECT (src, "sending mouse-move event " |
| "button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y); |
| rfb_decoder_send_pointer_event (src->decoder, src->button_mask, |
| (gint) x, (gint) y); |
| } else if (strcmp (event_type, "mouse-button-release") == 0) { |
| src->button_mask &= ~(1 << (button - 1)); |
| GST_LOG_OBJECT (src, "sending mouse-button-release event " |
| "button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y); |
| rfb_decoder_send_pointer_event (src->decoder, src->button_mask, |
| (gint) x, (gint) y); |
| } else if (strcmp (event_type, "mouse-button-press") == 0) { |
| src->button_mask |= (1 << (button - 1)); |
| GST_LOG_OBJECT (src, "sending mouse-button-press event " |
| "button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y); |
| rfb_decoder_send_pointer_event (src->decoder, src->button_mask, |
| (gint) x, (gint) y); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| gst_rfb_src_unlock (GstBaseSrc * bsrc) |
| { |
| GstRfbSrc *src = GST_RFB_SRC (bsrc); |
| g_cancellable_cancel (src->decoder->cancellable); |
| return TRUE; |
| } |
| |
| static gboolean |
| plugin_init (GstPlugin * plugin) |
| { |
| return gst_element_register (plugin, "rfbsrc", GST_RANK_NONE, |
| GST_TYPE_RFB_SRC); |
| } |
| |
| GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
| GST_VERSION_MINOR, |
| rfbsrc, |
| "Connects to a VNC server and decodes RFB stream", |
| plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |