From a14832c4daa933bdfd32df8985b223a77b381aa1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 27 Nov 2017 14:21:06 -0800 Subject: [PATCH] GUACAMOLE-313: Implement the "cursor" instruction to guacenc. --- src/guacenc/Makefile.am | 2 + src/guacenc/cursor.c | 57 ++++++++++++++++++++++ src/guacenc/cursor.h | 83 ++++++++++++++++++++++++++++++++ src/guacenc/display-flatten.c | 46 +++++++++++++++++- src/guacenc/display.c | 7 +++ src/guacenc/display.h | 7 +++ src/guacenc/instruction-cursor.c | 36 ++++++++++---- 7 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 src/guacenc/cursor.c create mode 100644 src/guacenc/cursor.h diff --git a/src/guacenc/Makefile.am b/src/guacenc/Makefile.am index b5039cbd..57fac56d 100644 --- a/src/guacenc/Makefile.am +++ b/src/guacenc/Makefile.am @@ -26,6 +26,7 @@ man_MANS = \ noinst_HEADERS = \ buffer.h \ + cursor.h \ display.h \ encode.h \ ffmpeg-compat.h \ @@ -41,6 +42,7 @@ noinst_HEADERS = \ guacenc_SOURCES = \ buffer.c \ + cursor.c \ display.c \ display-buffers.c \ display-image-streams.c \ diff --git a/src/guacenc/cursor.c b/src/guacenc/cursor.c new file mode 100644 index 00000000..4883ac10 --- /dev/null +++ b/src/guacenc/cursor.c @@ -0,0 +1,57 @@ +/* + * 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 "buffer.h" +#include "cursor.h" + +#include + +guacenc_cursor* guacenc_cursor_alloc() { + + /* Allocate new cursor */ + guacenc_cursor* cursor = (guacenc_cursor*) calloc(1, + sizeof(guacenc_cursor)); + if (cursor == NULL) + return NULL; + + /* Allocate associated buffer (image) */ + cursor->buffer = guacenc_buffer_alloc(); + if (cursor->buffer == NULL) { + free(cursor); + return NULL; + } + + return cursor; + +} + +void guacenc_cursor_free(guacenc_cursor* cursor) { + + /* Ignore NULL cursors */ + if (cursor == NULL) + return; + + /* Free underlying buffer */ + guacenc_buffer_free(cursor->buffer); + + free(cursor); + +} + diff --git a/src/guacenc/cursor.h b/src/guacenc/cursor.h new file mode 100644 index 00000000..73dc3c99 --- /dev/null +++ b/src/guacenc/cursor.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef GUACENC_CURSOR_H +#define GUACENC_CURSOR_H + +#include "config.h" +#include "buffer.h" + +#include +#include + +/** + * A mouse cursor, having a current location, hostspot, and associated cursor + * image. + */ +typedef struct guacenc_cursor { + + /** + * The current X coordinate of the mouse cursor, in pixels. + */ + int x; + + /** + * The current Y coordinate of the mouse cursor, in pixels. + */ + int y; + + /** + * The X coordinate of the mouse cursor hotspot within the cursor image, + * in pixels. + */ + int hotspot_x; + + /** + * The Y coordinate of the mouse cursor hotspot within the cursor image, + * in pixels. + */ + int hotspot_y; + + /** + * The current mouse cursor image. + */ + guacenc_buffer* buffer; + +} guacenc_cursor; + +/** + * Allocates and initializes a new cursor object. + * + * @return + * A newly-allocated and initialized guacenc_cursor, or NULL if allocation + * fails. + */ +guacenc_cursor* guacenc_cursor_alloc(); + +/** + * Frees all memory associated with the given cursor object. If the cursor + * provided is NULL, this function has no effect. + * + * @param cursor + * The cursor to free, which may be NULL. + */ +void guacenc_cursor_free(guacenc_cursor* cursor); + +#endif + diff --git a/src/guacenc/display-flatten.c b/src/guacenc/display-flatten.c index f081610c..e2fba829 100644 --- a/src/guacenc/display-flatten.c +++ b/src/guacenc/display-flatten.c @@ -20,9 +20,12 @@ #include "config.h" #include "display.h" #include "layer.h" +#include "log.h" #include +#include +#include #include #include @@ -75,6 +78,46 @@ static int guacenc_display_layer_comparator(const void* a, const void* b) { } +/** + * Renders the mouse cursor on top of the frame buffer of the default layer of + * the given display. + * + * @param display + * The display whose mouse cursor should be rendered to the frame buffer + * of its default layer. + * + * @return + * Zero if rendering succeeds, non-zero otherwise. + */ +static int guacenc_display_render_cursor(guacenc_display* display) { + + guacenc_cursor* cursor = display->cursor; + + /* Retrieve default layer (guaranteed to not be NULL) */ + guacenc_layer* def_layer = guacenc_display_get_layer(display, 0); + assert(def_layer != NULL); + + /* Get source and destination buffers */ + guacenc_buffer* src = cursor->buffer; + guacenc_buffer* dst = def_layer->frame; + + /* Render cursor to layer */ + if (src->width > 0 && src->height > 0) { + cairo_set_source_surface(dst->cairo, src->surface, + cursor->x - cursor->hotspot_x, + cursor->y - cursor->hotspot_y); + cairo_rectangle(dst->cairo, + cursor->x - cursor->hotspot_x, + cursor->y - cursor->hotspot_y, + src->width, src->height); + cairo_fill(dst->cairo); + } + + /* Always succeeds */ + return 0; + +} + int guacenc_display_flatten(guacenc_display* display) { int i; @@ -151,7 +194,8 @@ int guacenc_display_flatten(guacenc_display* display) { } - return 0; + /* Render cursor on top of everything else */ + return guacenc_display_render_cursor(display); } diff --git a/src/guacenc/display.c b/src/guacenc/display.c index fb0cb9aa..094376cf 100644 --- a/src/guacenc/display.c +++ b/src/guacenc/display.c @@ -18,6 +18,7 @@ */ #include "config.h" +#include "cursor.h" #include "display.h" #include "video.h" @@ -97,6 +98,9 @@ guacenc_display* guacenc_display_alloc(const char* path, const char* codec, /* Associate display with video output */ display->output = video; + /* Allocate special-purpose cursor layer */ + display->cursor = guacenc_cursor_alloc(); + return display; } @@ -124,6 +128,9 @@ int guacenc_display_free(guacenc_display* display) { for (i = 0; i < GUACENC_DISPLAY_MAX_STREAMS; i++) guacenc_image_stream_free(display->image_streams[i]); + /* Free cursor */ + guacenc_cursor_free(display->cursor); + free(display); return retval; diff --git a/src/guacenc/display.h b/src/guacenc/display.h index bb01d207..2616bdc3 100644 --- a/src/guacenc/display.h +++ b/src/guacenc/display.h @@ -22,10 +22,12 @@ #include "config.h" #include "buffer.h" +#include "cursor.h" #include "image-stream.h" #include "layer.h" #include "video.h" +#include #include #include @@ -52,6 +54,11 @@ */ typedef struct guacenc_display { + /** + * The current mouse cursor state. + */ + guacenc_cursor* cursor; + /** * All currently-allocated buffers. The index of the buffer corresponds to * its position within this array, where -1 is the 0th entry. If a buffer diff --git a/src/guacenc/instruction-cursor.c b/src/guacenc/instruction-cursor.c index 65fad91b..3307371a 100644 --- a/src/guacenc/instruction-cursor.c +++ b/src/guacenc/instruction-cursor.c @@ -18,6 +18,8 @@ */ #include "config.h" +#include "buffer.h" +#include "cursor.h" #include "display.h" #include "log.h" @@ -36,16 +38,32 @@ int guacenc_handle_cursor(guacenc_display* display, int argc, char** argv) { /* Parse arguments */ int hotspot_x = atoi(argv[0]); int hotspot_y = atoi(argv[1]); - int src_index = atoi(argv[2]); - int src_x = atoi(argv[3]); - int src_y = atoi(argv[4]); - int src_w = atoi(argv[5]); - int src_h = atoi(argv[6]); + int sindex = atoi(argv[2]); + int sx = atoi(argv[3]); + int sy = atoi(argv[4]); + int width = atoi(argv[5]); + int height = atoi(argv[6]); - /* Nothing to do with cursor (yet) */ - guacenc_log(GUAC_LOG_DEBUG, "Ignoring cursor: hotspot (%i, %i) " - "src_layer=%i (%i, %i) %ix%i", hotspot_x, hotspot_y, - src_index, src_x, src_y, src_w, src_h); + /* Pull buffer of source layer/buffer */ + guacenc_buffer* src = guacenc_display_get_related_buffer(display, sindex); + if (src == NULL) + return 1; + + /* Update cursor hotspot */ + guacenc_cursor* cursor = display->cursor; + cursor->hotspot_x = hotspot_x; + cursor->hotspot_y = hotspot_y; + + /* Resize cursor to exactly fit */ + guacenc_buffer_resize(cursor->buffer, width, height); + + /* Copy rectangle from source to cursor */ + guacenc_buffer* dst = cursor->buffer; + if (src->surface != NULL && dst->cairo != NULL) { + cairo_set_operator(dst->cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(dst->cairo, src->surface, sx, sy); + cairo_paint(dst->cairo); + } return 0;