From: Marc-André Lureau <mlureau@xxxxxxxxxx> Add spice-glib support for gl scanout messages. A note about SpiceGlScanout: it is struct with scanout details, registered as a boxed type, with associated gl-scanout property. That way, it doesn't need a seperate signal for change notification and the current scanout can be retrieve with gobject getter. Since boxed property are always duplicated by g_object_get(), an additional spice_display_get_gl_scanout() method returns the current scanout without duplication (that's what spice-gtk display widget will use). Signed-off-by: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx> --- doc/reference/spice-gtk-sections.txt | 1 + src/channel-display.c | 170 ++++++++++++++++++++++++++++++++++- src/channel-display.h | 19 ++++ src/map-file | 4 + src/spice-glib-sym-file | 4 + src/spice-marshal.txt | 1 + 6 files changed, 196 insertions(+), 3 deletions(-) diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt index f156a3f..a108386 100644 --- a/doc/reference/spice-gtk-sections.txt +++ b/doc/reference/spice-gtk-sections.txt @@ -158,6 +158,7 @@ SpiceAudioPrivate SpiceDisplayChannel SpiceDisplayChannelClass <SUBSECTION> +spice_display_get_gl_scanout spice_display_get_primary spice_display_change_preferred_compression <SUBSECTION Standard> diff --git a/src/channel-display.c b/src/channel-display.c index 6a474b1..8f99e94 100644 --- a/src/channel-display.c +++ b/src/channel-display.c @@ -82,6 +82,7 @@ struct _SpiceDisplayChannelPrivate { #ifdef G_OS_WIN32 HDC dc; #endif + SpiceGlScanout scanout; }; G_DEFINE_TYPE(SpiceDisplayChannel, spice_display_channel, SPICE_TYPE_CHANNEL) @@ -92,7 +93,8 @@ enum { PROP_WIDTH, PROP_HEIGHT, PROP_MONITORS, - PROP_MONITORS_MAX + PROP_MONITORS_MAX, + PROP_GL_SCANOUT, }; enum { @@ -100,6 +102,7 @@ enum { SPICE_DISPLAY_PRIMARY_DESTROY, SPICE_DISPLAY_INVALIDATE, SPICE_DISPLAY_MARK, + SPICE_DISPLAY_GL_DRAW, SPICE_DISPLAY_LAST_SIGNAL, }; @@ -118,9 +121,33 @@ static void spice_display_channel_reset_capabilities(SpiceChannel *channel); static void destroy_canvas(display_surface *surface); static void _msg_in_unref_func(gpointer data, gpointer user_data); static void display_session_mm_time_reset_cb(SpiceSession *session, gpointer data); +static SpiceGlScanout* spice_gl_scanout_copy(const SpiceGlScanout *scanout); + +G_DEFINE_BOXED_TYPE(SpiceGlScanout, spice_gl_scanout, + (GBoxedCopyFunc)spice_gl_scanout_copy, + (GBoxedFreeFunc)spice_gl_scanout_free) /* ------------------------------------------------------------------ */ +static SpiceGlScanout* +spice_gl_scanout_copy(const SpiceGlScanout *scanout) +{ + SpiceGlScanout *so = g_new(SpiceGlScanout, 1); + + *so = *scanout; + so->fd = dup(so->fd); + + return so; +} + +void +spice_gl_scanout_free(SpiceGlScanout *scanout) +{ + close(scanout->fd); + + g_free(scanout); +} + static void spice_display_channel_dispose(GObject *object) { SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(object)->priv; @@ -130,6 +157,11 @@ static void spice_display_channel_dispose(GObject *object) c->mark_false_event_id = 0; } + if (c->scanout.fd >= 0) { + close(c->scanout.fd); + c->scanout.fd = -1; + } + if (G_OBJECT_CLASS(spice_display_channel_parent_class)->dispose) G_OBJECT_CLASS(spice_display_channel_parent_class)->dispose(object); } @@ -171,13 +203,13 @@ static void spice_display_channel_constructed(GObject *object) G_OBJECT_CLASS(spice_display_channel_parent_class)->constructed(object); } - static void spice_display_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(object)->priv; + SpiceDisplayChannel *channel = SPICE_DISPLAY_CHANNEL(object); + SpiceDisplayChannelPrivate *c = channel->priv; switch (prop_id) { case PROP_WIDTH: { @@ -196,6 +228,10 @@ static void spice_display_get_property(GObject *object, g_value_set_uint(value, c->monitors_max); break; } + case PROP_GL_SCANOUT: { + g_value_set_static_boxed(value, spice_display_get_gl_scanout(channel)); + break; + } default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -292,6 +328,23 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass) G_PARAM_STATIC_STRINGS)); /** + * SpiceDisplayChannel:gl-scanout: + * @display: the #SpiceDisplayChannel that emitted the signal + * + * The last #SpiceGlScanout received. + * + * Since: 0.31 + */ + g_object_class_install_property + (gobject_class, PROP_GL_SCANOUT, + g_param_spec_boxed("gl-scanout", + "GL scanout", + "GL scanout", + SPICE_TYPE_GL_SCANOUT, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** * SpiceDisplayChannel::display-primary-create: * @display: the #SpiceDisplayChannel that emitted the signal * @format: %SPICE_SURFACE_FMT_32_xRGB or %SPICE_SURFACE_FMT_16_555; @@ -382,6 +435,31 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass) 1, G_TYPE_INT); + /** + * SpiceDisplayChannel::gl-draw: + * @display: the #SpiceDisplayChannel that emitted the signal + * @x: x position + * @y: y position + * @width: width + * @height: height + * + * The #SpiceDisplayChannel::draw signal is emitted when the + * rectangular region x/y/w/h of the GL scanout is updated and + * must be drawn. When the draw is finished, you must call + * spice_display_gl_draw_done() in order to release the GL + * resources. + * + * Since: 0.31 + **/ + signals[SPICE_DISPLAY_GL_DRAW] = + g_signal_new("gl-draw", + G_OBJECT_CLASS_TYPE(gobject_class), + 0, 0, NULL, NULL, + g_cclosure_user_marshal_VOID__UINT_UINT_UINT_UINT, + G_TYPE_NONE, + 4, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); + g_type_class_add_private(klass, sizeof(SpiceDisplayChannelPrivate)); sw_canvas_init(); @@ -459,6 +537,20 @@ void spice_display_change_preferred_compression(SpiceChannel *channel, gint comp spice_msg_out_send_internal(out); } +/** + * spice_display_get_gl_scanout: + * @channel: a #SpiceDisplayChannel + * + * Returns: the current GL scanout + **/ +const SpiceGlScanout * +spice_display_get_gl_scanout(SpiceDisplayChannel *channel) +{ + g_return_val_if_fail(SPICE_IS_DISPLAY_CHANNEL(channel), NULL); + + return &channel->priv->scanout; +} + /* ------------------------------------------------------------------ */ static void image_put(SpiceImageCache *cache, uint64_t id, pixman_image_t *image) @@ -628,6 +720,9 @@ static void spice_display_channel_reset_capabilities(SpiceChannel *channel) if (SPICE_DISPLAY_CHANNEL(channel)->priv->enable_adaptive_streaming) { spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_STREAM_REPORT); } +#ifdef G_OS_UNIX + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_GL_SCANOUT); +#endif } static void destroy_surface(gpointer data) @@ -652,6 +747,7 @@ static void spice_display_channel_init(SpiceDisplayChannel *channel) c->dc = create_compatible_dc(); #endif c->monitors_max = 1; + c->scanout.fd = -1; if (g_getenv("SPICE_DISABLE_ADAPTIVE_STREAMING")) { SPICE_DEBUG("adaptive video disabled"); @@ -1782,6 +1878,70 @@ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in g_coroutine_object_notify(G_OBJECT(channel), "monitors"); } + +#ifdef G_OS_UNIX +/* coroutine context */ +static void display_handle_gl_scanout_unix(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv; + SpiceMsgDisplayGlScanoutUnix *scanout = spice_msg_in_parsed(in); + + scanout->drm_dma_buf_fd = -1; + if (scanout->drm_fourcc_format != 0) { + scanout->drm_dma_buf_fd = spice_channel_unix_read_fd(channel); + CHANNEL_DEBUG(channel, "gl scanout fd: %d", scanout->drm_dma_buf_fd); + } + + c->scanout.y0top = scanout->flags & SPICE_GL_SCANOUT_FLAGS_Y0TOP; + if (c->scanout.fd >= 0) + close(c->scanout.fd); + c->scanout.fd = scanout->drm_dma_buf_fd; + c->scanout.width = scanout->width; + c->scanout.height = scanout->height; + c->scanout.stride = scanout->stride; + c->scanout.format = scanout->drm_fourcc_format; + + g_coroutine_object_notify(G_OBJECT(channel), "gl-scanout"); +} +#endif + +/* coroutine context */ +static void display_handle_gl_draw(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceMsgDisplayGlDraw *draw = spice_msg_in_parsed(in); + + CHANNEL_DEBUG(channel, "gl draw %dx%d+%d+%d", + draw->w, draw->h, draw->x, draw->y); + + g_coroutine_signal_emit(channel, signals[SPICE_DISPLAY_GL_DRAW], 0, + draw->x, draw->y, + draw->w, draw->h); +} + +/** + * spice_display_gl_draw_done: + * @channel: a #SpiceDisplayChannel + * + * After a SpiceDisplayChannel::gl-draw is emitted, the client should + * draw the current display with the current GL scanout, and must + * release the GL resource with a call to spice_display_gl_draw_done() + * (failing to do so for each gl-draw may result in a frozen display). + * + * Since: 0.31 + **/ +void spice_display_gl_draw_done(SpiceDisplayChannel *display) +{ + SpiceChannel *channel; + SpiceMsgOut *out; + + g_return_if_fail(SPICE_IS_DISPLAY_CHANNEL(display)); + channel = SPICE_CHANNEL(display); + + out = spice_msg_out_new(channel, SPICE_MSGC_DISPLAY_GL_DRAW_DONE); + out->marshallers->msgc_display_gl_draw_done(out->marshaller, NULL); + spice_msg_out_send_internal(out); +} + static void channel_set_handlers(SpiceChannelClass *klass) { static const spice_msg_handler handlers[] = { @@ -1820,6 +1980,10 @@ static void channel_set_handlers(SpiceChannelClass *klass) [ SPICE_MSG_DISPLAY_SURFACE_DESTROY ] = display_handle_surface_destroy, [ SPICE_MSG_DISPLAY_MONITORS_CONFIG ] = display_handle_monitors_config, +#ifdef G_OS_UNIX + [ SPICE_MSG_DISPLAY_GL_SCANOUT_UNIX ] = display_handle_gl_scanout_unix, +#endif + [ SPICE_MSG_DISPLAY_GL_DRAW ] = display_handle_gl_draw, }; spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers)); diff --git a/src/channel-display.h b/src/channel-display.h index 52d76f7..bd4bc87 100644 --- a/src/channel-display.h +++ b/src/channel-display.h @@ -37,6 +37,18 @@ typedef struct _SpiceDisplayChannel SpiceDisplayChannel; typedef struct _SpiceDisplayChannelClass SpiceDisplayChannelClass; typedef struct _SpiceDisplayChannelPrivate SpiceDisplayChannelPrivate; +#define SPICE_TYPE_GL_SCANOUT (spice_gl_scanout_get_type ()) + +typedef struct _SpiceGlScanout SpiceGlScanout; +struct _SpiceGlScanout { + gint fd; + guint32 width; + guint32 height; + guint32 stride; + guint32 format; + gboolean y0top; +}; + typedef struct _SpiceDisplayMonitorConfig SpiceDisplayMonitorConfig; struct _SpiceDisplayMonitorConfig { guint id; @@ -100,8 +112,15 @@ struct _SpiceDisplayChannelClass { GType spice_display_channel_get_type(void); gboolean spice_display_get_primary(SpiceChannel *channel, guint32 surface_id, SpiceDisplayPrimary *primary); + void spice_display_change_preferred_compression(SpiceChannel *channel, gint compression); +GType spice_gl_scanout_get_type (void) G_GNUC_CONST; +void spice_gl_scanout_free (SpiceGlScanout *scanout); + +const SpiceGlScanout* spice_display_get_gl_scanout(SpiceDisplayChannel *channel); +void spice_display_gl_draw_done(SpiceDisplayChannel *channel); + G_END_DECLS #endif /* __SPICE_CLIENT_DISPLAY_CHANNEL_H__ */ diff --git a/src/map-file b/src/map-file index 62cdb51..123ef96 100644 --- a/src/map-file +++ b/src/map-file @@ -23,10 +23,12 @@ spice_cursor_channel_get_type; spice_display_change_preferred_compression; spice_display_channel_get_type; spice_display_copy_to_guest; +spice_display_get_gl_scanout; spice_display_get_grab_keys; spice_display_get_pixbuf; spice_display_get_primary; spice_display_get_type; +spice_display_gl_draw_done; spice_display_key_event_get_type; spice_display_mouse_ungrab; spice_display_new; @@ -39,6 +41,8 @@ spice_file_transfer_task_get_filename; spice_file_transfer_task_get_progress; spice_file_transfer_task_get_type; spice_get_option_group; +spice_gl_scanout_free; +spice_gl_scanout_get_type; spice_grab_sequence_as_string; spice_grab_sequence_copy; spice_grab_sequence_free; diff --git a/src/spice-glib-sym-file b/src/spice-glib-sym-file index ae365cd..d49a713 100644 --- a/src/spice-glib-sym-file +++ b/src/spice-glib-sym-file @@ -20,12 +20,16 @@ spice_client_error_quark spice_cursor_channel_get_type spice_display_change_preferred_compression spice_display_channel_get_type +spice_display_get_gl_scanout spice_display_get_primary +spice_display_gl_draw_done spice_file_transfer_task_cancel spice_file_transfer_task_get_filename spice_file_transfer_task_get_progress spice_file_transfer_task_get_type spice_get_option_group +spice_gl_scanout_free +spice_gl_scanout_get_type spice_g_signal_connect_object spice_inputs_button_press spice_inputs_button_release diff --git a/src/spice-marshal.txt b/src/spice-marshal.txt index 9c76054..86673bd 100644 --- a/src/spice-marshal.txt +++ b/src/spice-marshal.txt @@ -1,6 +1,7 @@ VOID:INT,INT VOID:INT,INT,INT VOID:INT,INT,INT,INT +VOID:UINT,UINT,UINT,UINT VOID:INT,INT,INT,INT,POINTER VOID:INT,INT,INT,INT,INT,POINTER VOID:POINTER,INT -- 2.5.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel