Commit 378eb3d0db for qemu.org

commit 378eb3d0db28f73d60b656bd017a3aca8e5e1a95
Author: Chengyang Zhu <colazcyg@gmail.com>
Date:   Sun Aug 30 13:46:41 2026 +0800

    ui/dbus: add org.qemu.Display1.UIInfo interface

    Currently, the SetUIInfo method cannot set a refresh rate.
    Simply adding a refresh_rate argument would break the method signature.

    This patch adds the UIInfo interface containing
      * property `Supported` indicating whether console UI info is supported.
      * the method `Apply` taking a dictionary as input.
      * the method `Get` returning the current UI info as a dictionary.

    Message-ID: <20260830054641.45437-2-colazcyg@gmail.com>
    Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
    Signed-off-by: Chengyang Zhu <colazcyg@gmail.com>

diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index e1ac06814b..947c3b3a54 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -55,6 +55,8 @@ struct _DBusDisplayConsole {
     guint last_x;
     guint last_y;
     Notifier mouse_mode_notifier;
+
+    QemuDBusDisplay1UIInfo *iface_ui_info;
 };

 G_DEFINE_TYPE(DBusDisplayConsole,
@@ -155,6 +157,7 @@ dbus_display_console_dispose(GObject *object)
     qemu_input_led_notifier_remove(&ddc->led_notifier);
     qemu_console_unregister_listener(&ddc->dcl);
     qemu_remove_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
+    g_clear_object(&ddc->iface_ui_info);
     g_clear_object(&ddc->iface_touch);
     g_clear_object(&ddc->iface_mouse);
     g_clear_object(&ddc->iface_kbd);
@@ -528,6 +531,98 @@ dbus_mouse_mode_change(Notifier *notify, void *data)
     dbus_mouse_update_is_absolute(ddc);
 }

+static gboolean
+dbus_ui_info_get(DBusDisplayConsole *ddc,
+                 GDBusMethodInvocation *invocation)
+{
+    QemuUIInfo ui_info;
+    GVariantDict dict;
+
+    if (!qemu_console_ui_info_supported(ddc->dcl.con)) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_UNSUPPORTED,
+                                              "UIInfo is not supported");
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    ui_info = *qemu_console_get_ui_info(ddc->dcl.con);
+    g_variant_dict_init(&dict, NULL);
+
+    g_variant_dict_insert(&dict, "width_mm", "q", ui_info.width_mm);
+    g_variant_dict_insert(&dict, "height_mm", "q", ui_info.height_mm);
+    g_variant_dict_insert(&dict, "xoff", "i", ui_info.xoff);
+    g_variant_dict_insert(&dict, "yoff", "i", ui_info.yoff);
+    g_variant_dict_insert(&dict, "width", "u", ui_info.width);
+    g_variant_dict_insert(&dict, "height", "u", ui_info.height);
+    g_variant_dict_insert(&dict, "refresh_rate", "u", ui_info.refresh_rate);
+
+    qemu_dbus_display1_uiinfo_complete_get(ddc->iface_ui_info, invocation,
+                                           g_variant_dict_end(&dict));
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static bool
+dbus_ui_info_apply_lookup(GDBusMethodInvocation *invocation,
+                          GVariantDict *dict,
+                          const gchar *key,
+                          const gchar *fmt_str,
+                          void *res)
+{
+    if (g_variant_dict_contains(dict, key) &&
+        !g_variant_dict_lookup(dict, key, fmt_str, res)) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_INVALID,
+                                              "%s must have D-Bus signature %s",
+                                              key, fmt_str);
+        return false;
+    }
+    return true;
+}
+
+static gboolean
+dbus_ui_info_apply(DBusDisplayConsole *ddc,
+                   GDBusMethodInvocation *invocation,
+                   GVariant *arg_ui_info)
+{
+    QemuUIInfo ui_info;
+    g_auto(GVariantDict) dict = G_VARIANT_DICT_INIT(arg_ui_info);
+
+    if (!qemu_console_ui_info_supported(ddc->dcl.con)) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              DBUS_DISPLAY_ERROR,
+                                              DBUS_DISPLAY_ERROR_UNSUPPORTED,
+                                              "UIInfo is not supported");
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    ui_info = *qemu_console_get_ui_info(ddc->dcl.con);
+
+    if (!dbus_ui_info_apply_lookup(invocation, &dict, "width_mm", "q",
+                                   &ui_info.width_mm) ||
+        !dbus_ui_info_apply_lookup(invocation, &dict, "height_mm", "q",
+                                   &ui_info.height_mm) ||
+        !dbus_ui_info_apply_lookup(invocation, &dict, "xoff", "i",
+                                   &ui_info.xoff) ||
+        !dbus_ui_info_apply_lookup(invocation, &dict, "yoff", "i",
+                                   &ui_info.yoff) ||
+        !dbus_ui_info_apply_lookup(invocation, &dict, "width", "u",
+                                   &ui_info.width) ||
+        !dbus_ui_info_apply_lookup(invocation, &dict, "height", "u",
+                                   &ui_info.height) ||
+        !dbus_ui_info_apply_lookup(invocation, &dict, "refresh_rate", "u",
+                                   &ui_info.refresh_rate)) {
+        return DBUS_METHOD_INVOCATION_HANDLED;
+    }
+
+    qemu_console_set_ui_info(ddc->dcl.con, &ui_info, false);
+    qemu_dbus_display1_uiinfo_complete_apply(ddc->iface_ui_info, invocation);
+
+    return DBUS_METHOD_INVOCATION_HANDLED;
+}
+
 int dbus_display_console_get_index(DBusDisplayConsole *ddc)
 {
     return qemu_console_get_index(ddc->dcl.con);
@@ -550,6 +645,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
         "org.qemu.Display1.Keyboard",
         "org.qemu.Display1.Mouse",
         "org.qemu.Display1.MultiTouch",
+        "org.qemu.Display1.UIInfo",
         NULL
     };

