| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "rfb.h" |
| #include "d3des.h" |
| #include <gst/gst.h> |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define RFB_GET_UINT32(ptr) GST_READ_UINT32_BE(ptr) |
| #define RFB_GET_UINT16(ptr) GST_READ_UINT16_BE(ptr) |
| #define RFB_GET_UINT8(ptr) GST_READ_UINT8(ptr) |
| |
| #define RFB_SET_UINT32(ptr, val) GST_WRITE_UINT32_BE((ptr),(val)) |
| #define RFB_SET_UINT16(ptr, val) GST_WRITE_UINT16_BE((ptr),(val)) |
| #define RFB_SET_UINT8(ptr, val) GST_WRITE_UINT8((ptr),(val)) |
| |
| GST_DEBUG_CATEGORY_EXTERN (rfbdecoder_debug); |
| #define GST_CAT_DEFAULT rfbdecoder_debug |
| |
| |
| static gboolean rfb_decoder_state_wait_for_protocol_version (RfbDecoder * |
| decoder); |
| static gboolean rfb_decoder_state_wait_for_security (RfbDecoder * decoder); |
| static gboolean rfb_decoder_state_send_client_initialisation (RfbDecoder * |
| decoder); |
| static gboolean rfb_decoder_state_wait_for_server_initialisation (RfbDecoder * |
| decoder); |
| static gboolean rfb_decoder_state_security_result (RfbDecoder * decoder); |
| static gboolean rfb_decoder_state_normal (RfbDecoder * decoder); |
| static gboolean rfb_decoder_state_framebuffer_update (RfbDecoder * decoder); |
| static gboolean rfb_decoder_state_framebuffer_update_rectangle (RfbDecoder * |
| decoder); |
| static gboolean rfb_decoder_state_set_colour_map_entries (RfbDecoder * decoder); |
| static gboolean rfb_decoder_state_server_cut_text (RfbDecoder * decoder); |
| static void rfb_decoder_raw_encoding (RfbDecoder * decoder, gint start_x, |
| gint start_y, gint rect_w, gint rect_h); |
| static void rfb_decoder_copyrect_encoding (RfbDecoder * decoder, gint start_x, |
| gint start_y, gint rect_w, gint rect_h); |
| static void rfb_decoder_rre_encoding (RfbDecoder * decoder, gint start_x, |
| gint start_y, gint rect_w, gint rect_h); |
| static void rfb_decoder_corre_encoding (RfbDecoder * decoder, gint start_x, |
| gint start_y, gint rect_w, gint rect_h); |
| static void rfb_decoder_hextile_encoding (RfbDecoder * decoder, gint start_x, |
| gint start_y, gint rect_w, gint rect_h); |
| |
| RfbDecoder * |
| rfb_decoder_new (void) |
| { |
| RfbDecoder *decoder = g_new0 (RfbDecoder, 1); |
| |
| decoder->socket = NULL; |
| decoder->cancellable = g_cancellable_new (); |
| |
| decoder->password = NULL; |
| |
| decoder->use_copyrect = FALSE; |
| |
| decoder->offset_x = 0; |
| decoder->offset_y = 0; |
| decoder->rect_width = 0; |
| decoder->rect_height = 0; |
| decoder->shared_flag = TRUE; |
| decoder->disconnected = FALSE; |
| decoder->data = NULL; |
| decoder->data_len = 0; |
| decoder->error = NULL; |
| |
| return decoder; |
| } |
| |
| void |
| rfb_decoder_free (RfbDecoder * decoder) |
| { |
| g_return_if_fail (decoder != NULL); |
| |
| if (decoder->cancellable) { |
| g_cancellable_cancel (decoder->cancellable); |
| g_object_unref (decoder->cancellable); |
| decoder->cancellable = NULL; |
| } |
| |
| if (decoder->socket) { |
| g_object_unref (decoder->socket); |
| decoder->socket = NULL; |
| } |
| |
| g_clear_error (&decoder->error); |
| |
| if (decoder->data) |
| g_free (decoder->data); |
| |
| g_free (decoder); |
| } |
| |
| gboolean |
| rfb_decoder_connect_tcp (RfbDecoder * decoder, gchar * host, guint port) |
| { |
| GError *err = NULL; |
| GInetAddress *addr; |
| GSocketAddress *saddr; |
| GResolver *resolver; |
| |
| GST_DEBUG ("connecting to the rfb server"); |
| |
| g_return_val_if_fail (decoder != NULL, FALSE); |
| g_return_val_if_fail (decoder->socket == NULL, FALSE); |
| g_return_val_if_fail (host != NULL, FALSE); |
| |
| /* look up name if we need to */ |
| addr = g_inet_address_new_from_string (host); |
| if (!addr) { |
| GList *results; |
| |
| resolver = g_resolver_get_default (); |
| |
| results = |
| g_resolver_lookup_by_name (resolver, host, decoder->cancellable, &err); |
| if (!results) |
| goto name_resolve; |
| addr = G_INET_ADDRESS (g_object_ref (results->data)); |
| |
| g_resolver_free_addresses (results); |
| g_object_unref (resolver); |
| } |
| |
| saddr = g_inet_socket_address_new (addr, port); |
| |
| decoder->socket = |
| g_socket_new (g_socket_address_get_family (saddr), G_SOCKET_TYPE_STREAM, |
| G_SOCKET_PROTOCOL_TCP, &err); |
| |
| if (!decoder->socket) |
| goto no_socket; |
| |
| GST_DEBUG ("opened receiving client socket"); |
| |
| if (!g_socket_connect (decoder->socket, saddr, decoder->cancellable, &err)) |
| goto connect_failed; |
| |
| g_object_unref (saddr); |
| |
| decoder->disconnected = FALSE; |
| |
| return TRUE; |
| |
| no_socket: |
| { |
| GST_WARNING ("Failed to create socket: %s", err->message); |
| if (decoder->error == NULL) |
| decoder->error = err; |
| else |
| g_clear_error (&err); |
| g_object_unref (saddr); |
| return FALSE; |
| } |
| name_resolve: |
| { |
| if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
| GST_DEBUG ("Cancelled name resolval"); |
| } else { |
| GST_WARNING ("Failed to resolve host '%s': %s", host, err->message); |
| if (decoder->error == NULL) { |
| decoder->error = err; |
| err = NULL; |
| } |
| } |
| g_clear_error (&err); |
| g_object_unref (resolver); |
| return FALSE; |
| } |
| connect_failed: |
| { |
| if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
| GST_DEBUG ("Cancelled connecting"); |
| } else { |
| GST_WARNING ("Failed to connect to host '%s:%d': %s", host, port, |
| err->message); |
| if (decoder->error == NULL) { |
| decoder->error = err; |
| err = NULL; |
| } |
| } |
| g_clear_error (&err); |
| g_object_unref (saddr); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * rfb_decoder_iterate: |
| * @decoder: The rfb context |
| * |
| * Initializes the connection with the rfb server |
| * |
| * Returns: TRUE if initialization was succesfull, FALSE on fail. |
| */ |
| gboolean |
| rfb_decoder_iterate (RfbDecoder * decoder) |
| { |
| gboolean ret; |
| |
| g_return_val_if_fail (decoder != NULL, FALSE); |
| g_return_val_if_fail (decoder->socket != NULL, FALSE); |
| |
| if (decoder->state == NULL) { |
| GST_DEBUG ("First iteration: set state to -> wait for protocol version"); |
| decoder->state = rfb_decoder_state_wait_for_protocol_version; |
| } |
| |
| GST_DEBUG ("Executing next state in initialization"); |
| ret = decoder->state (decoder); |
| |
| if (ret == FALSE) { |
| if (decoder->error == NULL) |
| GST_WARNING ("Failure, but no error stored"); |
| else |
| GST_WARNING ("Failure: %s", decoder->error->message); |
| } |
| |
| return ret; |
| } |
| |
| static guint8 * |
| rfb_decoder_read (RfbDecoder * decoder, guint32 len) |
| { |
| guint32 total = 0; |
| gssize now = 0; |
| GError *err = NULL; |
| |
| g_return_val_if_fail (decoder->socket != NULL, NULL); |
| g_return_val_if_fail (len > 0, NULL); |
| |
| if (G_UNLIKELY (len > decoder->data_len)) { |
| if (decoder->data) |
| g_free (decoder->data); |
| decoder->data = g_malloc (len); |
| decoder->data_len = len; |
| } |
| |
| while (total < len) { |
| now = g_socket_receive (decoder->socket, (gchar *) decoder->data + total, |
| len - total, decoder->cancellable, &err); |
| |
| if (now < 0) |
| goto recv_error; |
| |
| total += now; |
| } |
| return decoder->data; |
| |
| recv_error: |
| { |
| if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
| GST_DEBUG ("Read on socket cancelled"); |
| } else { |
| GST_ERROR ("Read error on socket: %s", err->message); |
| if (decoder->error == NULL) { |
| decoder->error = err; |
| err = NULL; |
| } |
| } |
| g_clear_error (&err); |
| decoder->disconnected = TRUE; |
| return NULL; |
| } |
| } |
| |
| static gint |
| rfb_decoder_send (RfbDecoder * decoder, guint8 * buffer, guint len) |
| { |
| gssize now = 0; |
| GError *err = NULL; |
| |
| g_return_val_if_fail (decoder->socket != NULL, 0); |
| g_return_val_if_fail (buffer != NULL, 0); |
| g_return_val_if_fail (len > 0, 0); |
| |
| now = g_socket_send (decoder->socket, (gchar *) buffer, len, |
| decoder->cancellable, &err); |
| |
| if (now < 0) |
| goto send_error; |
| |
| done: |
| return now; |
| |
| send_error: |
| { |
| if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
| GST_DEBUG ("Send on socket cancelled"); |
| } else { |
| GST_ERROR ("Send error on socket: %s", err->message); |
| if (decoder->error == NULL) { |
| decoder->error = err; |
| err = NULL; |
| } |
| } |
| g_clear_error (&err); |
| goto done; |
| } |
| } |
| |
| void |
| rfb_decoder_send_update_request (RfbDecoder * decoder, |
| gboolean incremental, gint x, gint y, gint width, gint height) |
| { |
| guint8 data[10]; |
| |
| g_return_if_fail (decoder != NULL); |
| g_return_if_fail (decoder->socket != NULL); |
| |
| data[0] = 3; |
| data[1] = incremental; |
| RFB_SET_UINT16 (data + 2, x); |
| RFB_SET_UINT16 (data + 4, y); |
| RFB_SET_UINT16 (data + 6, width); |
| RFB_SET_UINT16 (data + 8, height); |
| |
| rfb_decoder_send (decoder, data, 10); |
| |
| /* create a backup of the prev frame for copyrect encoding */ |
| if (decoder->use_copyrect) { |
| memcpy (decoder->prev_frame, decoder->frame, |
| decoder->rect_width * decoder->rect_height * decoder->bpp / 8); |
| } |
| |
| decoder->state = rfb_decoder_state_normal; |
| } |
| |
| void |
| rfb_decoder_send_key_event (RfbDecoder * decoder, guint key, gboolean down_flag) |
| { |
| guint8 data[8]; |
| |
| g_return_if_fail (decoder != NULL); |
| g_return_if_fail (decoder->socket != NULL); |
| |
| data[0] = 4; |
| data[1] = down_flag; |
| RFB_SET_UINT16 (data + 2, 0); |
| RFB_SET_UINT32 (data + 4, key); |
| |
| rfb_decoder_send (decoder, data, 8); |
| } |
| |
| void |
| rfb_decoder_send_pointer_event (RfbDecoder * decoder, |
| gint button_mask, gint x, gint y) |
| { |
| guint8 data[6]; |
| |
| g_return_if_fail (decoder != NULL); |
| g_return_if_fail (decoder->socket != NULL); |
| |
| data[0] = 5; |
| data[1] = button_mask; |
| RFB_SET_UINT16 (data + 2, x); |
| RFB_SET_UINT16 (data + 4, y); |
| |
| rfb_decoder_send (decoder, data, 6); |
| } |
| |
| /** |
| * rfb_decoder_state_wait_for_protocol_version: |
| * |
| * Negotiate the rfb version used |
| * |
| * \TODO Support for versions 3.7 and 3.8 |
| */ |
| static gboolean |
| rfb_decoder_state_wait_for_protocol_version (RfbDecoder * decoder) |
| { |
| rfb_decoder_read (decoder, 12); |
| |
| g_return_val_if_fail (memcmp (decoder->data, "RFB 003.00", 10) == 0, FALSE); |
| g_return_val_if_fail (*(decoder->data + 11) == 0x0a, FALSE); |
| |
| GST_DEBUG ("\"%.11s\"", decoder->data); |
| *(decoder->data) = 0x00; |
| *(decoder->data + 11) = 0x00; |
| decoder->protocol_major = atoi ((char *) (decoder->data + 4)); |
| decoder->protocol_minor = atoi ((char *) (decoder->data + 8)); |
| GST_DEBUG ("Major version : %d", decoder->protocol_major); |
| GST_DEBUG ("Minor version : %d", decoder->protocol_minor); |
| |
| if (decoder->protocol_major != 3) { |
| GST_INFO |
| ("A major protocol version of %d is not supported, falling back to 3", |
| decoder->protocol_major); |
| decoder->protocol_major = 3; |
| } |
| switch (decoder->protocol_minor) { |
| case 3: |
| break; |
| default: |
| GST_INFO ("Minor version %d is not supported, using 3", |
| decoder->protocol_minor); |
| decoder->protocol_minor = 3; |
| } |
| rfb_decoder_send (decoder, (guint8 *) "RFB 003.003\n", 12); |
| |
| decoder->state = rfb_decoder_state_wait_for_security; |
| return TRUE; |
| } |
| |
| /* |
| * a string describing the reason (where a string is specified as a length |
| * followed by that many ASCII characters) |
| **/ |
| static gboolean |
| rfb_decoder_state_reason (RfbDecoder * decoder) |
| { |
| gint reason_length; |
| |
| rfb_decoder_read (decoder, 4); |
| |
| reason_length = RFB_GET_UINT32 (decoder->data); |
| rfb_decoder_read (decoder, reason_length); |
| GST_WARNING ("Reason by server: %s", decoder->data); |
| |
| if (decoder->error == NULL) { |
| decoder->error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, |
| "VNC server error: %s", decoder->data); |
| } |
| |
| return FALSE; |
| } |
| |
| static gboolean |
| rfb_decoder_state_wait_for_security (RfbDecoder * decoder) |
| { |
| /* |
| * Version 3.3 The server decides the security type and sends a single word |
| * |
| * The security-type may only take the value 0, 1 or 2. A value of 0 means that the |
| * connection has failed and is followed by a string giving the reason, as described |
| * above. |
| */ |
| if (IS_VERSION_3_3 (decoder)) { |
| rfb_decoder_read (decoder, 4); |
| |
| decoder->security_type = RFB_GET_UINT32 (decoder->data); |
| GST_DEBUG ("security = %d", decoder->security_type); |
| |
| g_return_val_if_fail (decoder->security_type < 3, FALSE); |
| g_return_val_if_fail (decoder->security_type != SECURITY_FAIL, |
| rfb_decoder_state_reason (decoder)); |
| } else { |
| /* \TODO Add behavior for the rfb 3.7 and 3.8 servers */ |
| GST_WARNING ("Other versions are not yet supported"); |
| } |
| |
| switch (decoder->security_type) { |
| case SECURITY_NONE: |
| GST_DEBUG ("Security type is None"); |
| if (IS_VERSION_3_8 (decoder)) { |
| decoder->state = rfb_decoder_state_security_result; |
| } else { |
| decoder->state = rfb_decoder_state_send_client_initialisation; |
| } |
| break; |
| case SECURITY_VNC:{ |
| unsigned char key[8], *challenge; |
| DESContext des_ctx; |
| gsize password_len; |
| |
| /* |
| * VNC authentication is to be used and protocol data is to be sent |
| * unencrypted. The server sends a random 16-byte challenge |
| */ |
| GST_DEBUG ("Security type is VNC Authentication"); |
| /* VNC Authentication can't be used if the password is not set */ |
| if (!decoder->password) { |
| GST_WARNING |
| ("VNC Authentication can't be used if the password is not set"); |
| decoder->error = |
| g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, |
| "VNC servers needs authentication, but no password set"); |
| return FALSE; |
| } |
| |
| /* key is 8 bytes and made up of password (padded with 0s if needed) */ |
| memset (key, 0, 8); |
| password_len = strlen (decoder->password); |
| memcpy (key, decoder->password, MIN (password_len, 8)); |
| |
| /* read challenge */ |
| challenge = rfb_decoder_read (decoder, 16); |
| if (challenge == NULL) |
| return FALSE; |
| |
| /* encrypt 16 challenge bytes in place using key */ |
| memset (&des_ctx, 0, sizeof (DESContext)); |
| deskey (&des_ctx, key, EN0); |
| des (&des_ctx, challenge, challenge); |
| des (&des_ctx, challenge + 8, challenge + 8); |
| |
| /* .. and send back to server */ |
| rfb_decoder_send (decoder, challenge, 16); |
| |
| GST_DEBUG ("Encrypted challenge sent to server"); |
| |
| decoder->state = rfb_decoder_state_security_result; |
| break; |
| } |
| default: |
| GST_WARNING ("Security type is not known"); |
| return FALSE; |
| break; |
| } |
| return TRUE; |
| } |
| |
| /* |
| * The server sends a word to inform the client whether the security handshaking was |
| * successful. |
| */ |
| static gboolean |
| rfb_decoder_state_security_result (RfbDecoder * decoder) |
| { |
| rfb_decoder_read (decoder, 4); |
| if (RFB_GET_UINT32 (decoder->data) != 0) { |
| GST_WARNING ("Security handshaking failed"); |
| if (IS_VERSION_3_8 (decoder)) { |
| decoder->state = rfb_decoder_state_reason; |
| return TRUE; |
| } |
| if (decoder->error == NULL) { |
| decoder->error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, |
| "authentication failed"); |
| } |
| return FALSE; |
| } |
| |
| GST_DEBUG ("Security handshaking succesful"); |
| decoder->state = rfb_decoder_state_send_client_initialisation; |
| |
| return TRUE; |
| } |
| |
| static guint8 * |
| rfb_decoder_message_set_encodings (GSList * encodings_list) |
| { |
| |
| guint8 *message = g_malloc0 (4 + 4 * g_slist_length (encodings_list)); |
| guint32 *encoding_type; |
| |
| message[0] = 0x02; /* message type */ |
| RFB_SET_UINT16 (message + 2, g_slist_length (encodings_list)); /* number of encodings */ |
| |
| /* write all the encoding types */ |
| encoding_type = (guint32 *) (message + 4); |
| |
| while (encodings_list) { |
| RFB_SET_UINT32 (encoding_type, GPOINTER_TO_UINT (encodings_list->data)); |
| encoding_type++; |
| encodings_list = encodings_list->next; |
| } |
| |
| return message; |
| } |
| |
| /* |
| * rfb_decoder_state_set_encodings: |
| * @decoder: The rfb context |
| * |
| * Sends the encoding types that the client can decode to the server |
| * |
| * Returns: TRUE if initialization was succesfull, FALSE on fail. |
| */ |
| static gboolean |
| rfb_decoder_state_set_encodings (RfbDecoder * decoder) |
| { |
| GSList *encoder_list = NULL; |
| guint8 *message; |
| |
| GST_DEBUG ("entered set encodings"); |
| |
| encoder_list = |
| g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_HEXTILE)); |
| encoder_list = |
| g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_CORRE)); |
| encoder_list = |
| g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_RRE)); |
| if (decoder->use_copyrect) { |
| encoder_list = |
| g_slist_append (encoder_list, |
| GUINT_TO_POINTER (ENCODING_TYPE_COPYRECT)); |
| } |
| encoder_list = |
| g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_RAW)); |
| |
| message = rfb_decoder_message_set_encodings (encoder_list); |
| |
| rfb_decoder_send (decoder, message, 4 + 4 * g_slist_length (encoder_list)); |
| |
| g_free (message); |
| |
| decoder->state = rfb_decoder_state_normal; |
| decoder->inited = TRUE; |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| rfb_decoder_state_send_client_initialisation (RfbDecoder * decoder) |
| { |
| guint8 shared_flag; |
| |
| shared_flag = decoder->shared_flag; |
| rfb_decoder_send (decoder, &shared_flag, 1); |
| GST_DEBUG ("shared_flag is %d", shared_flag); |
| |
| decoder->state = rfb_decoder_state_wait_for_server_initialisation; |
| return TRUE; |
| } |
| |
| static gboolean |
| rfb_decoder_state_wait_for_server_initialisation (RfbDecoder * decoder) |
| { |
| guint32 name_length; |
| |
| rfb_decoder_read (decoder, 24); |
| |
| decoder->width = RFB_GET_UINT16 (decoder->data + 0); |
| decoder->height = RFB_GET_UINT16 (decoder->data + 2); |
| decoder->bpp = RFB_GET_UINT8 (decoder->data + 4); |
| decoder->depth = RFB_GET_UINT8 (decoder->data + 5); |
| decoder->big_endian = RFB_GET_UINT8 (decoder->data + 6); |
| decoder->true_colour = RFB_GET_UINT8 (decoder->data + 7); |
| decoder->red_max = RFB_GET_UINT16 (decoder->data + 8); |
| decoder->green_max = RFB_GET_UINT16 (decoder->data + 10); |
| decoder->blue_max = RFB_GET_UINT16 (decoder->data + 12); |
| decoder->red_shift = RFB_GET_UINT8 (decoder->data + 14); |
| decoder->green_shift = RFB_GET_UINT8 (decoder->data + 15); |
| decoder->blue_shift = RFB_GET_UINT8 (decoder->data + 16); |
| |
| GST_DEBUG ("Server Initialization"); |
| GST_DEBUG ("width = %d", decoder->width); |
| GST_DEBUG ("height = %d", decoder->height); |
| GST_DEBUG ("bpp = %d", decoder->bpp); |
| GST_DEBUG ("depth = %d", decoder->depth); |
| GST_DEBUG ("big_endian = %d", decoder->big_endian); |
| GST_DEBUG ("true_colour= %d", decoder->true_colour); |
| GST_DEBUG ("red_max = %d", decoder->red_max); |
| GST_DEBUG ("green_max = %d", decoder->green_max); |
| GST_DEBUG ("blue_max = %d", decoder->blue_max); |
| GST_DEBUG ("red_shift = %d", decoder->red_shift); |
| GST_DEBUG ("green_shift= %d", decoder->green_shift); |
| GST_DEBUG ("blue_shift = %d", decoder->blue_shift); |
| |
| name_length = RFB_GET_UINT32 (decoder->data + 20); |
| |
| rfb_decoder_read (decoder, name_length); |
| |
| decoder->name = g_strndup ((gchar *) (decoder->data), name_length); |
| GST_DEBUG ("name = %s", decoder->name); |
| |
| /* check if we need cropping */ |
| |
| if (decoder->offset_x > 0) { |
| if (decoder->offset_x > decoder->width) { |
| GST_WARNING |
| ("Trying to crop more than the width of the server. Setting offset-x to 0."); |
| decoder->offset_x = 0; |
| } else { |
| decoder->width -= decoder->offset_x; |
| } |
| } |
| if (decoder->offset_y > 0) { |
| if (decoder->offset_y > decoder->height) { |
| GST_WARNING |
| ("Trying to crop more than the height of the server. Setting offset-y to 0."); |
| decoder->offset_y = 0; |
| } else { |
| decoder->height -= decoder->offset_y; |
| } |
| } |
| if (decoder->rect_width > 0) { |
| if (decoder->rect_width > decoder->width) { |
| GST_WARNING |
| ("Trying to crop more than the width of the server. Setting width to %u.", |
| decoder->width); |
| decoder->rect_width = decoder->width; |
| } else { |
| decoder->width = decoder->rect_width; |
| } |
| } |
| if (decoder->rect_height > 0) { |
| if (decoder->rect_height > decoder->height) { |
| GST_WARNING |
| ("Trying to crop more than the height of the server. Setting height to %u.", |
| decoder->height); |
| decoder->rect_height = decoder->height; |
| } else { |
| decoder->height = decoder->rect_height; |
| } |
| } |
| |
| decoder->state = rfb_decoder_state_set_encodings; |
| return TRUE; |
| } |
| |
| static gboolean |
| rfb_decoder_state_normal (RfbDecoder * decoder) |
| { |
| gint message_type; |
| |
| GST_DEBUG ("decoder_state_normal"); |
| |
| rfb_decoder_read (decoder, 1); |
| message_type = RFB_GET_UINT8 (decoder->data); |
| |
| switch (message_type) { |
| case MESSAGE_TYPE_FRAMEBUFFER_UPDATE: |
| GST_DEBUG ("Receiving framebuffer update"); |
| decoder->state = rfb_decoder_state_framebuffer_update; |
| break; |
| case 1: |
| decoder->state = rfb_decoder_state_set_colour_map_entries; |
| break; |
| case 2: |
| /* bell, ignored */ |
| decoder->state = rfb_decoder_state_normal; |
| break; |
| case 3: |
| decoder->state = rfb_decoder_state_server_cut_text; |
| break; |
| default: |
| g_critical ("unknown message type %d", message_type); |
| } |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| rfb_decoder_state_framebuffer_update (RfbDecoder * decoder) |
| { |
| |
| rfb_decoder_read (decoder, 3); |
| |
| decoder->n_rects = RFB_GET_UINT16 (decoder->data + 1); |
| GST_DEBUG ("Number of rectangles : %d", decoder->n_rects); |
| |
| decoder->state = rfb_decoder_state_framebuffer_update_rectangle; |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| rfb_decoder_state_framebuffer_update_rectangle (RfbDecoder * decoder) |
| { |
| gint x, y, w, h; |
| gint encoding; |
| |
| rfb_decoder_read (decoder, 12); |
| |
| x = RFB_GET_UINT16 (decoder->data + 0) - decoder->offset_x; |
| y = RFB_GET_UINT16 (decoder->data + 2) - decoder->offset_y; |
| w = RFB_GET_UINT16 (decoder->data + 4); |
| h = RFB_GET_UINT16 (decoder->data + 6); |
| encoding = RFB_GET_UINT32 (decoder->data + 8); |
| |
| GST_DEBUG ("update recieved"); |
| GST_DEBUG ("x:%d y:%d", x, y); |
| GST_DEBUG ("w:%d h:%d", w, h); |
| GST_DEBUG ("encoding: %d", encoding); |
| |
| if (((w * h) + (x * y)) > (decoder->width * decoder->height)) { |
| GST_ERROR ("Desktop resize is unsupported."); |
| decoder->state = NULL; |
| decoder->disconnected = TRUE; |
| return TRUE; |
| } |
| |
| switch (encoding) { |
| case ENCODING_TYPE_RAW: |
| rfb_decoder_raw_encoding (decoder, x, y, w, h); |
| break; |
| case ENCODING_TYPE_COPYRECT: |
| rfb_decoder_copyrect_encoding (decoder, x, y, w, h); |
| break; |
| case ENCODING_TYPE_RRE: |
| rfb_decoder_rre_encoding (decoder, x, y, w, h); |
| break; |
| case ENCODING_TYPE_CORRE: |
| rfb_decoder_corre_encoding (decoder, x, y, w, h); |
| break; |
| case ENCODING_TYPE_HEXTILE: |
| rfb_decoder_hextile_encoding (decoder, x, y, w, h); |
| break; |
| default: |
| g_critical ("unimplemented encoding\n"); |
| break; |
| } |
| decoder->n_rects--; |
| if (decoder->n_rects == 0 || decoder->disconnected) { |
| decoder->state = NULL; |
| } else { |
| decoder->state = rfb_decoder_state_framebuffer_update_rectangle; |
| } |
| return TRUE; |
| } |
| |
| static void |
| rfb_decoder_raw_encoding (RfbDecoder * decoder, gint start_x, gint start_y, |
| gint rect_w, gint rect_h) |
| { |
| gint size; |
| guint8 *frame, *p; |
| guint32 raw_line_size; |
| |
| raw_line_size = rect_w * decoder->bytespp; |
| size = rect_h * raw_line_size; |
| GST_DEBUG ("Reading %d bytes (%dx%d)", size, rect_w, rect_h); |
| rfb_decoder_read (decoder, size); |
| |
| frame = |
| decoder->frame + (((start_y * decoder->rect_width) + |
| start_x) * decoder->bytespp); |
| p = decoder->data; |
| |
| while (rect_h--) { |
| memcpy (frame, p, raw_line_size); |
| p += raw_line_size; |
| frame += decoder->line_size; |
| } |
| } |
| |
| static void |
| rfb_decoder_copyrect_encoding (RfbDecoder * decoder, gint start_x, gint start_y, |
| gint rect_w, gint rect_h) |
| { |
| guint16 src_x, src_y; |
| gint line_width, copyrect_width; |
| guint8 *src, *dst; |
| |
| rfb_decoder_read (decoder, 4); |
| |
| /* don't forget the offset */ |
| src_x = RFB_GET_UINT16 (decoder->data) - decoder->offset_x; |
| src_y = RFB_GET_UINT16 (decoder->data + 2) - decoder->offset_y; |
| GST_DEBUG ("Copyrect from %d %d", src_x, src_y); |
| |
| copyrect_width = rect_w * decoder->bytespp; |
| line_width = decoder->line_size; |
| src = |
| decoder->prev_frame + ((src_y * decoder->rect_width) + |
| src_x) * decoder->bytespp; |
| dst = |
| decoder->frame + ((start_y * decoder->rect_width) + |
| start_x) * decoder->bytespp; |
| |
| while (rect_h--) { |
| memcpy (dst, src, copyrect_width); |
| src += line_width; |
| dst += line_width; |
| } |
| } |
| |
| static void |
| rfb_decoder_fill_rectangle (RfbDecoder * decoder, gint x, gint y, gint w, |
| gint h, guint32 color) |
| { |
| /* fill the whole region with the same color */ |
| |
| guint32 *offset; |
| gint i, j; |
| |
| for (i = 0; i < h; i++) { |
| offset = |
| (guint32 *) (decoder->frame + ((x + (y + |
| i) * decoder->rect_width)) * decoder->bytespp); |
| for (j = 0; j < w; j++) { |
| *(offset++) = color; |
| } |
| } |
| } |
| |
| static void |
| rfb_decoder_rre_encoding (RfbDecoder * decoder, gint start_x, gint start_y, |
| gint rect_w, gint rect_h) |
| { |
| guint32 number_of_rectangles, color; |
| guint16 x, y, w, h; |
| |
| rfb_decoder_read (decoder, 4 + decoder->bytespp); |
| number_of_rectangles = RFB_GET_UINT32 (decoder->data); |
| color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + 4))); |
| |
| GST_DEBUG ("number of rectangles :%d", number_of_rectangles); |
| |
| /* color the background of this rectangle */ |
| rfb_decoder_fill_rectangle (decoder, start_x, start_y, rect_w, rect_h, color); |
| |
| while (number_of_rectangles--) { |
| |
| rfb_decoder_read (decoder, decoder->bytespp + 8); |
| color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data))); |
| x = RFB_GET_UINT16 (decoder->data + decoder->bytespp); |
| y = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 2); |
| w = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 4); |
| h = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 6); |
| |
| /* draw the rectangle in the foreground */ |
| rfb_decoder_fill_rectangle (decoder, start_x + x, start_y + y, w, h, color); |
| } |
| } |
| |
| static void |
| rfb_decoder_corre_encoding (RfbDecoder * decoder, gint start_x, gint start_y, |
| gint rect_w, gint rect_h) |
| { |
| guint32 number_of_rectangles, color; |
| guint8 x, y, w, h; |
| |
| rfb_decoder_read (decoder, 4 + decoder->bytespp); |
| number_of_rectangles = RFB_GET_UINT32 (decoder->data); |
| color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + 4))); |
| g_free (decoder->data); |
| |
| GST_DEBUG ("number of rectangles :%d", number_of_rectangles); |
| |
| /* color the background of this rectangle */ |
| rfb_decoder_fill_rectangle (decoder, start_x, start_y, rect_w, rect_h, color); |
| |
| while (number_of_rectangles--) { |
| |
| rfb_decoder_read (decoder, decoder->bytespp + 4); |
| color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data))); |
| x = RFB_GET_UINT8 (decoder->data + decoder->bytespp); |
| y = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 1); |
| w = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 2); |
| h = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 3); |
| |
| /* draw the rectangle in the foreground */ |
| rfb_decoder_fill_rectangle (decoder, start_x + x, start_y + y, w, h, color); |
| |
| g_free (decoder->data); |
| } |
| } |
| |
| static void |
| rfb_decoder_hextile_encoding (RfbDecoder * decoder, gint start_x, gint start_y, |
| gint rect_w, gint rect_h) |
| { |
| gint32 x, x_count G_GNUC_UNUSED, x_end, x_max, x_max_16; |
| gint32 y, y_count G_GNUC_UNUSED, y_end, y_max, y_max_16; |
| guint8 subencoding, nr_subrect, xy, wh; |
| guint32 background, foreground; |
| |
| foreground = background = 0; |
| x_end = rect_w % 16; |
| x_count = rect_w / 16 + (x_end > 0 ? 1 : 0); |
| y_end = rect_h % 16; |
| y_count = rect_h / 16 + (y_end > 0 ? 1 : 0); |
| x_max = start_x + rect_w; |
| y_max = start_y + rect_h; |
| x_max_16 = x_max - 16; |
| y_max_16 = y_max - 16; |
| |
| for (y = start_y; y < y_max; y += 16) { |
| for (x = start_x; x < x_max; x += 16) { |
| |
| rfb_decoder_read (decoder, 1); |
| subencoding = RFB_GET_UINT8 (decoder->data); |
| |
| if (subencoding & SUBENCODING_RAW) { |
| rfb_decoder_raw_encoding (decoder, x, y, |
| (x <= x_max_16 ? 16 : x_end), (y <= y_max_16 ? 16 : y_end)); |
| continue; |
| } |
| |
| if (subencoding & SUBENCODING_BACKGROUND) { |
| rfb_decoder_read (decoder, decoder->bytespp); |
| background = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data))); |
| } |
| rfb_decoder_fill_rectangle (decoder, x, y, |
| (x <= x_max_16 ? 16 : x_end), (y <= y_max_16 ? 16 : y_end), |
| background); |
| |
| if (subencoding & SUBENCODING_FOREGROUND) { |
| rfb_decoder_read (decoder, decoder->bytespp); |
| foreground = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data))); |
| } |
| |
| if (subencoding & SUBENCODING_ANYSUBRECTS) { |
| rfb_decoder_read (decoder, 1); |
| nr_subrect = RFB_GET_UINT8 (decoder->data); |
| } else { |
| continue; |
| } |
| |
| if (subencoding & SUBENCODING_SUBRECTSCOLORED) { |
| guint offset = 0; |
| |
| rfb_decoder_read (decoder, nr_subrect * (2 + decoder->bytespp)); |
| |
| while (nr_subrect--) { |
| foreground = |
| GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + offset))); |
| offset += decoder->bytespp; |
| xy = RFB_GET_UINT8 (decoder->data + offset++); |
| wh = RFB_GET_UINT8 (decoder->data + offset++); |
| rfb_decoder_fill_rectangle (decoder, x + (xy >> 4), y + (xy & 0xF), |
| 1 + (wh >> 4), 1 + (wh & 0xF), foreground); |
| } |
| } else { |
| guint offset = 0; |
| |
| rfb_decoder_read (decoder, 2 * nr_subrect); |
| |
| while (nr_subrect--) { |
| xy = RFB_GET_UINT8 (decoder->data + offset++); |
| wh = RFB_GET_UINT8 (decoder->data + offset++); |
| rfb_decoder_fill_rectangle (decoder, x + (xy >> 4), y + (xy & 0xF), |
| 1 + (wh >> 4), 1 + (wh & 0xF), foreground); |
| } |
| } |
| } |
| } |
| } |
| |
| static gboolean |
| rfb_decoder_state_set_colour_map_entries (RfbDecoder * decoder) |
| { |
| g_critical ("not implemented"); |
| |
| return FALSE; |
| } |
| |
| static gboolean |
| rfb_decoder_state_server_cut_text (RfbDecoder * decoder) |
| { |
| gint cut_text_length; |
| |
| /* 3 bytes padding, 4 bytes cut_text_length */ |
| rfb_decoder_read (decoder, 7); |
| cut_text_length = RFB_GET_UINT32 (decoder->data + 3); |
| |
| rfb_decoder_read (decoder, cut_text_length); |
| GST_DEBUG ("rfb_decoder_state_server_cut_text: throw away '%s'", |
| decoder->data); |
| |
| decoder->state = rfb_decoder_state_normal; |
| return TRUE; |
| } |