aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--audio/dbusaudio.c43
-rw-r--r--chardev/char-win-stdio.c2
-rw-r--r--hw/display/virtio-gpu-udmabuf.c3
-rw-r--r--hw/display/virtio-gpu-virgl.c43
-rw-r--r--hw/display/virtio-gpu.c99
-rw-r--r--include/hw/virtio/virtio-gpu.h3
-rw-r--r--include/sysemu/os-win32.h3
-rw-r--r--include/ui/console.h30
-rw-r--r--include/ui/egl-helpers.h11
-rw-r--r--include/ui/gtk.h6
-rw-r--r--include/ui/sdl2.h3
-rw-r--r--meson.build10
-rw-r--r--qapi/ui.json5
-rw-r--r--scripts/meson.build2
-rw-r--r--scripts/xml-preprocess-test.py136
-rwxr-xr-xscripts/xml-preprocess.py293
-rw-r--r--tests/qtest/dbus-display-test.c43
-rw-r--r--tests/qtest/libqtest.c5
-rw-r--r--tests/qtest/libqtest.h9
-rw-r--r--tests/qtest/meson.build2
-rw-r--r--ui/console.c137
-rw-r--r--ui/dbus-chardev.c20
-rw-r--r--ui/dbus-console.c124
-rw-r--r--ui/dbus-display1.xml285
-rw-r--r--ui/dbus-listener.c527
-rw-r--r--ui/dbus.c4
-rw-r--r--ui/dbus.h6
-rw-r--r--ui/egl-context.c10
-rw-r--r--ui/egl-headless.c25
-rw-r--r--ui/egl-helpers.c104
-rw-r--r--ui/gtk-egl.c16
-rw-r--r--ui/gtk-gl-area.c6
-rw-r--r--ui/gtk.c66
-rw-r--r--ui/meson.build15
-rw-r--r--ui/qemu-pixman.c1
-rw-r--r--ui/sdl2-gl.c3
-rw-r--r--ui/sdl2.c18
-rw-r--r--ui/spice-display.c3
-rw-r--r--ui/trace-events9
-rw-r--r--util/oslib-win32.c33
-rw-r--r--util/trace-events4
42 files changed, 1953 insertions, 215 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index e07746a..21a587c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3202,6 +3202,7 @@ F: docs/interop/dbus*
F: docs/sphinx/dbus*
F: docs/sphinx/fakedbusdoc.py
F: tests/qtest/dbus*
+F: scripts/xml-preprocess*
Seccomp
M: Daniel P. Berrange <berrange@redhat.com>
diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c
index fece74f..7a11fbf 100644
--- a/audio/dbusaudio.c
+++ b/audio/dbusaudio.c
@@ -29,7 +29,11 @@
#include "qemu/timer.h"
#include "qemu/dbus.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
+
+#include "ui/dbus.h"
#include "ui/dbus-display1.h"
#define AUDIO_CAP "dbus"
@@ -444,7 +448,9 @@ listener_in_vanished_cb(GDBusConnection *connection,
static gboolean
dbus_audio_register_listener(AudioState *s,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener,
bool out)
{
@@ -471,6 +477,11 @@ dbus_audio_register_listener(AudioState *s,
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#ifdef G_OS_WIN32
+ if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+#else
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
if (err) {
g_dbus_method_invocation_return_error(invocation,
@@ -480,6 +491,7 @@ dbus_audio_register_listener(AudioState *s,
err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#endif
socket = g_socket_new_from_fd(fd, &err);
if (err) {
@@ -488,15 +500,28 @@ dbus_audio_register_listener(AudioState *s,
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't make a socket: %s",
err->message);
+#ifdef G_OS_WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
return DBUS_METHOD_INVOCATION_HANDLED;
}
socket_conn = g_socket_connection_factory_create_connection(socket);
if (out) {
qemu_dbus_display1_audio_complete_register_out_listener(
- da->iface, invocation, NULL);
+ da->iface, invocation
+#ifdef G_OS_UNIX
+ , NULL
+#endif
+ );
} else {
qemu_dbus_display1_audio_complete_register_in_listener(
- da->iface, invocation, NULL);
+ da->iface, invocation
+#ifdef G_OS_UNIX
+ , NULL
+#endif
+ );
}
listener_conn =
@@ -574,22 +599,32 @@ dbus_audio_register_listener(AudioState *s,
static gboolean
dbus_audio_register_out_listener(AudioState *s,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener)
{
return dbus_audio_register_listener(s, invocation,
- fd_list, arg_listener, true);
+#ifdef G_OS_UNIX
+ fd_list,
+#endif
+ arg_listener, true);
}
static gboolean
dbus_audio_register_in_listener(AudioState *s,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener)
{
return dbus_audio_register_listener(s, invocation,
- fd_list, arg_listener, false);
+#ifdef G_OS_UNIX
+ fd_list,
+#endif
+ arg_listener, false);
}
static void
diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c
index eb830ea..1a18999 100644
--- a/chardev/char-win-stdio.c
+++ b/chardev/char-win-stdio.c
@@ -190,7 +190,7 @@ static void qemu_chr_open_stdio(Chardev *chr,
}
}
- dwMode |= ENABLE_LINE_INPUT;
+ dwMode |= ENABLE_LINE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT;
if (is_console) {
/* set the terminal in raw mode */
diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 69e2cf0..ef1a740 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -132,7 +132,8 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res)
void *pdata = NULL;
res->dmabuf_fd = -1;
- if (res->iov_cnt == 1) {
+ if (res->iov_cnt == 1 &&
+ res->iov[0].iov_len < 4096) {
pdata = res->iov[0].iov_base;
} else {
virtio_gpu_create_udmabuf(res);
diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index 1c47603..8bb7a2c 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -18,9 +18,17 @@
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-gpu.h"
+#include "ui/egl-helpers.h"
+
#include <virglrenderer.h>
-static struct virgl_renderer_callbacks virtio_gpu_3d_cbs;
+#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
+static void *
+virgl_get_egl_display(G_GNUC_UNUSED void *cookie)
+{
+ return qemu_egl_display;
+}
+#endif
static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
@@ -145,7 +153,6 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
struct virtio_gpu_set_scanout ss;
- struct virgl_renderer_resource_info info;
int ret;
VIRTIO_GPU_FILL_CMD(ss);
@@ -160,10 +167,20 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
}
g->parent_obj.enable = 1;
- memset(&info, 0, sizeof(info));
-
if (ss.resource_id && ss.r.width && ss.r.height) {
+ struct virgl_renderer_resource_info info;
+ void *d3d_tex2d = NULL;
+
+#ifdef HAVE_VIRGL_D3D_INFO_EXT
+ struct virgl_renderer_resource_info_ext ext;
+ memset(&ext, 0, sizeof(ext));
+ ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext);
+ info = ext.base;
+ d3d_tex2d = ext.d3d_tex2d;
+#else
+ memset(&info, 0, sizeof(info));
ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
+#endif
if (ret == -1) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: illegal resource specified %d\n",
@@ -178,7 +195,8 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
g->parent_obj.scanout[ss.scanout_id].con, info.tex_id,
info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
info.width, info.height,
- ss.r.x, ss.r.y, ss.r.width, ss.r.height);
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height,
+ d3d_tex2d);
} else {
dpy_gfx_replace_surface(
g->parent_obj.scanout[ss.scanout_id].con, NULL);
@@ -607,8 +625,21 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g)
int virtio_gpu_virgl_init(VirtIOGPU *g)
{
int ret;
+ uint32_t flags = 0;
+
+#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
+ if (qemu_egl_display) {
+ virtio_gpu_3d_cbs.version = 4;
+ virtio_gpu_3d_cbs.get_egl_display = virgl_get_egl_display;
+ }
+#endif
+#ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE
+ if (qemu_egl_angle_d3d) {
+ flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE;
+ }
+#endif
- ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs);
+ ret = virgl_renderer_init(g, flags, &virtio_gpu_3d_cbs);
if (ret != 0) {
error_report("virgl could not be initialized: %d", ret);
return ret;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 66cddd9..347e17d 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -258,6 +258,16 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat,
return height * stride;
}
+#ifdef WIN32
+static void
+win32_pixman_image_destroy(pixman_image_t *image, void *data)
+{
+ HANDLE handle = data;
+
+ qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn);
+}
+#endif
+
static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
@@ -304,12 +314,27 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height);
if (res->hostmem + g->hostmem < g->conf_max_hostmem) {
+ void *bits = NULL;
+#ifdef WIN32
+ bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
+ if (!bits) {
+ goto end;
+ }
+#endif
res->image = pixman_image_create_bits(pformat,
c2d.width,
c2d.height,
- NULL, 0);
+ bits, res->hostmem / c2d.height);
+#ifdef WIN32
+ if (res->image) {
+ pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle);
+ }
+#endif
}
+#ifdef WIN32
+end:
+#endif
if (!res->image) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: resource creation failed %d %d %d\n",
@@ -438,11 +463,11 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
struct virtio_gpu_simple_resource *res;
- int h;
+ int h, bpp;
uint32_t src_offset, dst_offset, stride;
- int bpp;
pixman_format_code_t format;
struct virtio_gpu_transfer_to_host_2d t2d;
+ void *img_data;
VIRTIO_GPU_FILL_CMD(t2d);
virtio_gpu_t2d_bswap(&t2d);
@@ -471,23 +496,23 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
format = pixman_image_get_format(res->image);
bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
stride = pixman_image_get_stride(res->image);
+ img_data = pixman_image_get_data(res->image);
- if (t2d.offset || t2d.r.x || t2d.r.y ||
- t2d.r.width != pixman_image_get_width(res->image)) {
- void *img_data = pixman_image_get_data(res->image);
+ if (t2d.r.x || t2d.r.width != pixman_image_get_width(res->image)) {
for (h = 0; h < t2d.r.height; h++) {
src_offset = t2d.offset + stride * h;
dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
iov_to_buf(res->iov, res->iov_cnt, src_offset,
- (uint8_t *)img_data
- + dst_offset, t2d.r.width * bpp);
+ (uint8_t *)img_data + dst_offset,
+ t2d.r.width * bpp);
}
} else {
- iov_to_buf(res->iov, res->iov_cnt, 0,
- pixman_image_get_data(res->image),
- pixman_image_get_stride(res->image)
- * pixman_image_get_height(res->image));
+ src_offset = t2d.offset;
+ dst_offset = t2d.r.y * stride + t2d.r.x * bpp;
+ iov_to_buf(res->iov, res->iov_cnt, src_offset,
+ (uint8_t *)img_data + dst_offset,
+ stride * t2d.r.height);
}
}
@@ -498,6 +523,8 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
struct virtio_gpu_resource_flush rf;
struct virtio_gpu_scanout *scanout;
pixman_region16_t flush_region;
+ bool within_bounds = false;
+ bool update_submitted = false;
int i;
VIRTIO_GPU_FILL_CMD(rf);
@@ -518,13 +545,28 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
rf.r.x < scanout->x + scanout->width &&
rf.r.x + rf.r.width >= scanout->x &&
rf.r.y < scanout->y + scanout->height &&
- rf.r.y + rf.r.height >= scanout->y &&
- console_has_gl(scanout->con)) {
- dpy_gl_update(scanout->con, 0, 0, scanout->width,
- scanout->height);
+ rf.r.y + rf.r.height >= scanout->y) {
+ within_bounds = true;
+
+ if (console_has_gl(scanout->con)) {
+ dpy_gl_update(scanout->con, 0, 0, scanout->width,
+ scanout->height);
+ update_submitted = true;
+ }
}
}
- return;
+
+ if (update_submitted) {
+ return;
+ }
+ if (!within_bounds) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside scanouts"
+ " bounds for flush %d: %d %d %d %d\n",
+ __func__, rf.resource_id, rf.r.x, rf.r.y,
+ rf.r.width, rf.r.height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
}
if (!res->blob &&
@@ -634,8 +676,10 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
if (console_has_gl(scanout->con)) {
if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) {
virtio_gpu_update_scanout(g, scanout_id, res, r);
- return;
+ } else {
+ *error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
}
+ return;
}
data = res->blob;
@@ -666,6 +710,9 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
*error = VIRTIO_GPU_RESP_ERR_UNSPEC;
return;
}
+#ifdef WIN32
+ qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset);
+#endif
pixman_image_unref(rect);
dpy_gfx_replace_surface(g->parent_obj.scanout[scanout_id].con,
@@ -1209,6 +1256,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
struct virtio_gpu_simple_resource *res;
struct virtio_gpu_scanout *scanout;
uint32_t resource_id, pformat;
+ void *bits = NULL;
int i;
g->hostmem = 0;
@@ -1233,15 +1281,23 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
g_free(res);
return -EINVAL;
}
+
+ res->hostmem = calc_image_hostmem(pformat, res->width, res->height);
+#ifdef WIN32
+ bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn);
+ if (!bits) {
+ g_free(res);
+ return -EINVAL;
+ }
+#endif
res->image = pixman_image_create_bits(pformat,
res->width, res->height,
- NULL, 0);
+ bits, res->hostmem / res->height);
if (!res->image) {
g_free(res);
return -EINVAL;
}
- res->hostmem = calc_image_hostmem(pformat, res->width, res->height);
res->addrs = g_new(uint64_t, res->iov_cnt);
res->iov = g_new(struct iovec, res->iov_cnt);
@@ -1302,6 +1358,9 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
if (!scanout->ds) {
return -EINVAL;
}
+#ifdef WIN32
+ qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
+#endif
dpy_gfx_replace_surface(scanout->con, scanout->ds);
dpy_gfx_update_full(scanout->con);
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 2e28507..7a5f805 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -48,6 +48,9 @@ struct virtio_gpu_simple_resource {
unsigned int iov_cnt;
uint32_t scanout_bitmask;
pixman_image_t *image;
+#ifdef WIN32
+ HANDLE handle;
+#endif
uint64_t hostmem;
uint64_t blob_size;
diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h
index 65f6c9e..91aa0d7 100644
--- a/include/sysemu/os-win32.h
+++ b/include/sysemu/os-win32.h
@@ -263,6 +263,9 @@ EXCEPTION_DISPOSITION
win32_close_exception_handler(struct _EXCEPTION_RECORD*, void*,
struct _CONTEXT*, void*);
+void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp);
+void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/ui/console.h b/include/ui/console.h
index ae5ec46..f27b2aa 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -5,6 +5,7 @@
#include "qom/object.h"
#include "qemu/notify.h"
#include "qapi/qapi-types-ui.h"
+#include "ui/input.h"
#ifdef CONFIG_OPENGL
# include <epoxy/gl.h>
@@ -95,6 +96,20 @@ bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl);
void kbd_put_string_console(QemuConsole *s, const char *str, int len);
void kbd_put_keysym(int keysym);
+/* Touch devices */
+typedef struct touch_slot {
+ int x;
+ int y;
+ int tracking_id;
+} touch_slot;
+
+void console_handle_touch_event(QemuConsole *con,
+ struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
+ uint64_t num_slot,
+ int width, int height,
+ double x, double y,
+ InputMultiTouchType type,
+ Error **errp);
/* consoles */
#define TYPE_QEMU_CONSOLE "qemu-console"
@@ -117,6 +132,7 @@ typedef struct ScanoutTexture {
uint32_t y;
uint32_t width;
uint32_t height;
+ void *d3d_tex2d;
} ScanoutTexture;
typedef struct DisplaySurface {
@@ -128,6 +144,10 @@ typedef struct DisplaySurface {
GLenum gltype;
GLuint texture;
#endif
+#ifdef WIN32
+ HANDLE handle;
+ uint32_t handle_offset;
+#endif
} DisplaySurface;
typedef struct QemuUIInfo {
@@ -251,7 +271,8 @@ typedef struct DisplayChangeListenerOps {
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
/* optional (default to true if has dpy_gl_scanout_dmabuf) */
bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl);
/* optional */
@@ -314,6 +335,10 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height,
DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image);
DisplaySurface *qemu_create_placeholder_surface(int w, int h,
const char *msg);
+#ifdef WIN32
+void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
+ HANDLE h, uint32_t offset);
+#endif
PixelFormat qemu_default_pixelformat(int bpp);
DisplaySurface *qemu_create_displaysurface(int width, int height);
@@ -355,7 +380,8 @@ void dpy_gl_scanout_disable(QemuConsole *con);
void dpy_gl_scanout_texture(QemuConsole *con,
uint32_t backing_id, bool backing_y_0_top,
uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void dpy_gl_scanout_dmabuf(QemuConsole *con,
QemuDmaBuf *dmabuf);
void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index 53d953d..4b8c0d2 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -12,6 +12,7 @@
extern EGLDisplay *qemu_egl_display;
extern EGLConfig qemu_egl_config;
extern DisplayGLMode qemu_egl_mode;
+extern bool qemu_egl_angle_d3d;
typedef struct egl_fb {
int width;
@@ -31,16 +32,18 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
void egl_fb_setup_new_tex(egl_fb *fb, int width, int height);
void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip);
void egl_fb_read(DisplaySurface *dst, egl_fb *src);
+void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h);
void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip);
void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
int x, int y, double scale_x, double scale_y);
+extern EGLContext qemu_egl_rn_ctx;
+
#ifdef CONFIG_GBM
extern int qemu_egl_rn_fd;
extern struct gbm_device *qemu_egl_rn_gbm_dev;
-extern EGLContext qemu_egl_rn_ctx;
int egl_rendernode_init(const char *rendernode, DisplayGLMode mode);
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc,
@@ -62,9 +65,15 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode);
#endif
+#ifdef WIN32
+int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode);
+#endif
+
EGLContext qemu_egl_init_ctx(void);
bool qemu_egl_has_dmabuf(void);
bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp);
+const char *qemu_egl_get_error_string(void);
+
#endif /* EGL_HELPERS_H */
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index ae0f537..aa3d637 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -175,7 +175,8 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf);
void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
@@ -211,7 +212,8 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h
index 8fb7e08..e3acc7c 100644
--- a/include/ui/sdl2.h
+++ b/include/ui/sdl2.h
@@ -90,7 +90,8 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d);
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
diff --git a/meson.build b/meson.build
index b409788..1631e60 100644
--- a/meson.build
+++ b/meson.build
@@ -838,6 +838,8 @@ if gdbus_codegen.found() and get_option('cfi')
gdbus_codegen_error = '@0@ uses gdbus-codegen, which does not support control flow integrity'
endif
+xml_pp = find_program('scripts/xml-preprocess.py')
+
lttng = not_found
if 'ust' in get_option('trace_backends')
lttng = dependency('lttng-ust', required: true, version: '>= 2.1',
@@ -1070,6 +1072,12 @@ if not get_option('virglrenderer').auto() or have_system or have_vhost_user_gpu
virgl = dependency('virglrenderer',
method: 'pkg-config',
required: get_option('virglrenderer'))
+ if virgl.found()
+ config_host_data.set('HAVE_VIRGL_D3D_INFO_EXT',
+ cc.has_member('struct virgl_renderer_resource_info_ext', 'd3d_tex2d',
+ prefix: '#include <virglrenderer.h>',
+ dependencies: virgl))
+ endif
endif
blkio = not_found
if not get_option('blkio').auto() or have_block
@@ -1985,8 +1993,6 @@ dbus_display = get_option('dbus_display') \
error_message: '-display dbus requires glib>=2.64') \
.require(gdbus_codegen.found(),
error_message: gdbus_codegen_error.format('-display dbus')) \
- .require(targetos != 'windows',
- error_message: '-display dbus is not available on Windows') \
.allowed()
have_virtfs = get_option('virtfs') \
diff --git a/qapi/ui.json b/qapi/ui.json
index 2755395..bb06fb6 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1484,8 +1484,7 @@
{ 'name': 'none' },
{ 'name': 'gtk', 'if': 'CONFIG_GTK' },
{ 'name': 'sdl', 'if': 'CONFIG_SDL' },
- { 'name': 'egl-headless',
- 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
+ { 'name': 'egl-headless', 'if': 'CONFIG_OPENGL' },
{ 'name': 'curses', 'if': 'CONFIG_CURSES' },
{ 'name': 'cocoa', 'if': 'CONFIG_COCOA' },
{ 'name': 'spice-app', 'if': 'CONFIG_SPICE' },
@@ -1525,7 +1524,7 @@
'cocoa': { 'type': 'DisplayCocoa', 'if': 'CONFIG_COCOA' },
'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' },
'egl-headless': { 'type': 'DisplayEGLHeadless',
- 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
+ 'if': 'CONFIG_OPENGL' },
'dbus': { 'type': 'DisplayDBus', 'if': 'CONFIG_DBUS_DISPLAY' },
'sdl': { 'type': 'DisplaySDL', 'if': 'CONFIG_SDL' }
}
diff --git a/scripts/meson.build b/scripts/meson.build
index 1c89e10..532277f 100644
--- a/scripts/meson.build
+++ b/scripts/meson.build
@@ -1,3 +1,5 @@
if stap.found()
install_data('qemu-trace-stap', install_dir: get_option('bindir'))
endif
+
+test('xml-preprocess', files('xml-preprocess-test.py'), suite: ['unit'])
diff --git a/scripts/xml-preprocess-test.py b/scripts/xml-preprocess-test.py
new file mode 100644
index 0000000..dd92579
--- /dev/null
+++ b/scripts/xml-preprocess-test.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2023 Red Hat, Inc.
+#
+# SPDX-License-Identifier: MIT
+"""Unit tests for xml-preprocess"""
+
+import contextlib
+import importlib
+import os
+import platform
+import subprocess
+import tempfile
+import unittest
+from io import StringIO
+
+xmlpp = importlib.import_module("xml-preprocess")
+
+
+class TestXmlPreprocess(unittest.TestCase):
+ """Tests for xml-preprocess.Preprocessor"""
+
+ def test_preprocess_xml(self):
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file:
+ temp_file.write("<root></root>")
+ temp_file_name = temp_file.name
+ result = xmlpp.preprocess_xml(temp_file_name)
+ self.assertEqual(result, "<root></root>")
+ os.remove(temp_file_name)
+
+ def test_save_xml(self):
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file:
+ temp_file_name = temp_file.name
+ xmlpp.save_xml("<root></root>", temp_file_name)
+ self.assertTrue(os.path.isfile(temp_file_name))
+ os.remove(temp_file_name)
+
+ def test_include(self):
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as inc_file:
+ inc_file.write("<included>Content from included file</included>")
+ inc_file_name = inc_file.name
+ xml_str = f"<?include {inc_file_name} ?>"
+ expected = "<included>Content from included file</included>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ os.remove(inc_file_name)
+ self.assertRaises(FileNotFoundError, xpp.preprocess, xml_str)
+
+ def test_envvar(self):
+ os.environ["TEST_ENV_VAR"] = "TestValue"
+ xml_str = "<root>$(env.TEST_ENV_VAR)</root>"
+ expected = "<root>TestValue</root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ self.assertRaises(KeyError, xpp.preprocess, "$(env.UNKNOWN)")
+
+ def test_sys_var(self):
+ xml_str = "<root>$(sys.ARCH)</root>"
+ expected = f"<root>{platform.architecture()[0]}</root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ self.assertRaises(KeyError, xpp.preprocess, "$(sys.UNKNOWN)")
+
+ def test_cus_var(self):
+ xml_str = "<root>$(var.USER)</root>"
+ expected = "<root></root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ xml_str = "<?define USER=FOO?><root>$(var.USER)</root>"
+ expected = "<root>FOO</root>"
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+
+ def test_error_warning(self):
+ xml_str = "<root><?warning \"test warn\"?></root>"
+ expected = "<root></root>"
+ xpp = xmlpp.Preprocessor()
+ out = StringIO()
+ with contextlib.redirect_stdout(out):
+ result = xpp.preprocess(xml_str)
+ self.assertEqual(result, expected)
+ self.assertEqual(out.getvalue(), "[Warning]: test warn\n")
+ self.assertRaises(RuntimeError, xpp.preprocess, "<?error \"test\"?>")
+
+ def test_cmd(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess('<root><?cmd "echo hello world"?></root>')
+ self.assertEqual(result, "<root>hello world</root>")
+ self.assertRaises(
+ subprocess.CalledProcessError,
+ xpp.preprocess, '<?cmd "test-unknown-cmd"?>'
+ )
+
+ def test_foreach(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess(
+ '<root><?foreach x in a;b;c?>$(var.x)<?endforeach?></root>'
+ )
+ self.assertEqual(result, "<root>abc</root>")
+
+ def test_if_elseif(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess('<root><?if True?>ok<?endif?></root>')
+ self.assertEqual(result, "<root>ok</root>")
+ result = xpp.preprocess('<root><?if False?>ok<?endif?></root>')
+ self.assertEqual(result, "<root></root>")
+ result = xpp.preprocess('<root><?if True?>ok<?else?>ko<?endif?></root>')
+ self.assertEqual(result, "<root>ok</root>")
+ result = xpp.preprocess('<root><?if False?>ok<?else?>ko<?endif?></root>')
+ self.assertEqual(result, "<root>ko</root>")
+ result = xpp.preprocess(
+ '<root><?if False?>ok<?elseif True?>ok2<?else?>ko<?endif?></root>'
+ )
+ self.assertEqual(result, "<root>ok2</root>")
+ result = xpp.preprocess(
+ '<root><?if False?>ok<?elseif False?>ok<?else?>ko<?endif?></root>'
+ )
+ self.assertEqual(result, "<root>ko</root>")
+
+ def test_ifdef(self):
+ xpp = xmlpp.Preprocessor()
+ result = xpp.preprocess('<root><?ifdef USER?>ok<?else?>ko<?endif?></root>')
+ self.assertEqual(result, "<root>ko</root>")
+ result = xpp.preprocess(
+ '<?define USER=FOO?><root><?ifdef USER?>ok<?else?>ko<?endif?></root>'
+ )
+ self.assertEqual(result, "<root>ok</root>")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/scripts/xml-preprocess.py b/scripts/xml-preprocess.py
new file mode 100755
index 0000000..57f1d28
--- /dev/null
+++ b/scripts/xml-preprocess.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2017-2019 Tony Su
+# Copyright (c) 2023 Red Hat, Inc.
+#
+# SPDX-License-Identifier: MIT
+#
+# Adapted from https://github.com/peitaosu/XML-Preprocessor
+#
+"""This is a XML Preprocessor which can be used to process your XML file before
+you use it, to process conditional statements, variables, iteration
+statements, error/warning, execute command, etc.
+
+## XML Schema
+
+### Include Files
+```
+<?include path/to/file ?>
+```
+
+### Variables
+```
+$(env.EnvironmentVariable)
+
+$(sys.SystemVariable)
+
+$(var.CustomVariable)
+```
+
+### Conditional Statements
+```
+<?if ?>
+
+<?ifdef ?>
+
+<?ifndef ?>
+
+<?else?>
+
+<?elseif ?>
+
+<?endif?>
+```
+
+### Iteration Statements
+```
+<?foreach VARNAME in 1;2;3?>
+ $(var.VARNAME)
+<?endforeach?>
+```
+
+### Errors and Warnings
+```
+<?error "This is error message!" ?>
+
+<?warning "This is warning message!" ?>
+```
+
+### Commands
+```
+<? cmd "echo hello world" ?>
+```
+"""
+
+import os
+import platform
+import re
+import subprocess
+import sys
+from typing import Optional
+from xml.dom import minidom
+
+
+class Preprocessor():
+ """This class holds the XML preprocessing state"""
+
+ def __init__(self):
+ self.sys_vars = {
+ "ARCH": platform.architecture()[0],
+ "SOURCE": os.path.abspath(__file__),
+ "CURRENT": os.getcwd(),
+ }
+ self.cus_vars = {}
+
+ def _pp_include(self, xml_str: str) -> str:
+ include_regex = r"(<\?include([\w\s\\/.:_-]+)\s*\?>)"
+ matches = re.findall(include_regex, xml_str)
+ for group_inc, group_xml in matches:
+ inc_file_path = group_xml.strip()
+ with open(inc_file_path, "r", encoding="utf-8") as inc_file:
+ inc_file_content = inc_file.read()
+ xml_str = xml_str.replace(group_inc, inc_file_content)
+ return xml_str
+
+ def _pp_env_var(self, xml_str: str) -> str:
+ envvar_regex = r"(\$\(env\.(\w+)\))"
+ matches = re.findall(envvar_regex, xml_str)
+ for group_env, group_var in matches:
+ xml_str = xml_str.replace(group_env, os.environ[group_var])
+ return xml_str
+
+ def _pp_sys_var(self, xml_str: str) -> str:
+ sysvar_regex = r"(\$\(sys\.(\w+)\))"
+ matches = re.findall(sysvar_regex, xml_str)
+ for group_sys, group_var in matches:
+ xml_str = xml_str.replace(group_sys, self.sys_vars[group_var])
+ return xml_str
+
+ def _pp_cus_var(self, xml_str: str) -> str:
+ define_regex = r"(<\?define\s*(\w+)\s*=\s*([\w\s\"]+)\s*\?>)"
+ matches = re.findall(define_regex, xml_str)
+ for group_def, group_name, group_var in matches:
+ group_name = group_name.strip()
+ group_var = group_var.strip().strip("\"")
+ self.cus_vars[group_name] = group_var
+ xml_str = xml_str.replace(group_def, "")
+ cusvar_regex = r"(\$\(var\.(\w+)\))"
+ matches = re.findall(cusvar_regex, xml_str)
+ for group_cus, group_var in matches:
+ xml_str = xml_str.replace(
+ group_cus,
+ self.cus_vars.get(group_var, "")
+ )
+ return xml_str
+
+ def _pp_foreach(self, xml_str: str) -> str:
+ foreach_regex = r"(<\?foreach\s+(\w+)\s+in\s+([\w;]+)\s*\?>(.*)<\?endforeach\?>)"
+ matches = re.findall(foreach_regex, xml_str)
+ for group_for, group_name, group_vars, group_text in matches:
+ group_texts = ""
+ for var in group_vars.split(";"):
+ self.cus_vars[group_name] = var
+ group_texts += self._pp_cus_var(group_text)
+ xml_str = xml_str.replace(group_for, group_texts)
+ return xml_str
+
+ def _pp_error_warning(self, xml_str: str) -> str:
+ error_regex = r"<\?error\s*\"([^\"]+)\"\s*\?>"
+ matches = re.findall(error_regex, xml_str)
+ for group_var in matches:
+ raise RuntimeError("[Error]: " + group_var)
+ warning_regex = r"(<\?warning\s*\"([^\"]+)\"\s*\?>)"
+ matches = re.findall(warning_regex, xml_str)
+ for group_wrn, group_var in matches:
+ print("[Warning]: " + group_var)
+ xml_str = xml_str.replace(group_wrn, "")
+ return xml_str
+
+ def _pp_if_eval(self, xml_str: str) -> str:
+ ifelif_regex = (
+ r"(<\?(if|elseif)\s*([^\"\s=<>!]+)\s*([!=<>]+)\s*\"*([^\"=<>!]+)\"*\s*\?>)"
+ )
+ matches = re.findall(ifelif_regex, xml_str)
+ for ifelif, tag, left, operator, right in matches:
+ if "<" in operator or ">" in operator:
+ result = eval(f"{left} {operator} {right}")
+ else:
+ result = eval(f'"{left}" {operator} "{right}"')
+ xml_str = xml_str.replace(ifelif, f"<?{tag} {result}?>")
+ return xml_str
+
+ def _pp_ifdef_ifndef(self, xml_str: str) -> str:
+ ifndef_regex = r"(<\?(ifdef|ifndef)\s*([\w]+)\s*\?>)"
+ matches = re.findall(ifndef_regex, xml_str)
+ for group_ifndef, group_tag, group_var in matches:
+ if group_tag == "ifdef":
+ result = group_var in self.cus_vars
+ else:
+ result = group_var not in self.cus_vars
+ xml_str = xml_str.replace(group_ifndef, f"<?if {result}?>")
+ return xml_str
+
+ def _pp_if_elseif(self, xml_str: str) -> str:
+ if_elif_else_regex = (
+ r"(<\?if\s(True|False)\?>"
+ r"(.*?)"
+ r"<\?elseif\s(True|False)\?>"
+ r"(.*?)"
+ r"<\?else\?>"
+ r"(.*?)"
+ r"<\?endif\?>)"
+ )
+ if_else_regex = (
+ r"(<\?if\s(True|False)\?>"
+ r"(.*?)"
+ r"<\?else\?>"
+ r"(.*?)"
+ r"<\?endif\?>)"
+ )
+ if_regex = r"(<\?if\s(True|False)\?>(.*?)<\?endif\?>)"
+ matches = re.findall(if_elif_else_regex, xml_str, re.DOTALL)
+ for (group_full, group_if, group_if_elif, group_elif,
+ group_elif_else, group_else) in matches:
+ result = ""
+ if group_if == "True":
+ result = group_if_elif
+ elif group_elif == "True":
+ result = group_elif_else
+ else:
+ result = group_else
+ xml_str = xml_str.replace(group_full, result)
+ matches = re.findall(if_else_regex, xml_str, re.DOTALL)
+ for group_full, group_if, group_if_else, group_else in matches:
+ result = ""
+ if group_if == "True":
+ result = group_if_else
+ else:
+ result = group_else
+ xml_str = xml_str.replace(group_full, result)
+ matches = re.findall(if_regex, xml_str, re.DOTALL)
+ for group_full, group_if, group_text in matches:
+ result = ""
+ if group_if == "True":
+ result = group_text
+ xml_str = xml_str.replace(group_full, result)
+ return xml_str
+
+ def _pp_command(self, xml_str: str) -> str:
+ cmd_regex = r"(<\?cmd\s*\"([^\"]+)\"\s*\?>)"
+ matches = re.findall(cmd_regex, xml_str)
+ for group_cmd, group_exec in matches:
+ output = subprocess.check_output(
+ group_exec, shell=True,
+ text=True, stderr=subprocess.STDOUT
+ )
+ xml_str = xml_str.replace(group_cmd, output)
+ return xml_str
+
+ def _pp_blanks(self, xml_str: str) -> str:
+ right_blank_regex = r">[\n\s\t\r]*"
+ left_blank_regex = r"[\n\s\t\r]*<"
+ xml_str = re.sub(right_blank_regex, ">", xml_str)
+ xml_str = re.sub(left_blank_regex, "<", xml_str)
+ return xml_str
+
+ def preprocess(self, xml_str: str) -> str:
+ fns = [
+ self._pp_blanks,
+ self._pp_include,
+ self._pp_foreach,
+ self._pp_env_var,
+ self._pp_sys_var,
+ self._pp_cus_var,
+ self._pp_if_eval,
+ self._pp_ifdef_ifndef,
+ self._pp_if_elseif,
+ self._pp_command,
+ self._pp_error_warning,
+ ]
+
+ while True:
+ changed = False
+ for func in fns:
+ out_xml = func(xml_str)
+ if not changed and out_xml != xml_str:
+ changed = True
+ xml_str = out_xml
+ if not changed:
+ break
+
+ return xml_str
+
+
+def preprocess_xml(path: str) -> str:
+ with open(path, "r", encoding="utf-8") as original_file:
+ input_xml = original_file.read()
+
+ proc = Preprocessor()
+ return proc.preprocess(input_xml)
+
+
+def save_xml(xml_str: str, path: Optional[str]):
+ xml = minidom.parseString(xml_str)
+ with open(path, "w", encoding="utf-8") if path else sys.stdout as output_file:
+ output_file.write(xml.toprettyxml())
+
+
+def main():
+ if len(sys.argv) < 2:
+ print("Usage: xml-preprocessor input.xml [output.xml]")
+ sys.exit(1)
+
+ output_file = None
+ if len(sys.argv) == 3:
+ output_file = sys.argv[2]
+
+ input_file = sys.argv[1]
+ output_xml = preprocess_xml(input_file)
+ save_xml(output_xml, output_file)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c
index fef025a..21edaa1 100644
--- a/tests/qtest/dbus-display-test.c
+++ b/tests/qtest/dbus-display-test.c
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
+#include "qemu/sockets.h"
#include "qemu/dbus.h"
#include "qemu/sockets.h"
#include <gio/gio.h>
@@ -14,7 +15,11 @@ test_dbus_p2p_from_fd(int fd)
g_autoptr(GSocketConnection) socketc = NULL;
GDBusConnection *conn;
+#ifdef WIN32
+ socket = g_socket_new_from_fd(_get_osfhandle(fd), &err);
+#else
socket = g_socket_new_from_fd(fd, &err);
+#endif
g_assert_no_error(err);
socketc = g_socket_connection_factory_create_connection(socket);
@@ -126,7 +131,10 @@ test_dbus_console_registered(GObject *source_object,
qemu_dbus_display1_console_call_register_listener_finish(
QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
- NULL, res, &err);
+#ifndef WIN32
+ NULL,
+#endif
+ res, &err);
g_assert_no_error(err);
test->listener_conn = g_thread_join(test->thread);
@@ -145,17 +153,25 @@ test_dbus_display_console(void)
g_autoptr(GError) err = NULL;
g_autoptr(GDBusConnection) conn = NULL;
g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL;
- g_autoptr(GUnixFDList) fd_list = NULL;
g_autoptr(GMainLoop) loop = NULL;
QTestState *qts = NULL;
- int pair[2], idx;
+ int pair[2];
TestDBusConsoleRegister test;
+#ifdef WIN32
+ WSAPROTOCOL_INFOW info;
+ g_autoptr(GVariant) listener = NULL;
+#else
+ g_autoptr(GUnixFDList) fd_list = NULL;
+ int idx;
+#endif
test_setup(&qts, &conn);
g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
+#ifndef WIN32
fd_list = g_unix_fd_list_new();
idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
+#endif
console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY(
qemu_dbus_display1_console_proxy_new_sync(
@@ -171,12 +187,33 @@ test_dbus_display_console(void)
test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread,
GINT_TO_POINTER(pair[0]));
+#ifdef WIN32
+ if (WSADuplicateSocketW(_get_osfhandle(pair[1]),
+ GetProcessId((HANDLE) qtest_pid(qts)),
+ &info) == SOCKET_ERROR)
+ {
+ g_autofree char *emsg = g_win32_error_message(WSAGetLastError());
+ g_error("WSADuplicateSocket failed: %s", emsg);
+ }
+ close(pair[1]);
+ listener = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ &info,
+ sizeof(info),
+ 1);
+#endif
+
qemu_dbus_display1_console_call_register_listener(
QEMU_DBUS_DISPLAY1_CONSOLE(console),
+#ifdef WIN32
+ listener,
+#else
g_variant_new_handle(idx),
+#endif
G_DBUS_CALL_FLAGS_NONE,
-1,
+#ifndef WIN32
fd_list,
+#endif
NULL,
test_dbus_console_registered,
&test);
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index de03ef5..79152f0 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -142,6 +142,11 @@ static int socket_accept(int sock)
return ret;
}
+pid_t qtest_pid(QTestState *s)
+{
+ return s->qemu_pid;
+}
+
bool qtest_probe_child(QTestState *s)
{
pid_t pid = s->qemu_pid;
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index a12acf7..913acc3 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -985,4 +985,13 @@ void qtest_qom_set_bool(QTestState *s, const char *path, const char *property,
* Returns: Value retrieved from property.
*/
bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property);
+
+/**
+ * qtest_pid:
+ * @s: QTestState instance to operate on.
+ *
+ * Returns: the PID of the QEMU process, or <= 0
+ */
+pid_t qtest_pid(QTestState *s);
+
#endif
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 5fa6833..74630f6 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -104,7 +104,7 @@ qtests_i386 = \
'numa-test'
]
-if dbus_display and targetos != 'windows'
+if dbus_display
qtests_i386 += ['dbus-display-test']
endif
diff --git a/ui/console.c b/ui/console.c
index e173731..c1544e0 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1223,7 +1223,8 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl,
con->scanout.texture.x,
con->scanout.texture.y,
con->scanout.texture.width,
- con->scanout.texture.height);
+ con->scanout.texture.height,
+ con->scanout.texture.d3d_tex2d);
}
}
@@ -1513,18 +1514,59 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
return s;
}
+#ifdef WIN32
+void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
+ HANDLE h, uint32_t offset)
+{
+ assert(!surface->handle);
+
+ surface->handle = h;
+ surface->handle_offset = offset;
+}
+
+static void
+win32_pixman_image_destroy(pixman_image_t *image, void *data)
+{
+ DisplaySurface *surface = data;
+
+ if (!surface->handle) {
+ return;
+ }
+
+ assert(surface->handle_offset == 0);
+
+ qemu_win32_map_free(
+ pixman_image_get_data(surface->image),
+ surface->handle,
+ &error_warn
+ );
+}
+#endif
+
DisplaySurface *qemu_create_displaysurface(int width, int height)
{
- DisplaySurface *surface = g_new0(DisplaySurface, 1);
+ DisplaySurface *surface;
+ void *bits = NULL;
+#ifdef WIN32
+ HANDLE handle = NULL;
+#endif
- trace_displaysurface_create(surface, width, height);
- surface->format = PIXMAN_x8r8g8b8;
- surface->image = pixman_image_create_bits(surface->format,
- width, height,
- NULL, width * 4);
- assert(surface->image != NULL);
+ trace_displaysurface_create(width, height);
+
+#ifdef WIN32
+ bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
+#endif
+
+ surface = qemu_create_displaysurface_from(
+ width, height,
+ PIXMAN_x8r8g8b8,
+ width * 4, bits
+ );
surface->flags = QEMU_ALLOCATED_FLAG;
+#ifdef WIN32
+ qemu_displaysurface_win32_set_handle(surface, handle, 0);
+#endif
return surface;
}
@@ -1540,6 +1582,10 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height,
width, height,
(void *)data, linesize);
assert(surface->image != NULL);
+#ifdef WIN32
+ pixman_image_set_destroy_function(surface->image,
+ win32_pixman_image_destroy, surface);
+#endif
return surface;
}
@@ -1635,6 +1681,71 @@ static bool console_compatible_with(QemuConsole *con,
return true;
}
+void console_handle_touch_event(QemuConsole *con,
+ struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
+ uint64_t num_slot,
+ int width, int height,
+ double x, double y,
+ InputMultiTouchType type,
+ Error **errp)
+{
+ struct touch_slot *slot;
+ bool needs_sync = false;
+ int update;
+ int i;
+
+ if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
+ error_setg(errp,
+ "Unexpected touch slot number: % " PRId64" >= %d",
+ num_slot, INPUT_EVENT_SLOTS_MAX);
+ return;
+ }
+
+ slot = &touch_slots[num_slot];
+ slot->x = x;
+ slot->y = y;
+
+ if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
+ slot->tracking_id = num_slot;
+ }
+
+ for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
+ if (i == num_slot) {
+ update = type;
+ } else {
+ update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
+ }
+
+ slot = &touch_slots[i];
+
+ if (slot->tracking_id == -1) {
+ continue;
+ }
+
+ if (update == INPUT_MULTI_TOUCH_TYPE_END) {
+ slot->tracking_id = -1;
+ qemu_input_queue_mtt(con, update, i, slot->tracking_id);
+ needs_sync = true;
+ } else {
+ qemu_input_queue_mtt(con, update, i, slot->tracking_id);
+ qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
+ qemu_input_queue_mtt_abs(con,
+ INPUT_AXIS_X, (int) slot->x,
+ 0, width,
+ i, slot->tracking_id);
+ qemu_input_queue_mtt_abs(con,
+ INPUT_AXIS_Y, (int) slot->y,
+ 0, height,
+ i, slot->tracking_id);
+ needs_sync = true;
+ }
+ }
+
+ if (needs_sync) {
+ qemu_input_event_sync();
+ }
+}
+
void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
{
/* display has opengl support */
@@ -2005,7 +2116,8 @@ void dpy_gl_scanout_texture(QemuConsole *con,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t width, uint32_t height)
+ uint32_t width, uint32_t height,
+ void *d3d_tex2d)
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
@@ -2013,7 +2125,7 @@ void dpy_gl_scanout_texture(QemuConsole *con,
con->scanout.kind = SCANOUT_TEXTURE;
con->scanout.texture = (ScanoutTexture) {
backing_id, backing_y_0_top, backing_width, backing_height,
- x, y, width, height
+ x, y, width, height, d3d_tex2d,
};
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != (dcl->con ? dcl->con : active_console)) {
@@ -2023,7 +2135,8 @@ void dpy_gl_scanout_texture(QemuConsole *con,
dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
backing_y_0_top,
backing_width, backing_height,
- x, y, width, height);
+ x, y, width, height,
+ d3d_tex2d);
}
}
}
@@ -2306,7 +2419,7 @@ QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
if (con == NULL) {
con = active_console;
}
- return con->cursor;
+ return con ? con->cursor : NULL;
}
bool qemu_console_is_visible(QemuConsole *con)
diff --git a/ui/dbus-chardev.c b/ui/dbus-chardev.c
index 940ef93..1d3a712 100644
--- a/ui/dbus-chardev.c
+++ b/ui/dbus-chardev.c
@@ -27,7 +27,9 @@
#include "qemu/config-file.h"
#include "qemu/option.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
#include "dbus.h"
@@ -112,13 +114,20 @@ static gboolean
dbus_chr_register(
DBusChardev *dc,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_stream,
QemuDBusDisplay1Chardev *object)
{
g_autoptr(GError) err = NULL;
int fd;
+#ifdef G_OS_WIN32
+ if (!dbus_win32_import_socket(invocation, arg_stream, &fd)) {
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+#else
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_stream), &err);
if (err) {
g_dbus_method_invocation_return_error(
@@ -128,13 +137,18 @@ dbus_chr_register(
"Couldn't get peer FD: %s", err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#endif
if (qemu_chr_add_client(CHARDEV(dc), fd) < 0) {
g_dbus_method_invocation_return_error(invocation,
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't register FD!");
+#ifdef G_OS_WIN32
+ closesocket(fd);
+#else
close(fd);
+#endif
return DBUS_METHOD_INVOCATION_HANDLED;
}
@@ -142,7 +156,11 @@ dbus_chr_register(
"owner", g_dbus_method_invocation_get_sender(invocation),
NULL);
- qemu_dbus_display1_chardev_complete_register(object, invocation, NULL);
+ qemu_dbus_display1_chardev_complete_register(object, invocation
+#ifndef G_OS_WIN32
+ , NULL
+#endif
+ );
return DBUS_METHOD_INVOCATION_HANDLED;
}
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index f77bc49..e19774f 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -28,10 +28,14 @@
#include "ui/kbd-state.h"
#include "trace.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
#include "dbus.h"
+static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX];
+
struct _DBusDisplayConsole {
GDBusObjectSkeleton parent_instance;
DisplayChangeListener dcl;
@@ -44,6 +48,7 @@ struct _DBusDisplayConsole {
QKbdState *kbd;
QemuDBusDisplay1Mouse *iface_mouse;
+ QemuDBusDisplay1MultiTouch *iface_touch;
gboolean last_set;
guint last_x;
guint last_y;
@@ -93,7 +98,8 @@ dbus_gl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
@@ -204,10 +210,47 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc,
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#ifdef G_OS_WIN32
+bool
+dbus_win32_import_socket(GDBusMethodInvocation *invocation,
+ GVariant *arg_listener, int *socket)
+{
+ gsize n;
+ WSAPROTOCOL_INFOW *info = (void *)g_variant_get_fixed_array(arg_listener, &n, 1);
+
+ if (!info || n != sizeof(*info)) {
+ g_dbus_method_invocation_return_error(
+ invocation,
+ DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_FAILED,
+ "Failed to get socket infos");
+ return false;
+ }
+
+ *socket = WSASocketW(FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ info, 0, 0);
+ if (*socket == INVALID_SOCKET) {
+ g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError());
+ g_dbus_method_invocation_return_error(
+ invocation,
+ DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_FAILED,
+ "Couldn't create socket: %s", emsg);
+ return false;
+ }
+
+ return true;
+}
+#endif
+
static gboolean
dbus_console_register_listener(DBusDisplayConsole *ddc,
GDBusMethodInvocation *invocation,
+#ifdef G_OS_UNIX
GUnixFDList *fd_list,
+#endif
GVariant *arg_listener)
{
const char *sender = g_dbus_method_invocation_get_sender(invocation);
@@ -229,6 +272,11 @@ dbus_console_register_listener(DBusDisplayConsole *ddc,
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#ifdef G_OS_WIN32
+ if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+#else
fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
if (err) {
g_dbus_method_invocation_return_error(
@@ -238,6 +286,7 @@ dbus_console_register_listener(DBusDisplayConsole *ddc,
"Couldn't get peer fd: %s", err->message);
return DBUS_METHOD_INVOCATION_HANDLED;
}
+#endif
socket = g_socket_new_from_fd(fd, &err);
if (err) {
@@ -246,13 +295,21 @@ dbus_console_register_listener(DBusDisplayConsole *ddc,
DBUS_DISPLAY_ERROR,
DBUS_DISPLAY_ERROR_FAILED,
"Couldn't make a socket: %s", err->message);
+#ifdef G_OS_WIN32
+ closesocket(fd);
+#else
close(fd);
+#endif
return DBUS_METHOD_INVOCATION_HANDLED;
}
socket_conn = g_socket_connection_factory_create_connection(socket);
qemu_dbus_display1_console_complete_register_listener(
- ddc->iface, invocation, NULL);
+ ddc->iface, invocation
+#ifdef G_OS_UNIX
+ , NULL
+#endif
+ );
listener_conn = g_dbus_connection_new_sync(
G_IO_STREAM(socket_conn),
@@ -346,6 +403,46 @@ dbus_mouse_rel_motion(DBusDisplayConsole *ddc,
}
static gboolean
+dbus_touch_send_event(DBusDisplayConsole *ddc,
+ GDBusMethodInvocation *invocation,
+ guint kind, uint64_t num_slot,
+ double x, double y)
+{
+ Error *error = NULL;
+ int width, height;
+ trace_dbus_touch_send_event(kind, num_slot, x, y);
+
+ if (kind != INPUT_MULTI_TOUCH_TYPE_BEGIN &&
+ kind != INPUT_MULTI_TOUCH_TYPE_UPDATE &&
+ kind != INPUT_MULTI_TOUCH_TYPE_CANCEL &&
+ kind != INPUT_MULTI_TOUCH_TYPE_END)
+ {
+ g_dbus_method_invocation_return_error(
+ invocation, DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_INVALID,
+ "Invalid touch event kind");
+ return DBUS_METHOD_INVOCATION_HANDLED;
+ }
+ width = qemu_console_get_width(ddc->dcl.con, 0);
+ height = qemu_console_get_height(ddc->dcl.con, 0);
+
+ console_handle_touch_event(ddc->dcl.con, touch_slots,
+ num_slot, width, height,
+ x, y, kind, &error);
+ if (error != NULL) {
+ g_dbus_method_invocation_return_error(
+ invocation, DBUS_DISPLAY_ERROR,
+ DBUS_DISPLAY_ERROR_INVALID,
+ error_get_pretty(error), NULL);
+ error_free(error);
+ } else {
+ qemu_dbus_display1_multi_touch_complete_send_event(ddc->iface_touch,
+ invocation);
+ }
+ return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
dbus_mouse_set_pos(DBusDisplayConsole *ddc,
GDBusMethodInvocation *invocation,
guint x, guint y)
@@ -440,7 +537,13 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
g_autofree char *label = NULL;
char device_addr[256] = "";
DBusDisplayConsole *ddc;
- int idx;
+ int idx, i;
+ const char *interfaces[] = {
+ "org.qemu.Display1.Keyboard",
+ "org.qemu.Display1.Mouse",
+ "org.qemu.Display1.MultiTouch",
+ NULL
+ };
assert(display);
assert(con);
@@ -465,6 +568,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
"width", qemu_console_get_width(con, 0),
"height", qemu_console_get_height(con, 0),
"device-address", device_addr,
+ "interfaces", interfaces,
NULL);
g_object_connect(ddc->iface,
"swapped-signal::handle-register-listener",
@@ -495,6 +599,20 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse));
+ ddc->iface_touch = qemu_dbus_display1_multi_touch_skeleton_new();
+ g_object_connect(ddc->iface_touch,
+ "swapped-signal::handle-send-event", dbus_touch_send_event, ddc,
+ NULL);
+ qemu_dbus_display1_multi_touch_set_max_slots(ddc->iface_touch,
+ INPUT_EVENT_SLOTS_MAX);
+ g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
+ G_DBUS_INTERFACE_SKELETON(ddc->iface_touch));
+
+ for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) {
+ struct touch_slot *slot = &touch_slots[i];
+ slot->tracking_id = -1;
+ }
+
register_displaychangelistener(&ddc->dcl);
ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change;
qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml
index c3b2293..f0e2fac 100644
--- a/ui/dbus-display1.xml
+++ b/ui/dbus-display1.xml
@@ -26,6 +26,20 @@
The list of consoles available on ``/org/qemu/Display1/Console_$id``.
-->
<property name="ConsoleIDs" type="au" access="read"/>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/VM object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
@@ -39,8 +53,9 @@
"Text" (see :dbus:prop:`Type` and other properties).
Interactions with a console may be done with
- :dbus:iface:`org.qemu.Display1.Keyboard` and
- :dbus:iface:`org.qemu.Display1.Mouse` interfaces when available.
+ :dbus:iface:`org.qemu.Display1.Keyboard`,
+ :dbus:iface:`org.qemu.Display1.Mouse` and
+ :dbus:iface:`org.qemu.Display1.MultiTouch` interfaces when available.
-->
<interface name="org.qemu.Display1.Console">
<!--
@@ -56,7 +71,13 @@
:dbus:iface:`org.qemu.Display1.Listener` interface.
-->
<method name="RegisterListener">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="listener" direction="in"/>
+ <?endif?>
</method>
<!--
@@ -120,12 +141,27 @@
The device address (ex: "pci/0000/02.0").
-->
<property name="DeviceAddress" type="s" access="read"/>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ ``/org/qemu/Display1/Console_$id`` object, and can be used to detect the
+ capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
org.qemu.Display1.Keyboard:
- This interface in implemented on ``/org/qemu/Display1/Console_$id`` (see
+ This interface is optionally implemented on
+ ``/org/qemu/Display1/Console_$id`` (see
:dbus:iface:`~org.qemu.Display1.Console`).
-->
<interface name="org.qemu.Display1.Keyboard">
@@ -164,7 +200,8 @@
<!--
org.qemu.Display1.Mouse:
- This interface in implemented on ``/org/qemu/Display1/Console_$id`` (see
+ This interface is optionally implemented on
+ ``/org/qemu/Display1/Console_$id`` (see
:dbus:iface:`~org.qemu.Display1.Console` documentation).
.. _dbus-button-values:
@@ -237,6 +274,46 @@
</interface>
<!--
+ org.qemu.Display1.MultiTouch:
+
+ This interface in implemented on ``/org/qemu/Display1/Console_$id`` (see
+ :dbus:iface:`~org.qemu.Display1.Console` documentation).
+
+ .. _dbus-kind-values:
+
+ **Kind values**::
+
+ Begin = 0
+ Update = 1
+ End = 2
+ Cancel = 3
+ -->
+ <interface name="org.qemu.Display1.MultiTouch">
+ <!--
+ SendEvent:
+ @kind: The touch event kind
+ @num_slot: The slot number.
+ @x: The x coordinates.
+ @y: The y coordinates.
+
+ Send a touch gesture event.
+ -->
+ <method name="SendEvent">
+ <arg type="u" name="kind" direction="in"/>
+ <arg type="t" name="num_slot" direction="in"/>
+ <arg type="d" name="x" direction="in"/>
+ <arg type="d" name="y" direction="in"/>
+ </method>
+
+ <!--
+ MaxSlots:
+
+ The maximum number of slots.
+ -->
+ <property name="MaxSlots" type="i" access="read"/>
+ </interface>
+
+ <!--
org.qemu.Display1.Listener:
This client-side interface must be available on
@@ -293,6 +370,7 @@
</arg>
</method>
+ <?if $(env.TARGETOS) != windows?>
<!--
ScanoutDMABUF:
@dmabuf: the DMABUF file descriptor.
@@ -331,6 +409,7 @@
<arg type="i" name="width" direction="in"/>
<arg type="i" name="height" direction="in"/>
</method>
+ <?endif?>
<!--
Disable:
@@ -374,6 +453,116 @@
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/Listener object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
+ </interface>
+
+ <!--
+ org.qemu.Display1.Listener.Win32.Map:
+
+ This optional client-side interface can complement
+ org.qemu.Display1.Listener on ``/org/qemu/Display1/Listener`` for Windows
+ specific shared memory scanouts.
+ -->
+ <interface name="org.qemu.Display1.Listener.Win32.Map">
+ <!--
+ ScanoutMap:
+ @handle: the shared map handle value.
+ @offset: mapping offset.
+ @width: display width, in pixels.
+ @height: display height, in pixels.
+ @stride: stride, in bytes.
+ @pixman_format: image format (ex: ``PIXMAN_X8R8G8B8``).
+
+ Resize and update the display content with a shared map.
+ -->
+ <method name="ScanoutMap">
+ <arg type="t" name="handle" direction="in"/>
+ <arg type="u" name="offset" direction="in"/>
+ <arg type="u" name="width" direction="in"/>
+ <arg type="u" name="height" direction="in"/>
+ <arg type="u" name="stride" direction="in"/>
+ <arg type="u" name="pixman_format" direction="in"/>
+ </method>
+
+ <!--
+ UpdateMap:
+ @x: the X update position, in pixels.
+ @y: the Y update position, in pixels.
+ @width: the update width, in pixels.
+ @height: the update height, in pixels.
+
+ Update the display content with the current shared map and the given region.
+ -->
+ <method name="UpdateMap">
+ <arg type="i" name="x" direction="in"/>
+ <arg type="i" name="y" direction="in"/>
+ <arg type="i" name="width" direction="in"/>
+ <arg type="i" name="height" direction="in"/>
+ </method>
+ </interface>
+
+ <!--
+ org.qemu.Display1.Listener.Win32.D3d11:
+
+ This optional client-side interface can complement
+ org.qemu.Display1.Listener on ``/org/qemu/Display1/Listener`` for Windows
+ specific Direct3D texture sharing of the scanouts.
+ -->
+ <interface name="org.qemu.Display1.Listener.Win32.D3d11">
+ <!--
+ ScanoutTexture2d:
+ @handle: the NT handle for the shared texture (to be opened back with ID3D11Device1::OpenSharedResource1).
+ @texture_width: texture width, in pixels.
+ @texture_height: texture height, in pixels.
+ @y0_top: whether Y position 0 is the top or not.
+ @x: the X scanout position, in pixels.
+ @y: the Y scanout position, in pixels.
+ @width: the scanout width, in pixels.
+ @height: the scanout height, in pixels.
+
+ Resize and update the display content with a Direct3D 11 2D texture.
+ You must acquire and release the associated KeyedMutex 0 during rendering.
+ -->
+ <method name="ScanoutTexture2d">
+ <arg type="t" name="handle" direction="in"/>
+ <arg type="u" name="texture_width" direction="in"/>
+ <arg type="u" name="texture_height" direction="in"/>
+ <arg type="b" name="y0_top" direction="in"/>
+ <arg type="u" name="x" direction="in"/>
+ <arg type="u" name="y" direction="in"/>
+ <arg type="u" name="width" direction="in"/>
+ <arg type="u" name="height" direction="in"/>
+ </method>
+
+ <!--
+ UpdateTexture2d:
+ @x: the X update position, in pixels.
+ @y: the Y update position, in pixels.
+ @width: the update width, in pixels.
+ @height: the update height, in pixels.
+
+ Update the display content with the current Direct3D 2D texture and the given region.
+ You must acquire and release the associated KeyedMutex 0 during rendering.
+ -->
+ <method name="UpdateTexture2d">
+ <arg type="i" name="x" direction="in"/>
+ <arg type="i" name="y" direction="in"/>
+ <arg type="i" name="width" direction="in"/>
+ <arg type="i" name="height" direction="in"/>
+ </method>
</interface>
<!--
@@ -471,6 +660,20 @@
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/Clipboard object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
@@ -491,7 +694,13 @@
:dbus:iface:`org.qemu.Display1.AudioOutListener` interface.
-->
<method name="RegisterOutListener">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="listener" direction="in"/>
+ <?endif?>
</method>
<!--
@@ -506,8 +715,28 @@
:dbus:iface:`org.qemu.Display1.AudioInListener` interface.
-->
<method name="RegisterInListener">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="listener" direction="in"/>
+ <?endif?>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/Audio object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
@@ -594,6 +823,20 @@
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/AudioOutListener object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
@@ -682,6 +925,20 @@
<annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
</arg>
</method>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ /org/qemu/Display1/AudioInListener object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
<!--
@@ -719,7 +976,13 @@
The current handler, if any, will be replaced.
-->
<method name="Register">
+ <?if $(env.TARGETOS) == windows?>
+ <arg type="ay" name="listener" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <?else?>
<arg type="h" name="stream" direction="in"/>
+ <?endif?>
</method>
<!--
@@ -757,5 +1020,19 @@
The D-Bus unique name of the registered handler.
-->
<property name="Owner" type="s" access="read"/>
+
+ <!--
+ Interfaces:
+
+ This property lists extra interfaces provided by the
+ ``/org/qemu/Display1/Chardev_$i`` object, and can be used to detect
+ the capabilities with which they are communicating.
+
+ Unlike the standard D-Bus Introspectable interface, querying this
+ property does not require parsing XML.
+
+ (earlier version of the display interface do not provide this property)
+ -->
+ <property name="Interfaces" type="as" access="read"/>
</interface>
</node>
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 23034ee..e10162b 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -23,9 +23,16 @@
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "dbus.h"
+#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
+#endif
+#ifdef WIN32
+#include <d3d11.h>
+#include <dxgi1_2.h>
+#endif
#ifdef CONFIG_OPENGL
#include "ui/shader.h"
@@ -34,6 +41,15 @@
#endif
#include "trace.h"
+static void dbus_gfx_switch(DisplayChangeListener *dcl,
+ struct DisplaySurface *new_surface);
+
+enum share_kind {
+ SHARE_KIND_NONE,
+ SHARE_KIND_MAPPED,
+ SHARE_KIND_D3DTEX,
+};
+
struct _DBusDisplayListener {
GObject parent;
@@ -45,21 +61,142 @@ struct _DBusDisplayListener {
DisplayChangeListener dcl;
DisplaySurface *ds;
+ enum share_kind ds_share;
+
int gl_updates;
+
+ bool ds_mapped;
+ bool can_share_map;
+
+#ifdef WIN32
+ QemuDBusDisplay1ListenerWin32Map *map_proxy;
+ QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy;
+ HANDLE peer_process;
+ ID3D11Texture2D *d3d_texture;
+#ifdef CONFIG_OPENGL
+ egl_fb fb;
+#endif
+#endif
};
G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+static void dbus_gfx_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h);
+
+#ifdef CONFIG_OPENGL
+static void dbus_scanout_disable(DisplayChangeListener *dcl)
+{
+ DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+
+ qemu_dbus_display1_listener_call_disable(
+ ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+#ifdef WIN32
+static bool d3d_texture2d_share(ID3D11Texture2D *d3d_texture,
+ HANDLE *handle, Error **errp)
+{
+ IDXGIResource1 *dxgiResource = NULL;
+ HRESULT hr;
+
+ hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
+ &IID_IDXGIResource1,
+ (void **)&dxgiResource);
+ if (FAILED(hr)) {
+ goto fail;
+ }
+
+ hr = dxgiResource->lpVtbl->CreateSharedHandle(
+ dxgiResource,
+ NULL,
+ DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
+ NULL,
+ handle
+ );
+
+ dxgiResource->lpVtbl->Release(dxgiResource);
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+
+fail:
+ error_setg_win32(errp, GetLastError(), "failed to create shared handle");
+ return false;
+}
+
+static bool d3d_texture2d_acquire0(ID3D11Texture2D *d3d_texture, Error **errp)
+{
+ IDXGIKeyedMutex *dxgiMutex = NULL;
+ HRESULT hr;
+
+ hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
+ &IID_IDXGIKeyedMutex,
+ (void **)&dxgiMutex);
+ if (FAILED(hr)) {
+ goto fail;
+ }
+
+ hr = dxgiMutex->lpVtbl->AcquireSync(dxgiMutex, 0, INFINITE);
+
+ dxgiMutex->lpVtbl->Release(dxgiMutex);
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+
+fail:
+ error_setg_win32(errp, GetLastError(), "failed to acquire texture mutex");
+ return false;
+}
+
+static bool d3d_texture2d_release0(ID3D11Texture2D *d3d_texture, Error **errp)
+{
+ IDXGIKeyedMutex *dxgiMutex = NULL;
+ HRESULT hr;
+
+ hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
+ &IID_IDXGIKeyedMutex,
+ (void **)&dxgiMutex);
+ if (FAILED(hr)) {
+ goto fail;
+ }
+
+ hr = dxgiMutex->lpVtbl->ReleaseSync(dxgiMutex, 0);
+
+ dxgiMutex->lpVtbl->Release(dxgiMutex);
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+
+fail:
+ error_setg_win32(errp, GetLastError(), "failed to release texture mutex");
+ return false;
+}
+#endif /* WIN32 */
+
static void dbus_update_gl_cb(GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+ GAsyncResult *res,
+ gpointer user_data)
{
g_autoptr(GError) err = NULL;
DBusDisplayListener *ddl = user_data;
+ bool success;
+
+#ifdef CONFIG_GBM
+ success = qemu_dbus_display1_listener_call_update_dmabuf_finish(
+ ddl->proxy, res, &err);
+#endif
+
+#ifdef WIN32
+ success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish(
+ ddl->d3d11_proxy, res, &err);
+ d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn);
+#endif
- if (!qemu_dbus_display1_listener_call_update_dmabuf_finish(ddl->proxy,
- res, &err)) {
+ if (!success) {
error_report("Failed to call update: %s", err->message);
}
@@ -67,28 +204,54 @@ static void dbus_update_gl_cb(GObject *source_object,
g_object_unref(ddl);
}
-static void dbus_call_update_gl(DBusDisplayListener *ddl,
+static void dbus_call_update_gl(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
- graphic_hw_gl_block(ddl->dcl.con, true);
+ DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+
+ trace_dbus_update_gl(x, y, w, h);
+
glFlush();
+#ifdef CONFIG_GBM
+ graphic_hw_gl_block(ddl->dcl.con, true);
qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy,
x, y, w, h,
G_DBUS_CALL_FLAGS_NONE,
DBUS_DEFAULT_TIMEOUT, NULL,
dbus_update_gl_cb,
g_object_ref(ddl));
-}
-
-static void dbus_scanout_disable(DisplayChangeListener *dcl)
-{
- DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+#endif
- ddl->ds = NULL;
- qemu_dbus_display1_listener_call_disable(
- ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+#ifdef WIN32
+ switch (ddl->ds_share) {
+ case SHARE_KIND_MAPPED:
+ egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h);
+ dbus_gfx_update(dcl, x, y, w, h);
+ break;
+ case SHARE_KIND_D3DTEX:
+ Error *err = NULL;
+ assert(ddl->d3d_texture);
+
+ graphic_hw_gl_block(ddl->dcl.con, true);
+ if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) {
+ error_report_err(err);
+ return;
+ }
+ qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d(
+ ddl->d3d11_proxy,
+ x, y, w, h,
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT, NULL,
+ dbus_update_gl_cb,
+ g_object_ref(ddl));
+ break;
+ default:
+ g_warn_if_reached();
+ }
+#endif
}
+#ifdef CONFIG_GBM
static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
@@ -117,15 +280,136 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
fd_list,
NULL, NULL, NULL);
}
+#endif /* GBM */
+#endif /* OPENGL */
+
+#ifdef WIN32
+static bool dbus_scanout_map(DBusDisplayListener *ddl)
+{
+ g_autoptr(GError) err = NULL;
+ BOOL success;
+ HANDLE target_handle;
+
+ if (ddl->ds_share == SHARE_KIND_MAPPED) {
+ return true;
+ }
+
+ if (!ddl->can_share_map || !ddl->ds->handle) {
+ return false;
+ }
+
+ success = DuplicateHandle(
+ GetCurrentProcess(),
+ ddl->ds->handle,
+ ddl->peer_process,
+ &target_handle,
+ FILE_MAP_READ | SECTION_QUERY,
+ FALSE, 0);
+ if (!success) {
+ g_autofree char *msg = g_win32_error_message(GetLastError());
+ g_debug("Failed to DuplicateHandle: %s", msg);
+ ddl->can_share_map = false;
+ return false;
+ }
+
+ if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync(
+ ddl->map_proxy,
+ GPOINTER_TO_UINT(target_handle),
+ ddl->ds->handle_offset,
+ surface_width(ddl->ds),
+ surface_height(ddl->ds),
+ surface_stride(ddl->ds),
+ surface_format(ddl->ds),
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT,
+ NULL,
+ &err)) {
+ g_debug("Failed to call ScanoutMap: %s", err->message);
+ ddl->can_share_map = false;
+ return false;
+ }
+
+ ddl->ds_share = SHARE_KIND_MAPPED;
+
+ return true;
+}
+
+static bool
+dbus_scanout_share_d3d_texture(
+ DBusDisplayListener *ddl,
+ ID3D11Texture2D *tex,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ Error *err = NULL;
+ BOOL success;
+ HANDLE share_handle, target_handle;
+
+ if (!d3d_texture2d_release0(tex, &err)) {
+ error_report_err(err);
+ return false;
+ }
+
+ if (!d3d_texture2d_share(tex, &share_handle, &err)) {
+ error_report_err(err);
+ return false;
+ }
+
+ success = DuplicateHandle(
+ GetCurrentProcess(),
+ share_handle,
+ ddl->peer_process,
+ &target_handle,
+ 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!success) {
+ g_autofree char *msg = g_win32_error_message(GetLastError());
+ g_debug("Failed to DuplicateHandle: %s", msg);
+ CloseHandle(share_handle);
+ return false;
+ }
+
+ qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d(
+ ddl->d3d11_proxy,
+ GPOINTER_TO_INT(target_handle),
+ backing_width,
+ backing_height,
+ backing_y_0_top,
+ x, y, w, h,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL, NULL, NULL);
+
+ CloseHandle(share_handle);
+
+ if (!d3d_texture2d_acquire0(tex, &err)) {
+ error_report_err(err);
+ return false;
+ }
+
+ ddl->d3d_texture = tex;
+ ddl->ds_share = SHARE_KIND_D3DTEX;
+
+ return true;
+}
+#endif
+#ifdef CONFIG_OPENGL
static void dbus_scanout_texture(DisplayChangeListener *dcl,
uint32_t tex_id,
bool backing_y_0_top,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
+ trace_dbus_scanout_texture(tex_id, backing_y_0_top,
+ backing_width, backing_height, x, y, w, h);
+#ifdef CONFIG_GBM
QemuDmaBuf dmabuf = {
.width = backing_width,
.height = backing_height,
@@ -148,8 +432,26 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl,
dbus_scanout_dmabuf(dcl, &dmabuf);
close(dmabuf.fd);
+#endif
+
+#ifdef WIN32
+ DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+
+ /* there must be a matching gfx_switch before */
+ assert(surface_width(ddl->ds) == w);
+ assert(surface_height(ddl->ds) == h);
+
+ if (d3d_tex2d) {
+ dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top,
+ backing_width, backing_height, x, y, w, h);
+ } else {
+ dbus_scanout_map(ddl);
+ egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false);
+ }
+#endif
}
+#ifdef CONFIG_GBM
static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf, bool have_hot,
uint32_t hot_x, uint32_t hot_y)
@@ -196,7 +498,14 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
NULL);
}
-static void dbus_cursor_position(DisplayChangeListener *dcl,
+static void dbus_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+ dbus_scanout_disable(dcl);
+}
+#endif /* GBM */
+
+static void dbus_gl_cursor_position(DisplayChangeListener *dcl,
uint32_t pos_x, uint32_t pos_y)
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
@@ -206,19 +515,11 @@ static void dbus_cursor_position(DisplayChangeListener *dcl,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
-static void dbus_release_dmabuf(DisplayChangeListener *dcl,
- QemuDmaBuf *dmabuf)
-{
- dbus_scanout_disable(dcl);
-}
-
static void dbus_scanout_update(DisplayChangeListener *dcl,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h)
{
- DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
-
- dbus_call_update_gl(ddl, x, y, w, h);
+ dbus_call_update_gl(dcl, x, y, w, h);
}
static void dbus_gl_refresh(DisplayChangeListener *dcl)
@@ -232,19 +533,19 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl)
}
if (ddl->gl_updates) {
- dbus_call_update_gl(ddl, 0, 0,
+ dbus_call_update_gl(dcl, 0, 0,
surface_width(ddl->ds), surface_height(ddl->ds));
ddl->gl_updates = 0;
}
}
-#endif
+#endif /* OPENGL */
static void dbus_refresh(DisplayChangeListener *dcl)
{
graphic_hw_update(dcl->con);
}
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
@@ -263,10 +564,20 @@ static void dbus_gfx_update(DisplayChangeListener *dcl,
size_t stride;
assert(ddl->ds);
- stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
trace_dbus_update(x, y, w, h);
+#ifdef WIN32
+ if (dbus_scanout_map(ddl)) {
+ qemu_dbus_display1_listener_win32_map_call_update_map(
+ ddl->map_proxy,
+ x, y, w, h,
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
+ return;
+ }
+#endif
+
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"),
@@ -288,6 +599,7 @@ static void dbus_gfx_update(DisplayChangeListener *dcl,
}
/* make a copy, since gvariant only handles linear data */
+ stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
img = pixman_image_create_bits(surface_format(ddl->ds),
w, h, NULL, stride);
pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img,
@@ -307,20 +619,23 @@ static void dbus_gfx_update(DisplayChangeListener *dcl,
DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
}
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
struct DisplaySurface *new_surface)
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
+ trace_dbus_gl_gfx_switch(new_surface);
+
ddl->ds = new_surface;
+ ddl->ds_share = SHARE_KIND_NONE;
if (ddl->ds) {
int width = surface_width(ddl->ds);
int height = surface_height(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);
+ width, height, 0, 0, width, height, NULL);
}
}
#endif
@@ -331,10 +646,7 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl,
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
ddl->ds = new_surface;
- if (!ddl->ds) {
- /* why not call disable instead? */
- return;
- }
+ ddl->ds_share = SHARE_KIND_NONE;
}
static void dbus_mouse_set(DisplayChangeListener *dcl,
@@ -374,7 +686,7 @@ static void dbus_cursor_define(DisplayChangeListener *dcl,
NULL);
}
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
const DisplayChangeListenerOps dbus_gl_dcl_ops = {
.dpy_name = "dbus-gl",
.dpy_gfx_update = dbus_gl_gfx_update,
@@ -386,10 +698,12 @@ const DisplayChangeListenerOps dbus_gl_dcl_ops = {
.dpy_gl_scanout_disable = dbus_scanout_disable,
.dpy_gl_scanout_texture = dbus_scanout_texture,
+#ifdef CONFIG_GBM
.dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf,
.dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf,
- .dpy_gl_cursor_position = dbus_cursor_position,
.dpy_gl_release_dmabuf = dbus_release_dmabuf,
+#endif
+ .dpy_gl_cursor_position = dbus_gl_cursor_position,
.dpy_gl_update = dbus_scanout_update,
};
#endif
@@ -412,6 +726,14 @@ dbus_display_listener_dispose(GObject *object)
g_clear_object(&ddl->conn);
g_clear_pointer(&ddl->bus_name, g_free);
g_clear_object(&ddl->proxy);
+#ifdef WIN32
+ g_clear_object(&ddl->map_proxy);
+ g_clear_object(&ddl->d3d11_proxy);
+ g_clear_pointer(&ddl->peer_process, CloseHandle);
+#ifdef CONFIG_OPENGL
+ egl_fb_destroy(&ddl->fb);
+#endif
+#endif
G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
}
@@ -422,7 +744,7 @@ dbus_display_listener_constructed(GObject *object)
DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
ddl->dcl.ops = &dbus_dcl_ops;
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#ifdef CONFIG_OPENGL
if (display_opengl) {
ddl->dcl.ops = &dbus_gl_dcl_ops;
}
@@ -457,6 +779,130 @@ dbus_display_listener_get_console(DBusDisplayListener *ddl)
return ddl->console;
}
+#ifdef WIN32
+static bool
+dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
+{
+ QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy);
+ bool implements;
+
+ implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface);
+ if (!implements) {
+ g_debug("Display listener does not implement: `%s`", iface);
+ }
+
+ return implements;
+}
+
+static bool
+dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
+{
+ g_autoptr(GError) err = NULL;
+ GDBusConnection *conn;
+ GIOStream *stream;
+ GSocket *sock;
+ g_autoptr(GCredentials) creds = NULL;
+ DWORD *pid;
+
+ if (ddl->peer_process) {
+ return true;
+ }
+
+ conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy));
+ stream = g_dbus_connection_get_stream(conn);
+
+ if (!G_IS_UNIX_CONNECTION(stream)) {
+ return false;
+ }
+
+ sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
+ creds = g_socket_get_credentials(sock, &err);
+
+ if (!creds) {
+ g_debug("Failed to get peer credentials: %s", err->message);
+ return false;
+ }
+
+ pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID);
+
+ if (pid == NULL) {
+ g_debug("Failed to get peer PID");
+ return false;
+ }
+
+ ddl->peer_process = OpenProcess(
+ PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
+ false, *pid);
+
+ if (!ddl->peer_process) {
+ g_autofree char *msg = g_win32_error_message(GetLastError());
+ g_debug("Failed to OpenProcess: %s", msg);
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+static void
+dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl)
+{
+#ifdef WIN32
+ g_autoptr(GError) err = NULL;
+
+ if (!dbus_display_listener_implements(ddl,
+ "org.qemu.Display1.Listener.Win32.D3d11")) {
+ return;
+ }
+
+ if (!dbus_display_listener_setup_peer_process(ddl)) {
+ return;
+ }
+
+ ddl->d3d11_proxy =
+ qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "/org/qemu/Display1/Listener",
+ NULL,
+ &err);
+ if (!ddl->d3d11_proxy) {
+ g_debug("Failed to setup win32 d3d11 proxy: %s", err->message);
+ return;
+ }
+#endif
+}
+
+static void
+dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
+{
+#ifdef WIN32
+ g_autoptr(GError) err = NULL;
+
+ if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) {
+ return;
+ }
+
+ if (!dbus_display_listener_setup_peer_process(ddl)) {
+ return;
+ }
+
+ ddl->map_proxy =
+ qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "/org/qemu/Display1/Listener",
+ NULL,
+ &err);
+ if (!ddl->map_proxy) {
+ g_debug("Failed to setup win32 map proxy: %s", err->message);
+ return;
+ }
+
+ ddl->can_share_map = true;
+#endif
+}
+
DBusDisplayListener *
dbus_display_listener_new(const char *bus_name,
GDBusConnection *conn,
@@ -485,6 +931,9 @@ dbus_display_listener_new(const char *bus_name,
ddl->conn = conn;
ddl->console = console;
+ dbus_display_listener_setup_shared_map(ddl);
+ dbus_display_listener_setup_d3d11(ddl);
+
con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
assert(con);
ddl->dcl.con = con;
diff --git a/ui/dbus.c b/ui/dbus.c
index b9e9698..32f1bbe 100644
--- a/ui/dbus.c
+++ b/ui/dbus.c
@@ -47,10 +47,8 @@ static DBusDisplay *dbus_display;
static QEMUGLContext dbus_create_context(DisplayGLCtx *dgc,
QEMUGLParams *params)
{
-#ifdef CONFIG_GBM
eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
qemu_egl_rn_ctx);
-#endif
return qemu_egl_create_context(dgc, params);
}
@@ -59,9 +57,7 @@ dbus_is_compatible_dcl(DisplayGLCtx *dgc,
DisplayChangeListener *dcl)
{
return
-#ifdef CONFIG_GBM
dcl->ops == &dbus_gl_dcl_ops ||
-#endif
dcl->ops == &dbus_console_dcl_ops;
}
diff --git a/ui/dbus.h b/ui/dbus.h
index 9c149e7..1e8c24a 100644
--- a/ui/dbus.h
+++ b/ui/dbus.h
@@ -62,6 +62,12 @@ struct DBusDisplay {
Notifier notifier;
};
+#ifdef WIN32
+bool
+dbus_win32_import_socket(GDBusMethodInvocation *invocation,
+ GVariant *arg_listener, int *socket);
+#endif
+
#define TYPE_DBUS_DISPLAY "dbus-display"
OBJECT_DECLARE_SIMPLE_TYPE(DBusDisplay, DBUS_DISPLAY)
diff --git a/ui/egl-context.c b/ui/egl-context.c
index eb5f520..9e0df46 100644
--- a/ui/egl-context.c
+++ b/ui/egl-context.c
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "ui/egl-context.h"
QEMUGLContext qemu_egl_create_context(DisplayGLCtx *dgc,
@@ -32,6 +33,11 @@ void qemu_egl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
int qemu_egl_make_context_current(DisplayGLCtx *dgc,
QEMUGLContext ctx)
{
- return eglMakeCurrent(qemu_egl_display,
- EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
+ if (!eglMakeCurrent(qemu_egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, ctx)) {
+ error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string());
+ return -1;
+ }
+
+ return 0;
}
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index ef70e6a..d5637da 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -61,7 +61,8 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
@@ -79,6 +80,8 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
}
}
+#ifdef CONFIG_GBM
+
static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
@@ -89,7 +92,7 @@ static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
- 0, 0, dmabuf->width, dmabuf->height);
+ 0, 0, dmabuf->width, dmabuf->height, NULL);
}
static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
@@ -110,6 +113,14 @@ static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
}
}
+static void egl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+ egl_dmabuf_release_texture(dmabuf);
+}
+
+#endif
+
static void egl_cursor_position(DisplayChangeListener *dcl,
uint32_t pos_x, uint32_t pos_y)
{
@@ -119,12 +130,6 @@ static void egl_cursor_position(DisplayChangeListener *dcl,
edpy->pos_y = pos_y;
}
-static void egl_release_dmabuf(DisplayChangeListener *dcl,
- QemuDmaBuf *dmabuf)
-{
- egl_dmabuf_release_texture(dmabuf);
-}
-
static void egl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h)
@@ -160,10 +165,12 @@ static const DisplayChangeListenerOps egl_ops = {
.dpy_gl_scanout_disable = egl_scanout_disable,
.dpy_gl_scanout_texture = egl_scanout_texture,
+#ifdef CONFIG_GBM
.dpy_gl_scanout_dmabuf = egl_scanout_dmabuf,
.dpy_gl_cursor_dmabuf = egl_cursor_dmabuf,
- .dpy_gl_cursor_position = egl_cursor_position,
.dpy_gl_release_dmabuf = egl_release_dmabuf,
+#endif
+ .dpy_gl_cursor_position = egl_cursor_position,
.dpy_gl_update = egl_scanout_flush,
};
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 4203163..8f9fbf5 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -15,21 +15,23 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
+
#include "qemu/drm.h"
#include "qemu/error-report.h"
#include "ui/console.h"
#include "ui/egl-helpers.h"
#include "sysemu/sysemu.h"
#include "qapi/error.h"
+#include "trace.h"
EGLDisplay *qemu_egl_display;
EGLConfig qemu_egl_config;
DisplayGLMode qemu_egl_mode;
+bool qemu_egl_angle_d3d;
/* ------------------------------------------------------------------ */
-#if defined(CONFIG_X11) || defined(CONFIG_GBM)
-static const char *egl_get_error_string(void)
+const char *qemu_egl_get_error_string(void)
{
EGLint error = eglGetError();
@@ -68,7 +70,6 @@ static const char *egl_get_error_string(void)
return "Unknown EGL error";
}
}
-#endif
static void egl_fb_delete_texture(egl_fb *fb)
{
@@ -171,6 +172,20 @@ void egl_fb_read(DisplaySurface *dst, egl_fb *src)
GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst));
}
+void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h)
+{
+ assert(surface_width(dst) == src->width);
+ assert(surface_height(dst) == src->height);
+ assert(surface_format(dst) == PIXMAN_x8r8g8b8);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
+ glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
+ glPixelStorei(GL_PACK_ROW_LENGTH, surface_stride(dst) / 4);
+ glReadPixels(x, y, w, h,
+ GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst) + x * 4);
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+}
+
void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip)
{
glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
@@ -201,11 +216,12 @@ void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
/* ---------------------------------------------------------------------- */
+EGLContext qemu_egl_rn_ctx;
+
#ifdef CONFIG_GBM
int qemu_egl_rn_fd;
struct gbm_device *qemu_egl_rn_gbm_dev;
-EGLContext qemu_egl_rn_ctx;
int egl_rendernode_init(const char *rendernode, DisplayGLMode mode)
{
@@ -402,7 +418,7 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
/* ---------------------------------------------------------------------- */
-#if defined(CONFIG_X11) || defined(CONFIG_GBM)
+#if defined(CONFIG_X11) || defined(CONFIG_GBM) || defined(WIN32)
/*
* Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed
@@ -439,10 +455,8 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
/* In practise any EGL 1.5 implementation would support the EXT extension */
if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
- PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
- (void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
- if (getPlatformDisplayEXT && platform != 0) {
- dpy = getPlatformDisplayEXT(platform, native, NULL);
+ if (platform != 0) {
+ dpy = eglGetPlatformDisplayEXT(platform, native, NULL);
}
}
@@ -482,20 +496,20 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
qemu_egl_display = qemu_egl_get_display(dpy, platform);
if (qemu_egl_display == EGL_NO_DISPLAY) {
- error_report("egl: eglGetDisplay failed: %s", egl_get_error_string());
+ error_report("egl: eglGetDisplay failed: %s", qemu_egl_get_error_string());
return -1;
}
b = eglInitialize(qemu_egl_display, &major, &minor);
if (b == EGL_FALSE) {
- error_report("egl: eglInitialize failed: %s", egl_get_error_string());
+ error_report("egl: eglInitialize failed: %s", qemu_egl_get_error_string());
return -1;
}
b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
if (b == EGL_FALSE) {
error_report("egl: eglBindAPI failed (%s mode): %s",
- gles ? "gles" : "core", egl_get_error_string());
+ gles ? "gles" : "core", qemu_egl_get_error_string());
return -1;
}
@@ -504,7 +518,7 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
&qemu_egl_config, 1, &n);
if (b == EGL_FALSE || n != 1) {
error_report("egl: eglChooseConfig failed (%s mode): %s",
- gles ? "gles" : "core", egl_get_error_string());
+ gles ? "gles" : "core", qemu_egl_get_error_string());
return -1;
}
@@ -512,6 +526,9 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
return 0;
}
+#endif
+
+#if defined(CONFIG_X11) || defined(CONFIG_GBM)
int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
{
#ifdef EGL_KHR_platform_x11
@@ -529,7 +546,45 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
return qemu_egl_init_dpy(dpy, 0, mode);
#endif
}
+#endif
+
+
+#ifdef WIN32
+int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode)
+{
+ /* prefer GL ES, as that's what ANGLE supports */
+ if (mode == DISPLAYGL_MODE_ON) {
+ mode = DISPLAYGL_MODE_ES;
+ }
+
+ if (qemu_egl_init_dpy(dpy, 0, mode) < 0) {
+ return -1;
+ }
+
+#ifdef EGL_D3D11_DEVICE_ANGLE
+ if (epoxy_has_egl_extension(qemu_egl_display, "EGL_EXT_device_query")) {
+ EGLDeviceEXT device;
+ void *d3d11_device;
+ if (!eglQueryDisplayAttribEXT(qemu_egl_display,
+ EGL_DEVICE_EXT,
+ (EGLAttrib *)&device)) {
+ return 0;
+ }
+
+ if (!eglQueryDeviceAttribEXT(device,
+ EGL_D3D11_DEVICE_ANGLE,
+ (EGLAttrib *)&d3d11_device)) {
+ return 0;
+ }
+
+ trace_egl_init_d3d11_device(device);
+ qemu_egl_angle_d3d = device != NULL;
+ }
+#endif
+
+ return 0;
+}
#endif
bool qemu_egl_has_dmabuf(void)
@@ -581,15 +636,28 @@ bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp)
return false;
}
-#ifdef CONFIG_GBM
+#ifdef WIN32
+ if (qemu_egl_init_dpy_win32(EGL_DEFAULT_DISPLAY, mode) < 0) {
+ error_setg(errp, "egl: init failed");
+ return false;
+ }
+ qemu_egl_rn_ctx = qemu_egl_init_ctx();
+ if (!qemu_egl_rn_ctx) {
+ error_setg(errp, "egl: egl_init_ctx failed");
+ return false;
+ }
+#elif defined(CONFIG_GBM)
if (egl_rendernode_init(rendernode, mode) < 0) {
error_setg(errp, "egl: render node init failed");
return false;
}
+#endif
+
+ if (!qemu_egl_rn_ctx) {
+ error_setg(errp, "egl: not available on this platform");
+ return false;
+ }
+
display_opengl = 1;
return true;
-#else
- error_setg(errp, "egl: not available on this platform");
- return false;
-#endif
}
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 1913004..d59b8cd 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
#include "trace.h"
@@ -223,7 +224,8 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_id, bool backing_y_0_top,
uint32_t backing_width, uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
@@ -257,7 +259,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
gd_egl_scanout_texture(dcl, dmabuf->texture,
dmabuf->y0_top, dmabuf->width, dmabuf->height,
- 0, 0, dmabuf->width, dmabuf->height);
+ dmabuf->x, dmabuf->y, dmabuf->scanout_width,
+ dmabuf->scanout_height, NULL);
if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf;
@@ -368,6 +371,11 @@ int gd_egl_make_current(DisplayGLCtx *dgc,
{
VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
- return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
- vc->gfx.esurface, ctx);
+ if (!eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, ctx)) {
+ error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string());
+ return -1;
+ }
+
+ return 0;
}
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index c384a15..7367dfd 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -244,7 +244,8 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
@@ -299,7 +300,8 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
gd_gl_area_scanout_texture(dcl, dmabuf->texture,
dmabuf->y0_top, dmabuf->width, dmabuf->height,
- 0, 0, dmabuf->width, dmabuf->height);
+ dmabuf->x, dmabuf->y, dmabuf->scanout_width,
+ dmabuf->scanout_height, NULL);
if (dmabuf->allow_fences) {
vc->gfx.guest_fb.dmabuf = dmabuf;
diff --git a/ui/gtk.c b/ui/gtk.c
index e50f950..8ba41c8 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -130,11 +130,6 @@ typedef struct VCChardev VCChardev;
DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
TYPE_CHARDEV_VC)
-struct touch_slot {
- int x;
- int y;
- int tracking_id;
-};
static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX];
bool gtk_use_gl_area;
@@ -588,7 +583,12 @@ static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
#ifdef CONFIG_GBM
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
egl_dmabuf_release_texture(dmabuf);
+ if (vc->gfx.guest_fb.dmabuf == dmabuf) {
+ vc->gfx.guest_fb.dmabuf = NULL;
+ }
#endif
}
@@ -1068,27 +1068,12 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch,
void *opaque)
{
VirtualConsole *vc = opaque;
- struct touch_slot *slot;
uint64_t num_slot = GPOINTER_TO_UINT(touch->sequence);
- bool needs_sync = false;
- int update;
int type = -1;
- int i;
-
- if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
- warn_report("gtk: unexpected touch slot number: % " PRId64" >= %d\n",
- num_slot, INPUT_EVENT_SLOTS_MAX);
- return FALSE;
- }
-
- slot = &touch_slots[num_slot];
- slot->x = touch->x;
- slot->y = touch->y;
switch (touch->type) {
case GDK_TOUCH_BEGIN:
type = INPUT_MULTI_TOUCH_TYPE_BEGIN;
- slot->tracking_id = num_slot;
break;
case GDK_TOUCH_UPDATE:
type = INPUT_MULTI_TOUCH_TYPE_UPDATE;
@@ -1099,44 +1084,13 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch,
break;
default:
warn_report("gtk: unexpected touch event type\n");
+ return FALSE;
}
- for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
- if (i == num_slot) {
- update = type;
- } else {
- update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
- }
-
- slot = &touch_slots[i];
-
- if (slot->tracking_id == -1) {
- continue;
- }
-
- if (update == INPUT_MULTI_TOUCH_TYPE_END) {
- slot->tracking_id = -1;
- qemu_input_queue_mtt(vc->gfx.dcl.con, update, i, slot->tracking_id);
- needs_sync = true;
- } else {
- qemu_input_queue_mtt(vc->gfx.dcl.con, update, i, slot->tracking_id);
- qemu_input_queue_btn(vc->gfx.dcl.con, INPUT_BUTTON_TOUCH, true);
- qemu_input_queue_mtt_abs(vc->gfx.dcl.con,
- INPUT_AXIS_X, (int) slot->x,
- 0, surface_width(vc->gfx.ds),
- i, slot->tracking_id);
- qemu_input_queue_mtt_abs(vc->gfx.dcl.con,
- INPUT_AXIS_Y, (int) slot->y,
- 0, surface_height(vc->gfx.ds),
- i, slot->tracking_id);
- needs_sync = true;
- }
- }
-
- if (needs_sync) {
- qemu_input_event_sync();
- }
-
+ console_handle_touch_event(vc->gfx.dcl.con, touch_slots,
+ num_slot, surface_width(vc->gfx.ds),
+ surface_height(vc->gfx.ds), touch->x,
+ touch->y, type, &error_warn);
return TRUE;
}
diff --git a/ui/meson.build b/ui/meson.build
index a5506ac..d81609f 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -65,18 +65,25 @@ if opengl.found()
ui_modules += {'opengl' : opengl_ss}
endif
-if opengl.found() and gbm.found()
+if opengl.found()
egl_headless_ss = ss.source_set()
- egl_headless_ss.add(when: [opengl, gbm, pixman],
- if_true: files('egl-headless.c'))
+ egl_headless_ss.add(when: [opengl, pixman],
+ if_true: [files('egl-headless.c'), gbm])
ui_modules += {'egl-headless' : egl_headless_ss}
endif
if dbus_display
dbus_ss = ss.source_set()
+ env = environment()
+ env.set('TARGETOS', targetos)
+ xml = custom_target('dbus-display preprocess',
+ input: 'dbus-display1.xml',
+ output: 'dbus-display1.xml',
+ env: env,
+ command: [xml_pp, '@INPUT@', '@OUTPUT@'])
dbus_display1 = custom_target('dbus-display gdbus-codegen',
output: ['dbus-display1.h', 'dbus-display1.c'],
- input: files('dbus-display1.xml'),
+ input: xml,
command: [gdbus_codegen, '@INPUT@',
'--glib-min-required', '2.64',
'--output-directory', meson.current_build_dir(),
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index 3ab7e2e..e4f024a 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -6,6 +6,7 @@
#include "qemu/osdep.h"
#include "ui/console.h"
#include "standard-headers/drm/drm_fourcc.h"
+#include "trace.h"
PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format)
{
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index bbfa70e..28d7966 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -205,7 +205,8 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 9d70320..0d91b55 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -113,11 +113,11 @@ void sdl2_window_create(struct sdl2_console *scon)
SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver);
SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");
- }
- scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
- if (scon->opengl) {
scon->winctx = SDL_GL_CreateContext(scon->real_window);
+ } else {
+ /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */
+ scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
}
sdl_update_caption(scon);
}
@@ -128,10 +128,14 @@ void sdl2_window_destroy(struct sdl2_console *scon)
return;
}
- SDL_GL_DeleteContext(scon->winctx);
- scon->winctx = NULL;
- SDL_DestroyRenderer(scon->real_renderer);
- scon->real_renderer = NULL;
+ if (scon->winctx) {
+ SDL_GL_DeleteContext(scon->winctx);
+ scon->winctx = NULL;
+ }
+ if (scon->real_renderer) {
+ SDL_DestroyRenderer(scon->real_renderer);
+ scon->real_renderer = NULL;
+ }
SDL_DestroyWindow(scon->real_window);
scon->real_window = NULL;
}
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 5bee19a..3f3f801 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -935,7 +935,8 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_width,
uint32_t backing_height,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
EGLint stride = 0, fourcc = 0;
diff --git a/ui/trace-events b/ui/trace-events
index 6747361..76b19a2 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -9,7 +9,7 @@ console_putchar_unhandled(int ch) "unhandled escape character '%c'"
console_txt_new(int w, int h) "%dx%d"
console_select(int nr) "%d"
console_refresh(int interval) "interval %d ms"
-displaysurface_create(void *display_surface, int w, int h) "surface=%p, %dx%d"
+displaysurface_create(int w, int h) "%dx%d"
displaysurface_create_from(void *display_surface, int w, int h, uint32_t format) "surface=%p, %dx%d, format 0x%x"
displaysurface_create_pixman(void *display_surface) "surface=%p"
displaysurface_free(void *display_surface) "surface=%p"
@@ -154,7 +154,14 @@ dbus_mouse_press(unsigned int button) "button %u"
dbus_mouse_release(unsigned int button) "button %u"
dbus_mouse_set_pos(unsigned int x, unsigned int y) "x=%u, y=%u"
dbus_mouse_rel_motion(int dx, int dy) "dx=%d, dy=%d"
+dbus_touch_send_event(unsigned int kind, uint32_t num_slot, uint32_t x, uint32_t y) "kind=%u, num_slot=%u, x=%d, y=%d"
dbus_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
+dbus_update_gl(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d"
dbus_clipboard_grab_failed(void) ""
dbus_clipboard_register(const char *bus_name) "peer %s"
dbus_clipboard_unregister(const char *bus_name) "peer %s"
+dbus_scanout_texture(uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "tex_id:%u y0top:%d back:%ux%u %u+%u-%ux%u"
+dbus_gl_gfx_switch(void *p) "surf: %p"
+
+# egl-helpers.c
+egl_init_d3d11_device(void *p) "d3d device: %p"
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index fafbab8..429542f 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -835,3 +835,36 @@ int qemu_msync(void *addr, size_t length, int fd)
*/
return qemu_fdatasync(fd);
}
+
+void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp)
+{
+ void *bits;
+
+ trace_win32_map_alloc(size);
+
+ *h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
+ size, NULL);
+ if (*h == NULL) {
+ error_setg_win32(errp, GetLastError(), "Failed to CreateFileMapping");
+ return NULL;
+ }
+
+ bits = MapViewOfFile(*h, FILE_MAP_ALL_ACCESS, 0, 0, size);
+ if (bits == NULL) {
+ error_setg_win32(errp, GetLastError(), "Failed to MapViewOfFile");
+ CloseHandle(*h);
+ return NULL;
+ }
+
+ return bits;
+}
+
+void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp)
+{
+ trace_win32_map_free(ptr, h);
+
+ if (UnmapViewOfFile(ptr) == 0) {
+ error_setg_win32(errp, GetLastError(), "Failed to UnmapViewOfFile");
+ }
+ CloseHandle(h);
+}
diff --git a/util/trace-events b/util/trace-events
index 3f7e766..49a4962 100644
--- a/util/trace-events
+++ b/util/trace-events
@@ -52,6 +52,10 @@ qemu_anon_ram_alloc(size_t size, void *ptr) "size %zu ptr %p"
qemu_vfree(void *ptr) "ptr %p"
qemu_anon_ram_free(void *ptr, size_t size) "ptr %p size %zu"
+# oslib-win32.c
+win32_map_alloc(size_t size) "size:%zd"
+win32_map_free(void *ptr, void *h) "ptr:%p handle:%p"
+
# hbitmap.c
hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx"
hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64