@@ -626,5 +722,15 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
     qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
     dbus_mouse_update_is_absolute(ddc);

+    ddc->iface_ui_info = qemu_dbus_display1_uiinfo_skeleton_new();
+    qemu_dbus_display1_uiinfo_set_supported(ddc->iface_ui_info,
+        qemu_console_ui_info_supported(ddc->dcl.con));
+    g_object_connect(ddc->iface_ui_info,
+        "swapped-signal::handle-get", dbus_ui_info_get, ddc,
+        "swapped-signal::handle-apply", dbus_ui_info_apply, ddc,
+        NULL);
+    g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
+        G_DBUS_INTERFACE_SKELETON(ddc->iface_ui_info));
+
     return ddc;
 }
diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml
index d96bae2ed6..a23ca0b8b1 100644
--- a/ui/dbus-display1.xml
+++ b/ui/dbus-display1.xml
@@ -90,6 +90,10 @@
         @height: console height, in pixels.

         Modify the dimensions and display settings.
+
+        .. seealso::
+
+           :dbus:iface:`org.qemu.Display1.UIInfo` which supports a superset of these properties.
     -->
     <method name="SetUIInfo">
       <arg name="width_mm" type="q" direction="in"/>
@@ -1159,4 +1163,55 @@
     -->
     <property name="Encoding" type="s" access="read"/>
   </interface>
+
+  <!--
+      org.qemu.Display1.UIInfo:
+
+      This interface is implemented on
+      ``/org/qemu/Display1/Console_$id`` (see
+      :dbus:iface:`~org.qemu.Display1.Console`).
+  -->
+  <interface name="org.qemu.Display1.UIInfo">
+    <!--
+        Supported:
+
+        Whether console UI info is supported.
+    -->
+    <property name="Supported" type="b" access="read"/>
+
+    <!--
+        Get:
+        @ui_info: a dictionary of UI info properties.
+
+        Return the currently requested properties for the console.
+        The list of returned properties may vary and be extended in the future.
+
+        - ``width_mm`` (q): the physical display width in millimeters.
+        - ``height_mm`` (q): the physical display height in millimeters.
+        - ``xoff`` (i): the horizontal offset in pixels.
+        - ``yoff`` (i): the vertical offset in pixels.
+        - ``width`` (u): the console width in pixels.
+        - ``height`` (u): the console height in pixels.
+        - ``refresh_rate`` (u): the display refresh rate in millihertz.
+    -->
+    <method name="Get">
+      <arg name="ui_info" type="a{sv}" direction="out"/>
+    </method>
+
+    <!--
+        Apply:
+        @ui_info: a dictionary of new UI info properties.
+
+        Apply the properties to the console display.
+        The dictionary schema of ``Apply`` is a subset of that of ``Get``.
+
+        The properties will be merged with existing values.
+        Extra keys will be ignored.
+        The invocation will fail if any key has the wrong type,
+        leaving the UI info unchanged.
+    -->
+    <method name="Apply">
+      <arg name="ui_info" type="a{sv}" direction="in"/>
+    </method>
+  </interface>
 </node>