static int PFW_LFW_guac_display_frame_complete()

in src/libguac/display-flush.c [110:289]


static int PFW_LFW_guac_display_frame_complete(guac_display* display) {

    guac_client* client = display->client;
    int retval = 0;

    display->last_frame.layers = display->pending_frame.layers;
    guac_display_layer* current = display->pending_frame.layers;
    while (current != NULL) {

        /* Skip processing any layers whose buffers have been replaced with
         * NULL (this is intentionally allowed to ensure references to external
         * buffers can be safely removed if necessary, even before guac_display
         * is freed) */
        if (current->pending_frame.buffer == NULL) {
            GUAC_ASSERT(current->pending_frame.buffer_is_external);
            continue;
        }

        /* Always resize the last_frame buffer to match the pending_frame prior
         * to copying over any changes (this is particularly important given
         * that the pending_frame buffer can be replaced with an external
         * buffer). Since this involves copying over all data from the
         * pending frame, we can skip the later pending frame copy based on
         * whether the pending frame is dirty. */
        if (current->last_frame.buffer_stride != current->pending_frame.buffer_stride
                || current->last_frame.buffer_width != current->pending_frame.buffer_width
                || current->last_frame.buffer_height != current->pending_frame.buffer_height) {

            size_t buffer_size = guac_mem_ckd_mul_or_die(current->pending_frame.buffer_height,
                    current->pending_frame.buffer_stride);

            guac_mem_free(current->last_frame.buffer);
            current->last_frame.buffer = guac_mem_zalloc(buffer_size);
            memcpy(current->last_frame.buffer, current->pending_frame.buffer, buffer_size);

            current->last_frame.buffer_stride = current->pending_frame.buffer_stride;
            current->last_frame.buffer_width = current->pending_frame.buffer_width;
            current->last_frame.buffer_height = current->pending_frame.buffer_height;

            current->last_frame.dirty = current->pending_frame.dirty;
            current->pending_frame.dirty = (guac_rect) { 0 };

            retval = 1;

        }

        /* Copy over pending frame contents if actually changed (this is not
         * necessary if the last_frame buffer was resized to match
         * pending_frame, as a copy from pending_frame to last_frame is
         * inherently part of that) */
        else if (!guac_rect_is_empty(&current->pending_frame.dirty)) {

            unsigned char* pending_frame = current->pending_frame.buffer;
            unsigned char* last_frame = current->last_frame.buffer;
            size_t row_length = guac_mem_ckd_mul_or_die(current->pending_frame.width, 4);

            for (int y = 0; y < current->pending_frame.height; y++) {
                memcpy(last_frame, pending_frame, row_length);
                last_frame += current->last_frame.buffer_stride;
                pending_frame += current->pending_frame.buffer_stride;
            }

            current->last_frame.dirty = current->pending_frame.dirty;
            current->pending_frame.dirty = (guac_rect) { 0 };

            retval = 1;

        }

        /* Even if nothing has changed in the pending frame, we have to at
         * least flush that fact to the last frame (otherwise, the last frame
         * may contain stale dirty rects) */
        else
            current->last_frame.dirty = (guac_rect) { 0 };

        /* Commit any change in layer size */
        if (current->pending_frame.width != current->last_frame.width
                || current->pending_frame.height != current->last_frame.height) {

            guac_protocol_send_size(client->socket, current->layer,
                    current->pending_frame.width, current->pending_frame.height);

            current->last_frame.width = current->pending_frame.width;
            current->last_frame.height = current->pending_frame.height;

            retval = 1;

        }

        /* Commit any change in layer opacity */
        if (current->pending_frame.opacity != current->last_frame.opacity) {

            guac_protocol_send_shade(client->socket, current->layer,
                    current->pending_frame.opacity);

            current->last_frame.opacity = current->pending_frame.opacity;

            retval = 1;

        }

        /* Commit any change in layer location/hierarchy */
        if (current->pending_frame.x != current->last_frame.x
                || current->pending_frame.y != current->last_frame.y
                || current->pending_frame.z != current->last_frame.z
                || current->pending_frame.parent != current->last_frame.parent) {

            guac_protocol_send_move(client->socket, current->layer,
                    current->pending_frame.parent,
                    current->pending_frame.x,
                    current->pending_frame.y,
                    current->pending_frame.z);

            current->last_frame.x = current->pending_frame.x;
            current->last_frame.y = current->pending_frame.y;
            current->last_frame.z = current->pending_frame.z;
            current->last_frame.parent = current->pending_frame.parent;

            retval = 1;

        }

        /* Commit any change in layer multitouch support */
        if (current->pending_frame.touches != current->last_frame.touches) {
            guac_protocol_send_set_int(client->socket, current->layer,
                    GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH,
                    current->pending_frame.touches);
            current->last_frame.touches = current->pending_frame.touches;
        }

        /* Commit any hinting regarding scroll/copy optimization (NOTE: While
         * this value is copied for consistency, it will already have taken
         * effect in the context of the pending frame due to the scroll/copy
         * optimization pass having occurred prior to the call to this
         * function) */
        current->last_frame.search_for_copies = current->pending_frame.search_for_copies;
        current->pending_frame.search_for_copies = 0;

        /* Commit any change in lossless setting (no need to synchronize this
         * to the client - it affects only how last_frame is interpreted) */
        current->last_frame.lossless = current->pending_frame.lossless;

        /* Duplicate layers from pending frame to last frame */
        current->last_frame.prev = current->pending_frame.prev;
        current->last_frame.next = current->pending_frame.next;
        current = current->pending_frame.next;

    }

    display->last_frame.timestamp = display->pending_frame.timestamp;
    display->last_frame.frames = display->pending_frame.frames;

    display->pending_frame.frames = 0;
    display->pending_frame_dirty_excluding_mouse = 0;

    /* Commit cursor hotspot */
    display->last_frame.cursor_hotspot_x = display->pending_frame.cursor_hotspot_x;
    display->last_frame.cursor_hotspot_y = display->pending_frame.cursor_hotspot_y;

    /* Commit mouse cursor location and notify all other users of change in
     * cursor state */
    if (display->pending_frame.cursor_x != display->last_frame.cursor_x
            || display->pending_frame.cursor_y != display->last_frame.cursor_y
            || display->pending_frame.cursor_mask != display->last_frame.cursor_mask) {

        display->last_frame.cursor_user = display->pending_frame.cursor_user;
        display->last_frame.cursor_x = display->pending_frame.cursor_x;
        display->last_frame.cursor_y = display->pending_frame.cursor_y;
        display->last_frame.cursor_mask = display->pending_frame.cursor_mask;
        guac_client_foreach_user(client, LFR_guac_display_broadcast_cursor_state, display);

        /* NOTE: We DO NOT set retval here, as flushing a frame due purely to
         * mouse position changes can cause slowdowns apparently from the sheer
         * quantity of frames */

    }

    return retval;

}