aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ui/dbus-display1.xml48
-rw-r--r--ui/dbus-listener.c165
2 files changed, 208 insertions, 5 deletions
diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml
index 06e8779c04..7233286b28 100644
--- a/ui/dbus-display1.xml
+++ b/ui/dbus-display1.xml
@@ -370,9 +370,7 @@
</arg>
</method>
- <?if $(env.TARGETOS) == windows?>
- <!-- Add shared memory/texture support -->
- <?else?>
+ <?if $(env.TARGETOS) != windows?>
<!--
ScanoutDMABUF:
@dmabuf: the DMABUF file descriptor.
@@ -472,6 +470,50 @@
</interface>
<!--
+ org.qemu.Display1.Listener.Win32.Map:
+
+ This client-side interface can complement org.qemu.Display1.Listener on
+ ``/org/qemu/Display1/Listener`` for Windows specific methods.
+ -->
+ <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.Clipboard:
This interface must be implemented by both the client and the server on
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 41597a0078..f6b1cd11be 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -48,6 +48,14 @@ struct _DBusDisplayListener {
DisplayChangeListener dcl;
DisplaySurface *ds;
int gl_updates;
+
+ bool ds_mapped;
+ bool can_share_map;
+
+#ifdef WIN32
+ QemuDBusDisplay1ListenerWin32Map *map_proxy;
+ HANDLE peer_process;
+#endif
};
G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
@@ -119,7 +127,61 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
fd_list,
NULL, NULL, NULL);
}
+#endif /* OPENGL & GBM */
+
+#ifdef WIN32
+static bool dbus_scanout_map(DBusDisplayListener *ddl)
+{
+ g_autoptr(GError) err = NULL;
+ BOOL success;
+ HANDLE target_handle;
+
+ if (ddl->ds_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_mapped = true;
+
+ return true;
+}
+#endif
+#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
static void dbus_scanout_texture(DisplayChangeListener *dcl,
uint32_t tex_id,
bool backing_y_0_top,
@@ -239,7 +301,7 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl)
ddl->gl_updates = 0;
}
}
-#endif
+#endif /* OPENGL & GBM */
static void dbus_refresh(DisplayChangeListener *dcl)
{
@@ -265,10 +327,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"),
@@ -290,6 +362,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,
@@ -333,6 +406,9 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl,
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
ddl->ds = new_surface;
+#ifdef WIN32
+ ddl->ds_mapped = false;
+#endif
if (!ddl->ds) {
/* why not call disable instead? */
return;
@@ -414,6 +490,10 @@ 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_pointer(&ddl->peer_process, CloseHandle);
+#endif
G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
}
@@ -459,6 +539,85 @@ 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;
+}
+#endif
+
+static void
+dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
+{
+#ifdef WIN32
+ g_autoptr(GError) err = NULL;
+ GDBusConnection *conn;
+ GIOStream *stream;
+ GSocket *sock;
+ g_autoptr(GCredentials) creds = NULL;
+ DWORD *pid;
+
+ if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) {
+ return;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID);
+
+ if (pid == NULL) {
+ g_debug("Failed to get peer PID");
+ return;
+ }
+
+ 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;
+ }
+
+ ddl->map_proxy =
+ qemu_dbus_display1_listener_win32_map_proxy_new_sync(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,
@@ -487,6 +646,8 @@ dbus_display_listener_new(const char *bus_name,
ddl->conn = conn;
ddl->console = console;
+ dbus_display_listener_setup_shared_map(ddl);
+
con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
assert(con);
ddl->dcl.con = con;