gdi.c (12175B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "bitmap.h" 21#include "color.h" 22#include "common/display.h" 23#include "common/surface.h" 24#include "rdp.h" 25#include "settings.h" 26 27#include <cairo/cairo.h> 28#include <freerdp/freerdp.h> 29#include <freerdp/graphics.h> 30#include <freerdp/primary.h> 31#include <guacamole/client.h> 32#include <guacamole/protocol.h> 33#include <winpr/wtypes.h> 34 35#include <stddef.h> 36#include <stddef.h> 37 38guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client, 39 int rop3) { 40 41 /* Translate supported ROP3 opcodes into composite modes */ 42 switch (rop3) { 43 44 /* "DSon" !(src | dest) */ 45 case 0x11: return GUAC_TRANSFER_BINARY_NOR; 46 47 /* "DSna" !src & dest */ 48 case 0x22: return GUAC_TRANSFER_BINARY_NSRC_AND; 49 50 /* "Sn" !src */ 51 case 0x33: return GUAC_TRANSFER_BINARY_NSRC; 52 53 /* "SDna" (src & !dest) */ 54 case 0x44: return GUAC_TRANSFER_BINARY_NDEST_AND; 55 56 /* "Dn" !dest */ 57 case 0x55: return GUAC_TRANSFER_BINARY_NDEST; 58 59 /* "SRCINVERT" (src ^ dest) */ 60 case 0x66: return GUAC_TRANSFER_BINARY_XOR; 61 62 /* "DSan" !(src & dest) */ 63 case 0x77: return GUAC_TRANSFER_BINARY_NAND; 64 65 /* "SRCAND" (src & dest) */ 66 case 0x88: return GUAC_TRANSFER_BINARY_AND; 67 68 /* "DSxn" !(src ^ dest) */ 69 case 0x99: return GUAC_TRANSFER_BINARY_XNOR; 70 71 /* "MERGEPAINT" (!src | dest)*/ 72 case 0xBB: return GUAC_TRANSFER_BINARY_NSRC_OR; 73 74 /* "SDno" (src | !dest) */ 75 case 0xDD: return GUAC_TRANSFER_BINARY_NDEST_OR; 76 77 /* "SRCPAINT" (src | dest) */ 78 case 0xEE: return GUAC_TRANSFER_BINARY_OR; 79 80 /* 0x00 = "BLACKNESS" (0) */ 81 /* 0xAA = "NOP" (dest) */ 82 /* 0xCC = "SRCCOPY" (src) */ 83 /* 0xFF = "WHITENESS" (1) */ 84 85 } 86 87 /* Log warning if ROP3 opcode not supported */ 88 guac_client_log(client, GUAC_LOG_INFO, "guac_rdp_rop3_transfer_function: " 89 "UNSUPPORTED opcode = 0x%02X", rop3); 90 91 /* Default to BINARY_SRC */ 92 return GUAC_TRANSFER_BINARY_SRC; 93 94} 95 96BOOL guac_rdp_gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt) { 97 98 guac_client* client = ((rdp_freerdp_context*) context)->client; 99 guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface; 100 101 int x = dstblt->nLeftRect; 102 int y = dstblt->nTopRect; 103 int w = dstblt->nWidth; 104 int h = dstblt->nHeight; 105 106 switch (dstblt->bRop) { 107 108 /* Blackness */ 109 case 0: 110 111 /* Send black rectangle */ 112 guac_common_surface_set(current_surface, x, y, w, h, 113 0x00, 0x00, 0x00, 0xFF); 114 break; 115 116 /* DSTINVERT */ 117 case 0x55: 118 guac_common_surface_transfer(current_surface, x, y, w, h, 119 GUAC_TRANSFER_BINARY_NDEST, current_surface, x, y); 120 break; 121 122 /* NOP */ 123 case 0xAA: 124 break; 125 126 /* Whiteness */ 127 case 0xFF: 128 guac_common_surface_set(current_surface, x, y, w, h, 129 0xFF, 0xFF, 0xFF, 0xFF); 130 break; 131 132 /* Unsupported ROP3 */ 133 default: 134 guac_client_log(client, GUAC_LOG_INFO, 135 "guac_rdp_gdi_dstblt(rop3=0x%x)", dstblt->bRop); 136 137 } 138 139 return TRUE; 140 141} 142 143BOOL guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) { 144 145 /* 146 * Note that this is not a full implementation of PATBLT. This is a 147 * fallback implementation which only renders a solid block of background 148 * color using the specified ROP3 operation, ignoring whatever brush 149 * was actually specified. 150 * 151 * As libguac-client-rdp explicitly tells the server not to send PATBLT, 152 * well-behaved RDP servers will not use this operation at all, while 153 * others will at least have a fallback. 154 */ 155 156 /* Get client and current layer */ 157 guac_client* client = ((rdp_freerdp_context*) context)->client; 158 guac_common_surface* current_surface = 159 ((guac_rdp_client*) client->data)->current_surface; 160 161 int x = patblt->nLeftRect; 162 int y = patblt->nTopRect; 163 int w = patblt->nWidth; 164 int h = patblt->nHeight; 165 166 /* 167 * Warn that rendering is a fallback, as the server should not be sending 168 * this order. 169 */ 170 guac_client_log(client, GUAC_LOG_INFO, "Using fallback PATBLT (server is ignoring " 171 "negotiated client capabilities)"); 172 173 /* Render rectangle based on ROP */ 174 switch (patblt->bRop) { 175 176 /* If blackness, send black rectangle */ 177 case 0x00: 178 guac_common_surface_set(current_surface, x, y, w, h, 179 0x00, 0x00, 0x00, 0xFF); 180 break; 181 182 /* If NOP, do nothing */ 183 case 0xAA: 184 break; 185 186 /* If operation is just a copy, send foreground only */ 187 case 0xCC: 188 case 0xF0: 189 guac_common_surface_set(current_surface, x, y, w, h, 190 (patblt->foreColor >> 16) & 0xFF, 191 (patblt->foreColor >> 8 ) & 0xFF, 192 (patblt->foreColor ) & 0xFF, 193 0xFF); 194 break; 195 196 /* If whiteness, send white rectangle */ 197 case 0xFF: 198 guac_common_surface_set(current_surface, x, y, w, h, 199 0xFF, 0xFF, 0xFF, 0xFF); 200 break; 201 202 /* Otherwise, invert entire rect */ 203 default: 204 guac_common_surface_transfer(current_surface, x, y, w, h, 205 GUAC_TRANSFER_BINARY_NDEST, current_surface, x, y); 206 207 } 208 209 return TRUE; 210 211} 212 213BOOL guac_rdp_gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt) { 214 215 guac_client* client = ((rdp_freerdp_context*) context)->client; 216 guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface; 217 218 int x = scrblt->nLeftRect; 219 int y = scrblt->nTopRect; 220 int w = scrblt->nWidth; 221 int h = scrblt->nHeight; 222 223 int x_src = scrblt->nXSrc; 224 int y_src = scrblt->nYSrc; 225 226 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 227 228 /* Copy screen rect to current surface */ 229 guac_common_surface_copy(rdp_client->display->default_surface, 230 x_src, y_src, w, h, current_surface, x, y); 231 232 return TRUE; 233 234} 235 236BOOL guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) { 237 238 guac_client* client = ((rdp_freerdp_context*) context)->client; 239 guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface; 240 guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap; 241 242 int x = memblt->nLeftRect; 243 int y = memblt->nTopRect; 244 int w = memblt->nWidth; 245 int h = memblt->nHeight; 246 247 int x_src = memblt->nXSrc; 248 int y_src = memblt->nYSrc; 249 250 /* Make sure that the received bitmap is not NULL before processing */ 251 if (bitmap == NULL) { 252 guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction."); 253 return TRUE; 254 } 255 256 switch (memblt->bRop) { 257 258 /* If blackness, send black rectangle */ 259 case 0x00: 260 guac_common_surface_set(current_surface, x, y, w, h, 261 0x00, 0x00, 0x00, 0xFF); 262 break; 263 264 /* If NOP, do nothing */ 265 case 0xAA: 266 break; 267 268 /* If operation is just SRC, simply copy */ 269 case 0xCC: 270 271 /* If not cached, cache if necessary */ 272 if (bitmap->layer == NULL && bitmap->used >= 1) 273 guac_rdp_cache_bitmap(context, memblt->bitmap); 274 275 /* If not cached, send as PNG */ 276 if (bitmap->layer == NULL) { 277 if (memblt->bitmap->data != NULL) { 278 279 /* Create surface from image data */ 280 cairo_surface_t* surface = cairo_image_surface_create_for_data( 281 memblt->bitmap->data + 4*(x_src + y_src*memblt->bitmap->width), 282 CAIRO_FORMAT_RGB24, w, h, 4*memblt->bitmap->width); 283 284 /* Send surface to buffer */ 285 guac_common_surface_draw(current_surface, x, y, surface); 286 287 /* Free surface */ 288 cairo_surface_destroy(surface); 289 290 } 291 } 292 293 /* Otherwise, copy */ 294 else 295 guac_common_surface_copy(bitmap->layer->surface, 296 x_src, y_src, w, h, current_surface, x, y); 297 298 /* Increment usage counter */ 299 ((guac_rdp_bitmap*) bitmap)->used++; 300 301 break; 302 303 /* If whiteness, send white rectangle */ 304 case 0xFF: 305 guac_common_surface_set(current_surface, x, y, w, h, 306 0xFF, 0xFF, 0xFF, 0xFF); 307 break; 308 309 /* Otherwise, use transfer */ 310 default: 311 312 /* If not available as a surface, make available. */ 313 if (bitmap->layer == NULL) 314 guac_rdp_cache_bitmap(context, memblt->bitmap); 315 316 guac_common_surface_transfer(bitmap->layer->surface, 317 x_src, y_src, w, h, 318 guac_rdp_rop3_transfer_function(client, memblt->bRop), 319 current_surface, x, y); 320 321 /* Increment usage counter */ 322 ((guac_rdp_bitmap*) bitmap)->used++; 323 324 } 325 326 return TRUE; 327 328} 329 330BOOL guac_rdp_gdi_opaquerect(rdpContext* context, const OPAQUE_RECT_ORDER* opaque_rect) { 331 332 /* Get client data */ 333 guac_client* client = ((rdp_freerdp_context*) context)->client; 334 335 UINT32 color = guac_rdp_convert_color(context, opaque_rect->color); 336 337 guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface; 338 339 int x = opaque_rect->nLeftRect; 340 int y = opaque_rect->nTopRect; 341 int w = opaque_rect->nWidth; 342 int h = opaque_rect->nHeight; 343 344 guac_common_surface_set(current_surface, x, y, w, h, 345 (color >> 16) & 0xFF, 346 (color >> 8 ) & 0xFF, 347 (color ) & 0xFF, 348 0xFF); 349 350 return TRUE; 351 352} 353 354BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) { 355 356 guac_client* client = ((rdp_freerdp_context*) context)->client; 357 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 358 359 /* If no bounds given, clear bounding rect */ 360 if (bounds == NULL) 361 guac_common_surface_reset_clip(rdp_client->display->default_surface); 362 363 /* Otherwise, set bounding rectangle */ 364 else 365 guac_common_surface_clip(rdp_client->display->default_surface, 366 bounds->left, bounds->top, 367 bounds->right - bounds->left + 1, 368 bounds->bottom - bounds->top + 1); 369 370 return TRUE; 371 372} 373 374BOOL guac_rdp_gdi_end_paint(rdpContext* context) { 375 /* IGNORE */ 376 return TRUE; 377} 378 379BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) { 380 381 guac_client* client = ((rdp_freerdp_context*) context)->client; 382 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 383 384 guac_common_surface_resize(rdp_client->display->default_surface, 385 guac_rdp_get_width(context->instance), 386 guac_rdp_get_height(context->instance)); 387 388 guac_common_surface_reset_clip(rdp_client->display->default_surface); 389 390 guac_client_log(client, GUAC_LOG_DEBUG, "Server resized display to %ix%i", 391 guac_rdp_get_width(context->instance), 392 guac_rdp_get_height(context->instance)); 393 394 return TRUE; 395 396} 397 398