Esempio n. 1
0
void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {

    guac_client* client = ((rdp_freerdp_context*) context)->client;
    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;

    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
    pthread_mutex_lock(&(data->update_lock));

    switch (dstblt->bRop) {

        /* Blackness */
        case 0:

            /* Send black rectangle */
            guac_protocol_send_rect(client->socket, current_layer,
                    dstblt->nLeftRect, dstblt->nTopRect,
                    dstblt->nWidth, dstblt->nHeight);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    0, 0, 0, 255);

            break;

        /* Unsupported ROP3 */
        default:
            guac_client_log_info(client,
                    "guac_rdp_gdi_dstblt(rop3=%i)", dstblt->bRop);

    }

    pthread_mutex_unlock(&(data->update_lock));

}
Esempio n. 2
0
void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect) {

    guac_client* client = ((rdp_freerdp_context*) context)->client;
    uint32 color = freerdp_color_convert_var(opaque_rect->color,
            context->instance->settings->color_depth, 32,
            ((rdp_freerdp_context*) context)->clrconv);

    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;

    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
    pthread_mutex_lock(&(data->update_lock));

    int x = opaque_rect->nLeftRect;
    int y = opaque_rect->nTopRect;
    int w = opaque_rect->nWidth;
    int h = opaque_rect->nHeight;

    /* Clip operation to bounds */
    __guac_rdp_clip_rect(data, &x, &y, &w, &h);

    guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

    guac_protocol_send_cfill(client->socket,
            GUAC_COMP_OVER, current_layer,
            (color >> 16) & 0xFF,
            (color >> 8 ) & 0xFF,
            (color      ) & 0xFF,
            255);

    pthread_mutex_unlock(&(data->update_lock));

}
Esempio n. 3
0
void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect) {

    guac_client* client = ((rdp_freerdp_context*) context)->client;
    UINT32 color = freerdp_color_convert_var(opaque_rect->color,
            context->instance->settings->ColorDepth, 32,
            ((rdp_freerdp_context*) context)->clrconv);

    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;

    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
    pthread_mutex_lock(&(data->update_lock));

    guac_protocol_send_rect(client->socket, current_layer,
            opaque_rect->nLeftRect, opaque_rect->nTopRect,
            opaque_rect->nWidth, opaque_rect->nHeight);

    guac_protocol_send_cfill(client->socket,
            GUAC_COMP_OVER, current_layer,
            (color >> 16) & 0xFF,
            (color >> 8 ) & 0xFF,
            (color      ) & 0xFF,
            255);

    pthread_mutex_unlock(&(data->update_lock));

}
Esempio n. 4
0
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {

    guac_client* client = ((rdp_freerdp_context*) context)->client;
    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;

    /* Reset clip */
    guac_protocol_send_reset(client->socket, current_layer);

    /* Set clip if specified */
    if (bounds != NULL) {
        guac_protocol_send_rect(client->socket, current_layer,
                bounds->left, bounds->top,
                bounds->right - bounds->left + 1,
                bounds->bottom - bounds->top + 1);

        guac_protocol_send_clip(client->socket, current_layer);
    }

    /* If no bounds given, clear bounding rect */
    if (bounds == NULL)
        ((rdp_guac_client_data*)client->data)->bounded = false;

    /* Otherwise, set bounding rectangle */
    else {
        ((rdp_guac_client_data*)client->data)->bounded = true;
        ((rdp_guac_client_data*)client->data)->bounds_left   = bounds->left;
        ((rdp_guac_client_data*)client->data)->bounds_top    = bounds->top;
        ((rdp_guac_client_data*)client->data)->bounds_right  = bounds->right;
        ((rdp_guac_client_data*)client->data)->bounds_bottom = bounds->bottom;    
    }

}
Esempio n. 5
0
/**
 * Clears the currently-selected region, removing the highlight.
 */
