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)); }
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)); }
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)); }
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; } }
/** * 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; }
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; }
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); }
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)); }
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; }
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)); }
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); } }
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); }
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); }