cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

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