static void __guac_terminal_display_clear_select(guac_terminal_display* display) {

    guac_socket* socket = display->client->socket;
    guac_layer* select_layer = display->select_layer;

    guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1);
    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer,
            0x00, 0x00, 0x00, 0x00);

    guac_client_end_frame(display->client);
    guac_socket_flush(socket);

    /* Text is no longer selected */
    display->text_selected =
    display->selection_committed = false;

}
Esempio n. 6
0
void guac_terminal_display_clear_select(guac_terminal_display* display) {

    /* Do nothing if nothing is selected */
    if (!display->text_selected)
        return;

    guac_socket* socket = display->client->socket;
    guac_layer* select_layer = display->select_layer;

    guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1);
    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer,
            0x00, 0x00, 0x00, 0x00);

    /* Text is no longer selected */
    display->text_selected = false;

}
Esempio n. 7
0
guac_ssh_cursor* guac_ssh_create_blank(guac_client* client) {

    guac_socket* socket = client->socket;
    guac_ssh_cursor* cursor = guac_ssh_cursor_alloc(client);

    /* Set buffer to a single 1x1 transparent rectangle */
    guac_protocol_send_rect(socket, cursor->buffer, 0, 0, 1, 1);
    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, cursor->buffer,
            0x00, 0x00, 0x00, 0x00);

    /* Initialize cursor properties */
    cursor->width  = 1;
    cursor->height = 1;
    cursor->hotspot_x = 0;
    cursor->hotspot_y = 0;

    return cursor;

}
/**
 * Resizes and redraws the handle layer of the scrollbar according to the given
 * scrollbar render state, sending any necessary Guacamole instructions over
 * the given socket. The handle is the portion of the scrollbar that indicates
 * the current scroll value and which the user can click and drag to change the
 * value.
 *
 * @param scrollbar
 *     The scrollbar associated with the handle being resized and redrawn.
 *
 * @param state
 *     The guac_terminal_scrollbar_render_state describing the new scrollbar
 *     handle size and appearance.
 *
 * @param socket
 *     The guac_socket over which any instructions necessary to perform the
 *     render operation should be sent.
 */
static void guac_terminal_scrollbar_draw_handle(
        guac_terminal_scrollbar* scrollbar,
        guac_terminal_scrollbar_render_state* state,
        guac_socket* socket) {

    /* Set handle size */
    guac_protocol_send_size(socket, scrollbar->handle,
            state->handle_width,
            state->handle_height);

    /* Fill handle with solid color */
    guac_protocol_send_rect(socket, scrollbar->handle, 0, 0,
            state->handle_width,
            state->handle_height);

    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->handle,
            0xA0, 0xA0, 0xA0, 0x8F);

}
/**
 * Resizes and redraws the main scrollbar layer according to the given
 * scrollbar render state, sending any necessary Guacamole instructions over
 * the given socket.
 *
 * @param scrollbar
 *     The scrollbar to resize and redraw.
 *
 * @param state
 *     The guac_terminal_scrollbar_render_state describing the new scrollbar
 *     size and appearance.
 *
 * @param socket
 *     The guac_socket over which any instructions necessary to perform the
 *     render operation should be sent.
 */
