};
typedef struct DisplayGLCtxOps {
- /*
- * We only check if the GLCtx is compatible with a DCL via ops. A natural
- * evolution of this would be a callback to check some runtime requirements
- * and allow various DCL kinds.
- */
- const DisplayChangeListenerOps *compatible_dcl;
-
+ bool (*dpy_gl_ctx_is_compatible_dcl)(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl);
QEMUGLContext (*dpy_gl_ctx_create)(DisplayGLCtx *dgc,
QEMUGLParams *params);
void (*dpy_gl_ctx_destroy)(DisplayGLCtx *dgc,
QEMUGLContext ctx);
int (*dpy_gl_ctx_make_current)(DisplayGLCtx *dgc,
QEMUGLContext ctx);
+ void (*dpy_gl_ctx_create_texture)(DisplayGLCtx *dgc,
+ DisplaySurface *surface);
+ void (*dpy_gl_ctx_destroy_texture)(DisplayGLCtx *dgc,
+ DisplaySurface *surface);
+ void (*dpy_gl_ctx_update_texture)(DisplayGLCtx *dgc,
+ DisplaySurface *surface,
+ int x, int y, int w, int h);
} DisplayGLCtxOps;
struct DisplayGLCtx {
const DisplayGLCtxOps *ops;
+#ifdef CONFIG_OPENGL
+ QemuGLShader *gls; /* optional shared shader */
+#endif
};
DisplayState *init_displaystate(void);
static void text_console_update_cursor_timer(void);
static void text_console_update_cursor(void *opaque);
static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
+static bool console_compatible_with(QemuConsole *con,
+ DisplayChangeListener *dcl, Error **errp);
static void gui_update(void *opaque)
{
}
}
+static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl,
+ struct DisplaySurface *new_surface,
+ bool update)
+{
+ if (dcl->ops->dpy_gfx_switch) {
+ dcl->ops->dpy_gfx_switch(dcl, new_surface);
+ }
+
+ if (update && dcl->ops->dpy_gfx_update) {
+ dcl->ops->dpy_gfx_update(dcl, 0, 0,
+ surface_width(new_surface),
+ surface_height(new_surface));
+ }
+}
+
+static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface)
+{
+ if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) {
+ con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface);
+ }
+}
+
+static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface)
+{
+ if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) {
+ con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface);
+ }
+}
+
+static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface,
+ int x, int y, int w, int h)
+{
+ if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) {
+ con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h);
+ }
+}
+
static void displaychangelistener_display_console(DisplayChangeListener *dcl,
- QemuConsole *con)
+ QemuConsole *con,
+ Error **errp)
{
static const char nodev[] =
"This VM has no graphic display device.";
static DisplaySurface *dummy;
- if (!con) {
- if (!dcl->ops->dpy_gfx_switch) {
- return;
- }
+ if (!con || !console_compatible_with(con, dcl, errp)) {
if (!dummy) {
dummy = qemu_create_placeholder_surface(640, 480, nodev);
}
- dcl->ops->dpy_gfx_switch(dcl, dummy);
+ if (con) {
+ dpy_gfx_create_texture(con, dummy);
+ }
+ displaychangelistener_gfx_switch(dcl, dummy, TRUE);
return;
}
+ dpy_gfx_create_texture(con, con->surface);
+ displaychangelistener_gfx_switch(dcl, con->surface,
+ con->scanout.kind == SCANOUT_SURFACE);
+
if (con->scanout.kind == SCANOUT_DMABUF &&
displaychangelistener_has_dmabuf(dcl)) {
dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
con->scanout.texture.y,
con->scanout.texture.width,
con->scanout.texture.height);
- } else if (con->scanout.kind == SCANOUT_SURFACE &&
- dcl->ops->dpy_gfx_switch) {
- dcl->ops->dpy_gfx_switch(dcl, con->surface);
}
-
- dcl->ops->dpy_gfx_update(dcl, 0, 0,
- qemu_console_get_width(con, 0),
- qemu_console_get_height(con, 0));
}
void console_select(unsigned int index)
if (dcl->con != NULL) {
continue;
}
- displaychangelistener_display_console(dcl, s);
+ displaychangelistener_display_console(dcl, s, NULL);
}
}
if (ds->have_text) {
return false;
}
-static bool dpy_compatible_with(QemuConsole *con,
- DisplayChangeListener *dcl, Error **errp)
+static bool console_compatible_with(QemuConsole *con,
+ DisplayChangeListener *dcl, Error **errp)
{
int flags;
flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
+ if (console_has_gl(con) &&
+ !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
+ error_setg(errp, "Display %s is incompatible with the GL context",
+ dcl->ops->dpy_name);
+ return false;
+ }
+
if (flags & GRAPHIC_FLAGS_GL &&
!console_has_gl(con)) {
error_setg(errp, "The console requires a GL context.");
con->gl = gl;
}
-static bool dpy_gl_compatible_with(QemuConsole *con, DisplayChangeListener *dcl)
-{
- if (!con->gl) {
- return true;
- }
-
- return con->gl->ops->compatible_dcl == dcl->ops;
-}
-
void register_displaychangelistener(DisplayChangeListener *dcl)
{
QemuConsole *con;
assert(!dcl->ds);
- if (dcl->con && !dpy_gl_compatible_with(dcl->con, dcl)) {
- error_report("Display %s is incompatible with the GL context",
- dcl->ops->dpy_name);
- exit(1);
- }
-
- if (dcl->con) {
- dpy_compatible_with(dcl->con, dcl, &error_fatal);
- }
-
trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
dcl->ds = get_alloc_displaystate();
QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
} else {
con = active_console;
}
- displaychangelistener_display_console(dcl, con);
+ displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
text_console_update_cursor(NULL);
}
if (!qemu_console_is_visible(con)) {
return;
}
+ dpy_gfx_update_texture(con, con->surface, x, y, w, h);
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != (dcl->con ? dcl->con : active_console)) {
continue;
con->scanout.kind = SCANOUT_SURFACE;
con->surface = surface;
+ dpy_gfx_create_texture(con, surface);
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != (dcl->con ? dcl->con : active_console)) {
continue;
}
- if (dcl->ops->dpy_gfx_switch) {
- dcl->ops->dpy_gfx_switch(dcl, surface);
- }
+ displaychangelistener_gfx_switch(dcl, surface, FALSE);
}
+ dpy_gfx_destroy_texture(con, old_surface);
qemu_free_displaysurface(old_surface);
}
DisplayChangeListener dcl;
DBusDisplay *display;
- QemuConsole *con;
GHashTable *listeners;
QemuDBusDisplay1Console *iface;
{
}
-static const DisplayChangeListenerOps dbus_console_dcl_ops = {
+const DisplayChangeListenerOps dbus_console_dcl_ops = {
.dpy_name = "dbus-console",
.dpy_gfx_switch = dbus_gfx_switch,
.dpy_gfx_update = dbus_gfx_update,
.height = arg_height,
};
- if (!dpy_ui_info_supported(ddc->con)) {
+ if (!dpy_ui_info_supported(ddc->dcl.con)) {
g_dbus_method_invocation_return_error(invocation,
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_UNSUPPORTED,
return DBUS_METHOD_INVOCATION_HANDLED;
}
- dpy_set_ui_info(ddc->con, &info, false);
+ dpy_set_ui_info(ddc->dcl.con, &info, false);
qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation);
return DBUS_METHOD_INVOCATION_HANDLED;
}
return DBUS_METHOD_INVOCATION_HANDLED;
}
- qemu_input_queue_rel(ddc->con, INPUT_AXIS_X, dx);
- qemu_input_queue_rel(ddc->con, INPUT_AXIS_Y, dy);
+ qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_X, dx);
+ qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_Y, dy);
qemu_input_event_sync();
qemu_dbus_display1_mouse_complete_rel_motion(ddc->iface_mouse,
return DBUS_METHOD_INVOCATION_HANDLED;
}
- width = qemu_console_get_width(ddc->con, 0);
- height = qemu_console_get_height(ddc->con, 0);
+ width = qemu_console_get_width(ddc->dcl.con, 0);
+ height = qemu_console_get_height(ddc->dcl.con, 0);
if (x >= width || y >= height) {
g_dbus_method_invocation_return_error(
invocation, DBUS_DISPLAY_ERROR,
"Invalid mouse position");
return DBUS_METHOD_INVOCATION_HANDLED;
}
- qemu_input_queue_abs(ddc->con, INPUT_AXIS_X, x, 0, width);
- qemu_input_queue_abs(ddc->con, INPUT_AXIS_Y, y, 0, height);
+ qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_X, x, 0, width);
+ qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_Y, y, 0, height);
qemu_input_event_sync();
qemu_dbus_display1_mouse_complete_set_abs_position(ddc->iface_mouse,
{
trace_dbus_mouse_press(button);
- qemu_input_queue_btn(ddc->con, button, true);
+ qemu_input_queue_btn(ddc->dcl.con, button, true);
qemu_input_event_sync();
qemu_dbus_display1_mouse_complete_press(ddc->iface_mouse, invocation);
{
trace_dbus_mouse_release(button);
- qemu_input_queue_btn(ddc->con, button, false);
+ qemu_input_queue_btn(ddc->dcl.con, button, false);
qemu_input_event_sync();
qemu_dbus_display1_mouse_complete_release(ddc->iface_mouse, invocation);
int dbus_display_console_get_index(DBusDisplayConsole *ddc)
{
- return qemu_console_get_index(ddc->con);
+ return qemu_console_get_index(ddc->dcl.con);
}
DBusDisplayConsole *
"g-object-path", path,
NULL);
ddc->display = display;
- ddc->con = con;
+ ddc->dcl.con = con;
/* handle errors, and skip non graphics? */
qemu_console_fill_device_address(
con, device_addr, sizeof(device_addr), NULL);
DisplayChangeListener dcl;
DisplaySurface *ds;
- QemuGLShader *gls;
int gl_updates;
};
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
- if (ddl->ds) {
- surface_gl_update_texture(ddl->gls, ddl->ds, x, y, w, h);
- }
-
ddl->gl_updates++;
}
trace_dbus_update(x, y, w, h);
+ if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
+ v_data = g_variant_new_from_data(
+ G_VARIANT_TYPE("ay"),
+ surface_data(ddl->ds),
+ surface_stride(ddl->ds) * surface_height(ddl->ds),
+ TRUE,
+ (GDestroyNotify)pixman_image_unref,
+ pixman_image_ref(ddl->ds->image));
+ qemu_dbus_display1_listener_call_scanout(
+ ddl->proxy,
+ surface_width(ddl->ds),
+ surface_height(ddl->ds),
+ surface_stride(ddl->ds),
+ surface_format(ddl->ds),
+ v_data,
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+ return;
+ }
+
/* make a copy, since gvariant only handles linear data */
img = pixman_image_create_bits(surface_format(ddl->ds),
w, h, NULL, stride);
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
- if (ddl->ds) {
- surface_gl_destroy_texture(ddl->gls, ddl->ds);
- }
ddl->ds = new_surface;
if (ddl->ds) {
int width = surface_width(ddl->ds);
int height = surface_height(ddl->ds);
- surface_gl_create_texture(ddl->gls, ddl->ds);
/* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
width, height, 0, 0, width, height);
struct DisplaySurface *new_surface)
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
- GVariant *v_data = NULL;
ddl->ds = new_surface;
if (!ddl->ds) {
/* why not call disable instead? */
return;
}
-
- v_data = g_variant_new_from_data(
- G_VARIANT_TYPE("ay"),
- surface_data(ddl->ds),
- surface_stride(ddl->ds) * surface_height(ddl->ds),
- TRUE,
- (GDestroyNotify)pixman_image_unref,
- pixman_image_ref(ddl->ds->image));
- qemu_dbus_display1_listener_call_scanout(ddl->proxy,
- surface_width(ddl->ds),
- surface_height(ddl->ds),
- surface_stride(ddl->ds),
- surface_format(ddl->ds),
- v_data,
- G_DBUS_CALL_FLAGS_NONE,
- DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
}
static void dbus_mouse_set(DisplayChangeListener *dcl,
g_clear_object(&ddl->conn);
g_clear_pointer(&ddl->bus_name, g_free);
g_clear_object(&ddl->proxy);
- g_clear_pointer(&ddl->gls, qemu_gl_fini_shader);
G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
}
DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
if (display_opengl) {
- ddl->gls = qemu_gl_init_shader();
ddl->dcl.ops = &dbus_gl_dcl_ops;
} else {
ddl->dcl.ops = &dbus_dcl_ops;
return qemu_egl_create_context(dgc, params);
}
+static bool
+dbus_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &dbus_gl_dcl_ops || dcl->ops == &dbus_console_dcl_ops;
+}
+
+static void
+dbus_create_texture(DisplayGLCtx *ctx, DisplaySurface *surface)
+{
+ surface_gl_create_texture(ctx->gls, surface);
+}
+
+static void
+dbus_destroy_texture(DisplayGLCtx *ctx, DisplaySurface *surface)
+{
+ surface_gl_destroy_texture(ctx->gls, surface);
+}
+
+static void
+dbus_update_texture(DisplayGLCtx *ctx, DisplaySurface *surface,
+ int x, int y, int w, int h)
+{
+ surface_gl_update_texture(ctx->gls, surface, x, y, w, h);
+}
+
static const DisplayGLCtxOps dbus_gl_ops = {
- .compatible_dcl = &dbus_gl_dcl_ops,
+ .dpy_gl_ctx_is_compatible_dcl = dbus_is_compatible_dcl,
.dpy_gl_ctx_create = dbus_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = qemu_egl_make_context_current,
+ .dpy_gl_ctx_create_texture = dbus_create_texture,
+ .dpy_gl_ctx_destroy_texture = dbus_destroy_texture,
+ .dpy_gl_ctx_update_texture = dbus_update_texture,
};
static NotifierList dbus_display_notifiers =
g_autoptr(GDBusObjectSkeleton) vm = NULL;
dd->glctx.ops = &dbus_gl_ops;
+ if (display_opengl) {
+ dd->glctx.gls = qemu_gl_init_shader();
+ }
dd->iface = qemu_dbus_display1_vm_skeleton_new();
dd->consoles = g_ptr_array_new_with_free_func(g_object_unref);
g_clear_object(&dd->iface);
g_free(dd->dbus_addr);
g_free(dd->audiodev);
+ g_clear_pointer(&dd->glctx.gls, qemu_gl_fini_shader);
dbus_display = NULL;
}
int
dbus_display_console_get_index(DBusDisplayConsole *ddc);
+
+extern const DisplayChangeListenerOps dbus_console_dcl_ops;
+
#define DBUS_DISPLAY_TYPE_LISTENER dbus_display_listener_get_type()
G_DECLARE_FINAL_TYPE(DBusDisplayListener,
dbus_display_listener,
.dpy_gl_update = egl_scanout_flush,
};
+static bool
+egl_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ if (!dcl->ops->dpy_gl_update) {
+ /*
+ * egl-headless is compatible with all 2d listeners, as it blits the GL
+ * updates on the 2d console surface.
+ */
+ return true;
+ }
+
+ return dcl->ops == &egl_ops;
+}
+
static const DisplayGLCtxOps eglctx_ops = {
- .compatible_dcl = &egl_ops,
+ .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl,
.dpy_gl_ctx_create = egl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = qemu_egl_make_context_current,
.dpy_has_dmabuf = gd_has_dmabuf,
};
+static bool
+gd_gl_area_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &dcl_gl_area_ops;
+}
+
static const DisplayGLCtxOps gl_area_ctx_ops = {
- .compatible_dcl = &dcl_gl_area_ops,
+ .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
.dpy_gl_ctx_create = gd_gl_area_create_context,
.dpy_gl_ctx_destroy = gd_gl_area_destroy_context,
.dpy_gl_ctx_make_current = gd_gl_area_make_current,
.dpy_has_dmabuf = gd_has_dmabuf,
};
+static bool
+gd_egl_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &dcl_egl_ops;
+}
+
static const DisplayGLCtxOps egl_ctx_ops = {
- .compatible_dcl = &dcl_egl_ops,
+ .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
.dpy_gl_ctx_create = gd_egl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = gd_egl_make_current,
.dpy_gl_update = sdl2_gl_scanout_flush,
};
+static bool
+sdl2_gl_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &dcl_gl_ops;
+}
+
static const DisplayGLCtxOps gl_ctx_ops = {
- .compatible_dcl = &dcl_gl_ops,
+ .dpy_gl_ctx_is_compatible_dcl = sdl2_gl_is_compatible_dcl,
.dpy_gl_ctx_create = sdl2_gl_create_context,
.dpy_gl_ctx_destroy = sdl2_gl_destroy_context,
.dpy_gl_ctx_make_current = sdl2_gl_make_context_current,
static GLuint qemu_gl_create_compile_link_program(const GLchar *vert_src,
const GLchar *frag_src)
{
- GLuint vert_shader, frag_shader, program;
+ GLuint vert_shader, frag_shader, program = 0;
vert_shader = qemu_gl_create_compile_shader(GL_VERTEX_SHADER, vert_src);
frag_shader = qemu_gl_create_compile_shader(GL_FRAGMENT_SHADER, frag_src);
if (!vert_shader || !frag_shader) {
- return 0;
+ goto end;
}
program = qemu_gl_create_link_program(vert_shader, frag_shader);
+
+end:
glDeleteShader(vert_shader);
glDeleteShader(frag_shader);
if (!gls) {
return;
}
+ glDeleteProgram(gls->texture_blit_prog);
+ glDeleteProgram(gls->texture_blit_flip_prog);
+ glDeleteProgram(gls->texture_blit_vao);
g_free(gls);
}
.dpy_gl_update = qemu_spice_gl_update,
};
+static bool
+qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &display_listener_gl_ops;
+}
+
static const DisplayGLCtxOps gl_ctx_ops = {
- .compatible_dcl = &display_listener_gl_ops,
+ .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl,
.dpy_gl_ctx_create = qemu_spice_gl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = qemu_egl_make_context_current,