| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "config.h" |
| |
| #include "client.h" |
| #include "common/iconv.h" |
| #include "common/surface.h" |
| #include "vnc.h" |
| |
| #include <cairo/cairo.h> |
| #include <guacamole/client.h> |
| #include <guacamole/layer.h> |
| #include <guacamole/protocol.h> |
| #include <guacamole/socket.h> |
| #include <rfb/rfbclient.h> |
| #include <rfb/rfbproto.h> |
| |
| /* Define cairo_format_stride_for_width() if missing */ |
| #ifndef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH |
| #define cairo_format_stride_for_width(format, width) (width*4) |
| #endif |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <syslog.h> |
| |
| void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) { |
| |
| guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); |
| guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; |
| |
| int dx, dy; |
| |
| /* Cairo image buffer */ |
| int stride; |
| unsigned char* buffer; |
| unsigned char* buffer_row_current; |
| cairo_surface_t* surface; |
| |
| /* VNC framebuffer */ |
| unsigned int bpp; |
| unsigned int fb_stride; |
| unsigned char* fb_row_current; |
| |
| /* Ignore extra update if already handled by copyrect */ |
| if (vnc_client->copy_rect_used) { |
| vnc_client->copy_rect_used = 0; |
| return; |
| } |
| |
| /* Init Cairo buffer */ |
| stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); |
| buffer = malloc(h*stride); |
| buffer_row_current = buffer; |
| |
| bpp = client->format.bitsPerPixel/8; |
| fb_stride = bpp * client->width; |
| fb_row_current = client->frameBuffer + (y * fb_stride) + (x * bpp); |
| |
| /* Copy image data from VNC client to PNG */ |
| for (dy = y; dy<y+h; dy++) { |
| |
| unsigned int* buffer_current; |
| unsigned char* fb_current; |
| |
| /* Get current buffer row, advance to next */ |
| buffer_current = (unsigned int*) buffer_row_current; |
| buffer_row_current += stride; |
| |
| /* Get current framebuffer row, advance to next */ |
| fb_current = fb_row_current; |
| fb_row_current += fb_stride; |
| |
| for (dx = x; dx<x+w; dx++) { |
| |
| unsigned char red, green, blue; |
| unsigned int v; |
| |
| switch (bpp) { |
| case 4: |
| v = *((uint32_t*) fb_current); |
| break; |
| |
| case 2: |
| v = *((uint16_t*) fb_current); |
| break; |
| |
| default: |
| v = *((uint8_t*) fb_current); |
| } |
| |
| /* Translate value to RGB */ |
| red = (v >> client->format.redShift) * 0x100 / (client->format.redMax + 1); |
| green = (v >> client->format.greenShift) * 0x100 / (client->format.greenMax+ 1); |
| blue = (v >> client->format.blueShift) * 0x100 / (client->format.blueMax + 1); |
| |
| /* Output RGB */ |
| if (vnc_client->settings->swap_red_blue) |
| *(buffer_current++) = (blue << 16) | (green << 8) | red; |
| else |
| *(buffer_current++) = (red << 16) | (green << 8) | blue; |
| |
| fb_current += bpp; |
| |
| } |
| } |
| |
| /* Create surface from decoded buffer */ |
| surface = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24, |
| w, h, stride); |
| |
| /* Draw directly to default layer */ |
| guac_common_surface_draw(vnc_client->display->default_surface, |
| x, y, surface); |
| |
| /* Free surface */ |
| cairo_surface_destroy(surface); |
| free(buffer); |
| |
| } |
| |
| void guac_vnc_copyrect(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) { |
| |
| guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); |
| guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; |
| |
| /* Copy specified rectangle within default layer */ |
| guac_common_surface_copy(vnc_client->display->default_surface, |
| src_x, src_y, w, h, |
| vnc_client->display->default_surface, dest_x, dest_y); |
| |
| vnc_client->copy_rect_used = 1; |
| |
| } |
| |
| void guac_vnc_set_pixel_format(rfbClient* client, int color_depth) { |
| client->format.trueColour = 1; |
| switch(color_depth) { |
| case 8: |
| client->format.depth = 8; |
| client->format.bitsPerPixel = 8; |
| client->format.blueShift = 6; |
| client->format.redShift = 0; |
| client->format.greenShift = 3; |
| client->format.blueMax = 3; |
| client->format.redMax = 7; |
| client->format.greenMax = 7; |
| break; |
| |
| case 16: |
| client->format.depth = 16; |
| client->format.bitsPerPixel = 16; |
| client->format.blueShift = 0; |
| client->format.redShift = 11; |
| client->format.greenShift = 5; |
| client->format.blueMax = 0x1f; |
| client->format.redMax = 0x1f; |
| client->format.greenMax = 0x3f; |
| break; |
| |
| case 24: |
| case 32: |
| default: |
| client->format.depth = 24; |
| client->format.bitsPerPixel = 32; |
| client->format.blueShift = 0; |
| client->format.redShift = 16; |
| client->format.greenShift = 8; |
| client->format.blueMax = 0xff; |
| client->format.redMax = 0xff; |
| client->format.greenMax = 0xff; |
| } |
| } |
| |
| rfbBool guac_vnc_malloc_framebuffer(rfbClient* rfb_client) { |
| |
| guac_client* gc = rfbClientGetClientData(rfb_client, GUAC_VNC_CLIENT_KEY); |
| guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; |
| |
| /* Resize surface */ |
| if (vnc_client->display != NULL) |
| guac_common_surface_resize(vnc_client->display->default_surface, |
| rfb_client->width, rfb_client->height); |
| |
| /* Use original, wrapped proc */ |
| return vnc_client->rfb_MallocFrameBuffer(rfb_client); |
| } |
| |