static void guac_terminal_scrollbar_draw_container(
        guac_terminal_scrollbar* scrollbar,
        guac_terminal_scrollbar_render_state* state,
        guac_socket* socket) {

    /* Set container size */
    guac_protocol_send_size(socket, scrollbar->container,
            state->container_width,
            state->container_height);

    /* Fill container with solid color */
    guac_protocol_send_rect(socket, scrollbar->container, 0, 0,
            state->container_width,
            state->container_height);

    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->container,
            0x80, 0x80, 0x80, 0x40);

}
Esempio n. 10
0
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {

    guac_client* client = ((rdp_freerdp_context*) context)->client;
    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;

    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
    pthread_mutex_lock(&(data->update_lock));

    /* Reset clip */
    guac_protocol_send_reset(client->socket, current_layer);

    /* Set clip if specified */
    if (bounds != NULL) {
        guac_protocol_send_rect(client->socket, current_layer,
                bounds->left, bounds->top,
                bounds->right - bounds->left + 1,
                bounds->bottom - bounds->top + 1);

        guac_protocol_send_clip(client->socket, current_layer);
    }

    pthread_mutex_unlock(&(data->update_lock));

}
Esempio n. 11
0
void guac_terminal_scrollbar_flush(guac_terminal_scrollbar* scrollbar) {

    guac_socket* socket = scrollbar->client->socket;

    /* Get old state */
    int old_value = scrollbar->value;
    guac_terminal_scrollbar_render_state* old_state = &scrollbar->render_state;

    /* Calculate new state */
    int new_value;
    guac_terminal_scrollbar_render_state new_state;
    calculate_state(scrollbar, &new_state, &new_value);

    /* Notify of scroll if value is changing */
    if (new_value != old_value && scrollbar->scroll_handler)
        scrollbar->scroll_handler(scrollbar, new_value);

    /* Reposition container if moved */
    if (old_state->container_x != new_state.container_x
     || old_state->container_y != new_state.container_y) {

        guac_protocol_send_move(socket,
                scrollbar->container, scrollbar->parent,
                new_state.container_x,
                new_state.container_y,
                0);

    }

    /* Resize and redraw container if size changed */
    if (old_state->container_width  != new_state.container_width
     || old_state->container_height != new_state.container_height) {

        /* Set new size */
        guac_protocol_send_size(socket, scrollbar->container,
                new_state.container_width,
                new_state.container_height);

        /* Fill container with solid color */
        guac_protocol_send_rect(socket, scrollbar->container, 0, 0,
                new_state.container_width,
                new_state.container_height);

        guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->container,
                0x40, 0x40, 0x40, 0xFF);

    }

    /* Reposition handle if moved */
    if (old_state->handle_x != new_state.handle_x
     || old_state->handle_y != new_state.handle_y) {

        guac_protocol_send_move(socket,
                scrollbar->handle, scrollbar->container,
                new_state.handle_x,
                new_state.handle_y,
                0);

    }

    /* Resize and redraw handle if size changed */
    if (old_state->handle_width  != new_state.handle_width
     || old_state->handle_height != new_state.handle_height) {

        /* Send new size */
        guac_protocol_send_size(socket, scrollbar->handle,
                new_state.handle_width,
                new_state.handle_height);

        /* Fill and stroke handle with solid color */
        guac_protocol_send_rect(socket, scrollbar->handle, 0, 0,
                new_state.handle_width,
                new_state.handle_height);

        guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->handle,
                0x80, 0x80, 0x80, 0xFF);

        guac_protocol_send_cstroke(socket, GUAC_COMP_OVER, scrollbar->handle,
                GUAC_LINE_CAP_SQUARE, GUAC_LINE_JOIN_MITER, 2,
                0xA0, 0xA0, 0xA0, 0xFF);

    }

    /* Store current render state */
    scrollbar->render_state = new_state;

}
Esempio n. 12
0
void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {

    guac_client* client = ((rdp_freerdp_context*) context)->client;
    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
    guac_socket* socket = client->socket;
    guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap;

    int x = memblt->nLeftRect;
    int y = memblt->nTopRect;
    int w = memblt->nWidth;
    int h = memblt->nHeight;

    int x_src = memblt->nXSrc;
    int y_src = memblt->nYSrc;

    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
    pthread_mutex_lock(&(data->update_lock));

    /* Clip operation to bounds */
    __guac_rdp_clip_rect(data, &x, &y, &w, &h);

    /* Update source coordinates */
    x_src += x - memblt->nLeftRect;
    y_src += y - memblt->nTopRect;

    switch (memblt->bRop) {

        /* If blackness, send black rectangle */
        case 0x00:
            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    0x00, 0x00, 0x00, 0xFF);
            break;

        /* If NOP, do nothing */
        case 0xAA:
            break;

        /* If operation is just SRC, simply copy */
        case 0xCC: 

            /* If not cached, cache if necessary */
            if (((guac_rdp_bitmap*) bitmap)->layer == NULL
                    && ((guac_rdp_bitmap*) bitmap)->used >= 1)
                guac_rdp_cache_bitmap(context, memblt->bitmap);

            /* If not cached, send as PNG */
            if (bitmap->layer == NULL) {
                if (memblt->bitmap->data != NULL) {

                    /* Create surface from image data */
                    cairo_surface_t* surface = cairo_image_surface_create_for_data(
                        memblt->bitmap->data + 4*(x_src + y_src*memblt->bitmap->width),
                        CAIRO_FORMAT_RGB24, w, h, 4*memblt->bitmap->width);

                    /* Send surface to buffer */
                    guac_protocol_send_png(socket,
                            GUAC_COMP_OVER, current_layer,
                            x, y, surface);

                    /* Free surface */
                    cairo_surface_destroy(surface);

                }
            }

            /* Otherwise, copy */
            else
                guac_protocol_send_copy(socket,
                        bitmap->layer, x_src, y_src, w, h,
                        GUAC_COMP_OVER, current_layer, x, y);

            /* Increment usage counter */
            ((guac_rdp_bitmap*) bitmap)->used++;

            break;

        /* If whiteness, send white rectangle */
        case 0xFF:
            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    0xFF, 0xFF, 0xFF, 0xFF);
            break;

        /* Otherwise, use transfer */
        default:

            /* If not available as a surface, make available. */
            if (bitmap->layer == NULL)
                guac_rdp_cache_bitmap(context, memblt->bitmap);

            guac_protocol_send_transfer(socket,
                    bitmap->layer, x_src, y_src, w, h,
                    guac_rdp_rop3_transfer_function(client, memblt->bRop),
                    current_layer, x, y);

            /* Increment usage counter */
            ((guac_rdp_bitmap*) bitmap)->used++;

    }

    pthread_mutex_unlock(&(data->update_lock));

}
Esempio n. 13
0
void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {

    /*
     * Note that this is not a full implementation of PATBLT. This is a
     * fallback implementation which only renders a solid block of background
     * color using the specified ROP3 operation, ignoring whatever brush
     * was actually specified.
     *
     * As libguac-client-rdp explicitly tells the server not to send PATBLT,
     * well-behaved RDP servers will not use this operation at all, while
     * others will at least have a fallback.
     */

    /* Get client and current layer */
    guac_client* client = ((rdp_freerdp_context*) context)->client;
    const guac_layer* current_layer =
        ((rdp_guac_client_data*) client->data)->current_surface;

    int x = patblt->nLeftRect;
    int y = patblt->nTopRect;
    int w = patblt->nWidth;
    int h = patblt->nHeight;

    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;

    /* Layer for actual transfer */
    guac_layer* buffer;

    /*
     * Warn that rendering is a fallback, as the server should not be sending
     * this order.
     */
    guac_client_log_info(client, "Using fallback PATBLT (server is ignoring "
            "negotiated client capabilities)");

    /* Clip operation to bounds */
    __guac_rdp_clip_rect(data, &x, &y, &w, &h);

    /* Render rectangle based on ROP */
    switch (patblt->bRop) {

        /* If blackness, send black rectangle */
        case 0x00:
            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    0x00, 0x00, 0x00, 0xFF);
            break;

        /* If NOP, do nothing */
        case 0xAA:
            break;

        /* If operation is just a copy, send foreground only */
        case 0xCC:
        case 0xF0:
            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    (patblt->foreColor >> 16) & 0xFF,
                    (patblt->foreColor >> 8 ) & 0xFF,
                    (patblt->foreColor      ) & 0xFF,
                    0xFF);
            break;

        /* If whiteness, send white rectangle */
        case 0xFF:
            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    0xFF, 0xFF, 0xFF, 0xFF);
            break;

        /* Otherwise, invert entire rect */
        default:

            /* Allocate buffer for transfer */
            buffer = guac_client_alloc_buffer(client);

            /* Send rectangle stroke */
            guac_protocol_send_rect(client->socket, buffer,
                    0, 0, w, h);

            /* Fill rectangle with fore color only */
            guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, buffer,
                    0xFF, 0xFF, 0xFF, 0xFF);

            /* Transfer */
            guac_protocol_send_transfer(client->socket,

                    /* ... from buffer */
                    buffer, 0, 0, w, h,

                    /* ... inverting */
                    GUAC_TRANSFER_BINARY_XOR,

                    /* ... to current layer */
                    current_layer, x, y);

            /* Done with buffer */
            guac_client_free_buffer(client, buffer);

    }

}
Esempio n. 14
0
void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {

    guac_client* client = ((rdp_freerdp_context*) context)->client;
    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;

    int x = dstblt->nLeftRect;
    int y = dstblt->nTopRect;
    int w = dstblt->nWidth;
    int h = dstblt->nHeight;

    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
    pthread_mutex_lock(&(data->update_lock));

    /* Clip operation to bounds */
    __guac_rdp_clip_rect(data, &x, &y, &w, &h);

    switch (dstblt->bRop) {

        /* Blackness */
        case 0:

            /* Send black rectangle */
            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    0, 0, 0, 255);

            break;

        /* DSTINVERT */
        case 0x55:

            /* Invert */
            guac_protocol_send_transfer(client->socket,
                    current_layer, x, y, w, h,
                    GUAC_TRANSFER_BINARY_NDEST,
                    current_layer, x, y);

            break;

        /* NOP */
        case 0xAA:
            break;

        /* Whiteness */
        case 0xFF:
            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);

            guac_protocol_send_cfill(client->socket,
                    GUAC_COMP_OVER, current_layer,
                    0xFF, 0xFF, 0xFF, 0xFF);
            break;

        /* Unsupported ROP3 */
        default:
            guac_client_log_info(client,
                    "guac_rdp_gdi_dstblt(rop3=0x%x)", dstblt->bRop);

    }

    pthread_mutex_unlock(&(data->update_lock));

}
/**
 * Suspends execution for the given number of microseconds.
 *
 * @param usecs
 *     The number of microseconds to sleep for.
 */
