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(¤t->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;
}