static void streamtest_usleep(int usecs) {

    /* Calculate sleep duration in seconds and nanoseconds */
    struct timespec sleep_duration = {
        .tv_sec  =  usecs / 1000000,
        .tv_nsec = (usecs % 1000000) * 1000
    };

    /* Sleep for specified amount of time */
    nanosleep(&sleep_duration, NULL);

}

/**
 * Display a progress bar which indicates the current stream status.
 *
 * @param client
 *     The guac_client associated with the libguac-client-streamtest
 *     connection whose current status should be redrawn.
 */
static void streamtest_render_progress(guac_client* client) {

    /* Get stream state from client */
    streamtest_state* state = (streamtest_state*) client->data;

    /* Only render progress bar for audio streams */
    if (state->mode != STREAMTEST_AUDIO)
        return;

    /* Get current position within file */
    int position = lseek(state->fd, 0, SEEK_CUR);
    if (position == -1) {
        guac_client_log(client, GUAC_LOG_WARNING,
                "Unable to determine current position in stream: %s",
                strerror(errno));
        return;
    }

    /*
     * Render background
     */

    guac_protocol_send_rect(client->socket,
            GUAC_DEFAULT_LAYER,
            0, 0, STREAMTEST_PROGRESS_WIDTH, STREAMTEST_PROGRESS_HEIGHT);

    guac_protocol_send_cfill(client->socket,
            GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
            0x40, 0x40, 0x40, 0xFF);

    /*
     * Render progress
     */

    guac_protocol_send_rect(client->socket,
            GUAC_DEFAULT_LAYER, 0, 0,
            (position+1) / ((state->file_size+1) / STREAMTEST_PROGRESS_WIDTH),
            STREAMTEST_PROGRESS_HEIGHT);

    if (state->paused)
        guac_protocol_send_cfill(client->socket,
                GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
                0x80, 0x80, 0x00, 0xFF);
    else
        guac_protocol_send_cfill(client->socket,
                GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
                0x00, 0x80, 0x00, 0xFF);

}
Esempio n. 16
0
void guac_terminal_display_select(guac_terminal_display* display,
        int start_row, int start_col, int end_row, int end_col) {

    guac_socket* socket = display->client->socket;
    guac_layer* select_layer = display->select_layer;

    /* Text is now selected */
    display->text_selected = true;

    display->selection_start_row = start_row;
    display->selection_start_column = start_col;
    display->selection_end_row = end_row;
    display->selection_end_column = end_col;

    /* If single row, just need one rectangle */
    if (start_row == end_row) {

        /* Ensure proper ordering of columns */
        if (start_col > end_col) {
            int temp = start_col;
            start_col = end_col;
            end_col = temp;
        }

        /* Select characters between columns */
        guac_protocol_send_rect(socket, select_layer,

                start_col * display->char_width,
                start_row * display->char_height,

                (end_col - start_col + 1) * display->char_width,
                display->char_height);

    }

    /* Otherwise, need three */
    else {

        /* Ensure proper ordering of start and end coords */
        if (start_row > end_row) {

            int temp;

            temp = start_row;
            start_row = end_row;
            end_row = temp;

            temp = start_col;
            start_col = end_col;
            end_col = temp;

        }

        /* First row */
        guac_protocol_send_rect(socket, select_layer,

                start_col * display->char_width,
                start_row * display->char_height,

                display->width * display->char_width,
                display->char_height);

        /* Middle */
        guac_protocol_send_rect(socket, select_layer,

                0,
                (start_row + 1) * display->char_height,

                display->width * display->char_width,
                (end_row - start_row - 1) * display->char_height);

        /* Last row */
        guac_protocol_send_rect(socket, select_layer,

                0,
                end_row * display->char_height,

                (end_col + 1) * display->char_width,
                display->char_height);

    }

    /* Draw new selection, erasing old */
    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer,
            0x00, 0x80, 0xFF, 0x60);

    guac_client_end_frame(display->client);
    guac_socket_flush(socket);

}