qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: [PATCH v1 3/3] ui/gtk: Add a new parameter to assign connectors/moni


From: Kasireddy, Vivek
Subject: RE: [PATCH v1 3/3] ui/gtk: Add a new parameter to assign connectors/monitors to GFX VCs
Date: Wed, 21 Sep 2022 22:21:33 +0000

Hi Markus,

Thank you for the review.

> Vivek Kasireddy <vivek.kasireddy@intel.com> writes:
> 
> > The new parameter named "connector" can be used to assign physical
> > monitors/connectors to individual GFX VCs such that when the monitor
> > is connected or hotplugged, the associated GTK window would be
> > fullscreened on it. If the monitor is disconnected or unplugged,
> > the associated GTK window would be destroyed and a relevant
> > disconnect event would be sent to the Guest.
> >
> > Usage: -device virtio-gpu-pci,max_outputs=2,blob=true,xres=1920,yres=1080...
> >        -display gtk,gl=on,connector.0=eDP-1,connector.1=DP-1.....
> >
> > Cc: Dongwon Kim <dongwon.kim@intel.com>
> > Cc: Gerd Hoffmann <kraxel@redhat.com>
> > Cc: Daniel P. Berrangé <berrange@redhat.com>
> > Cc: Markus Armbruster <armbru@redhat.com>
> > Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> > Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
> > Cc: Thomas Huth <thuth@redhat.com>
> > Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
> > ---
> >  qapi/ui.json    |   9 ++-
> >  qemu-options.hx |   1 +
> >  ui/gtk.c        | 168 ++++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 177 insertions(+), 1 deletion(-)
> >
> > diff --git a/qapi/ui.json b/qapi/ui.json
> > index 286c5731d1..86787a4c95 100644
> > --- a/qapi/ui.json
> > +++ b/qapi/ui.json
> > @@ -1199,13 +1199,20 @@
> >  #               interfaces (e.g. VGA and virtual console character devices)
> >  #               by default.
> >  #               Since 7.1
> > +# @connector:   List of physical monitor/connector names where the GTK 
> > windows
> > +#               containing the respective graphics virtual consoles (VCs) 
> > are
> > +#               to be placed. If a mapping exists for a VC, it will be
> > +#               fullscreened on that specific monitor or else it would not 
> > be
> > +#               displayed anywhere and would appear disconnected to the 
> > guest.
> 
> Let's see whether I understand this...  We have VCs numbered 0, 1, ...
> VC #i is mapped to the i-th element of @connector, counting from zero.
> Correct?
[Kasireddy, Vivek] Yes, correct.

> 
> Ignorant question: what's a "physical monitor/connector name"?
[Kasireddy, Vivek] IIUC, while the HDMI/DP protocols refer to a receiver (RX)
as a "sink", the DRM Graphics subsystem in the kernel uses the term "connector"
and the GTK library prefers the term "monitor". All of these terms can be
used interchangeably but I chose the term connector for the new parameter
after debating between connector, monitor, output, etc. 
The connector name (e.g. DP-1, HDMI-A-2, etc) uniquely identifies a connector
or a monitor on any given system:
https://elixir.bootlin.com/linux/v6.0-rc6/source/include/drm/drm_connector.h#L1328
If you are on Intel hardware, you can find more info related to connectors by 
doing:
cat /sys/kernel/debug/dri/0/i915_display_info

> 
> Would you mind breaking the lines a bit earlier, between column 70 and
> 75?
[Kasireddy, Vivek] Np, will do that.

> 
> > +#               Since 7.2
> >  #
> >  # Since: 2.12
> >  ##
> >  { 'struct'  : 'DisplayGTK',
> >    'data'    : { '*grab-on-hover' : 'bool',
> >                  '*zoom-to-fit'   : 'bool',
> > -                '*show-tabs'     : 'bool'  } }
> > +                '*show-tabs'     : 'bool',
> > +                '*connector'     : ['str']  } }
> >
> >  ##
> >  # @DisplayEGLHeadless:
> > diff --git a/qemu-options.hx b/qemu-options.hx
> > index 31c04f7eea..576b65ef9f 100644
> > --- a/qemu-options.hx
> > +++ b/qemu-options.hx
> > @@ -1945,6 +1945,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
> >  #if defined(CONFIG_GTK)
> >      "-display 
> > gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
> >      "            
> > [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n"
> > +    "            [,connector.<id of VC>=<connector name>]\n"
> 
> Is "<id of VC>" a VC number?
[Kasireddy, Vivek] Yes.

> 
> >  #endif
> >  #if defined(CONFIG_VNC)
> >      "-display vnc=<display>[,<optargs>]\n"
> 
> Remainder of my review is on style only.  Style suggestions are not
> demands :)
[Kasireddy, Vivek] No problem; most of your suggestions are sensible
and I'll include them all in v2. 

> 
> > diff --git a/ui/gtk.c b/ui/gtk.c
> > index 945c550909..651aaaf174 100644
> > --- a/ui/gtk.c
> > +++ b/ui/gtk.c
> > @@ -37,6 +37,7 @@
> >  #include "qapi/qapi-commands-misc.h"
> >  #include "qemu/cutils.h"
> >  #include "qemu/main-loop.h"
> > +#include "qemu/option.h"
> >
> >  #include "ui/console.h"
> >  #include "ui/gtk.h"
> > @@ -115,6 +116,7 @@
> >  #endif
> >
> >  #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
> > +#define MAX_NUM_ATTEMPTS 5
> 
> Could use a comment, and maybe a more telling name (this one doesn't
> tell me what is being attempted).
[Kasireddy, Vivek] How about MAX_NUM_RETRIES?
gdk_monitor_get_model() can return NULL but if I wait for sometime 
(few milliseconds) and try again it may give a valid value. So, I need a 
macro to limit the number of attempts or retries. 

> 
> >
> >  static const guint16 *keycode_map;
> >  static size_t keycode_maplen;
> > @@ -126,6 +128,15 @@ struct VCChardev {
> >  };
> >  typedef struct VCChardev VCChardev;
> >
> > +struct gd_monitor_data {
> > +    GtkDisplayState *s;
> > +    GdkDisplay *dpy;
> > +    GdkMonitor *monitor;
> > +    QEMUTimer *hp_timer;
> > +    int attempt;
> > +};
> > +typedef struct gd_monitor_data gd_monitor_data;
> 
> We usually contract these like
> 
>    typedef struct gd_monitor_data {
>        ...
>    } gd_monitor_data;
[Kasireddy, Vivek] I was following the style in this file but sure I can
do that.

> 
> > +
> >  #define TYPE_CHARDEV_VC "chardev-vc"
> >  DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
> >                           TYPE_CHARDEV_VC)
> > @@ -1385,6 +1396,147 @@ static void gd_menu_untabify(GtkMenuItem *item, void
> *opaque)
> >      }
> >  }
> >
> > +static void gd_monitor_fullscreen(GdkDisplay *dpy, VirtualConsole *vc,
> > +                                  gint monitor_num)
> > +{
> > +    GtkDisplayState *s = vc->s;
> > +
> > +    if (!vc->window) {
> > +        gd_tab_window_create(vc);
> > +    }
> > +    gtk_window_fullscreen_on_monitor(GTK_WINDOW(vc->window),
> > +                                     gdk_display_get_default_screen(dpy),
> > +                                     monitor_num);
> > +    s->full_screen = TRUE;
> > +    gd_update_cursor(vc);
> > +}
> > +
> > +static int gd_monitor_lookup(GdkDisplay *dpy, char *label)
> > +{
> > +    GdkMonitor *monitor;
> > +    const char *monitor_name;
> > +    int i, total_monitors;
> > +
> > +    total_monitors = gdk_display_get_n_monitors(dpy);
> > +    for (i = 0; i < total_monitors; i++) {
> 
> Suggest to format like this:
> 
>        int total_monitors = gdk_display_get_n_monitors(dpy);
>        GdkMonitor *monitor;
>        const char *monitor_name;
>        int i;
> 
>        for (i = 0; i < total_monitors; i++) {
[Kasireddy, Vivek] Makes sense; will do that.

> 
> > +        monitor = gdk_display_get_monitor(dpy, i);
> > +        if (monitor) {
> > +            monitor_name = gdk_monitor_get_model(monitor);
> > +            if (monitor_name && !strcmp(monitor_name, label)) {
> 
> Would
> 
>            if (monitor && !g_strcmp0(gdk_monitor_get_model(monitor), label)) {
> 
> do?
[Kasireddy, Vivek] Yes, that should work; didn't realize there was a Glib 
version
of a string compare function that can take NULL as well.

> 
> > +                return i;
> > +            }
> > +        }
> > +    }
> > +    return -1;
> > +}
> > +
> > +static void gd_monitor_check_vcs(GdkDisplay *dpy, GdkMonitor *monitor,
> > +                                 GtkDisplayState *s)
> > +{
> > +    VirtualConsole *vc;
> > +    const char *monitor_name = gdk_monitor_get_model(monitor);
> > +    int i;
> > +
> > +    for (i = 0; i < s->nb_vcs; i++) {
> > +        vc = &s->vc[i];
> > +        if (!strcmp(vc->label, monitor_name)) {
> > +            gd_monitor_fullscreen(dpy, vc, gd_monitor_lookup(dpy, 
> > vc->label));
> > +            gd_set_ui_size(vc, surface_width(vc->gfx.ds),
> > +                           surface_height(vc->gfx.ds));
> > +            break;
> > +        }
> > +    }
> > +}
> > +
> > +static void gd_monitor_hotplug_timer(void *opaque)
> > +{
> > +    gd_monitor_data *data = opaque;
> > +    const char *monitor_name = gdk_monitor_get_model(data->monitor);
> > +
> > +    if (monitor_name) {
> > +        gd_monitor_check_vcs(data->dpy, data->monitor, data->s);
> > +    }
> > +    if (monitor_name || data->attempt == MAX_NUM_ATTEMPTS) {
> > +        timer_del(data->hp_timer);
> > +        g_free(data);
> > +    } else {
> > +        data->attempt++;
> > +        timer_mod(data->hp_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
> + 50);
> 
> Suggest to break the line like
> 
>            timer_mod(data->hp_timer,
>                      qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 50);
> 
> for readability.
[Kasireddy, Vivek] Ok.

> 
> > +    }
> > +}
> > +
> > +static void gd_monitor_add(GdkDisplay *dpy, GdkMonitor *monitor,
> > +                           void *opaque)
> > +{
> > +    GtkDisplayState *s = opaque;
> > +    gd_monitor_data *data;
> > +    const char *monitor_name = gdk_monitor_get_model(monitor);
> > +
> > +    if (!monitor_name) {
> > +        data = g_malloc0(sizeof(*data));
> > +        data->s = s;
> > +        data->dpy = dpy;
> > +        data->monitor = monitor;
> > +        data->hp_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
> > +                                      gd_monitor_hotplug_timer, data);
> > +        timer_mod(data->hp_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
> + 50);
> > +    } else {
> > +        gd_monitor_check_vcs(dpy, monitor, s);
> > +    }
> 
> Often
> 
>        if (cond) {
>            do stuff when cond
>        } else {
>            do stuff when !cond
>        }
> 
> is easier to read than
> 
>        if (!cond) {
>            do stuff when !cond
>        } else {
>            do stuff when !!cond
>        }
> 
> Give it a thought.
[Kasireddy, Vivek] Ok, makes sense; will do what you are suggesting.

> 
> > +}
> > +
> > +static void gd_monitor_remove(GdkDisplay *dpy, GdkMonitor *monitor,
> > +                              void *opaque)
> > +{
> > +    GtkDisplayState *s = opaque;
> > +    VirtualConsole *vc;
> > +    const char *monitor_name = gdk_monitor_get_model(monitor);
> > +    int i;
> > +
> > +    if (!monitor_name) {
> > +        return;
> > +    }
> > +    for (i = 0; i < s->nb_vcs; i++) {
> > +        vc = &s->vc[i];
> > +        if (!strcmp(vc->label, monitor_name)) {
> > +            gd_tab_window_close(NULL, NULL, vc);
> > +            gd_set_ui_size(vc, 0, 0);
> > +            break;
> > +        }
> > +    }
> > +}
> > +
> > +static void gd_connectors_init(GdkDisplay *dpy, GtkDisplayState *s)
> > +{
> > +    VirtualConsole *vc;
> > +    strList *connector = s->opts->u.gtk.connector;
> > +    gint page_num = 0, monitor_num;
> > +
> > +    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
> > +    gtk_widget_hide(s->menu_bar);
> > +    for (; connector; connector = connector->next) {
> 
> Please don't split off part of the loop control.  What about
> 
>        for (conn = s->opts->u.gtk.connector; conn; conn = conn->next) {
> 
> ?
[Kasireddy, Vivek] Sure, I am ok with shortening the variable name.

Thanks,
Vivek

> 
> > +        vc = gd_vc_find_by_page(s, page_num);
> > +        if (!vc) {
> > +            break;
> > +        }
> > +        if (page_num == 0) {
> > +            vc->window = s->window;
> > +        }
> > +
> > +        g_free(vc->label);
> > +        vc->label = g_strdup(connector->value);
> > +        monitor_num = gd_monitor_lookup(dpy, vc->label);
> > +        if (monitor_num >= 0) {
> > +            gd_monitor_fullscreen(dpy, vc, monitor_num);
> > +            gd_set_ui_size(vc, surface_width(vc->gfx.ds),
> > +                           surface_height(vc->gfx.ds));
> > +        } else {
> > +            gd_set_ui_size(vc, 0, 0);
> > +        }
> > +        page_num++;
> > +    }
> > +}
> > +
> >  static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque)
> >  {
> >      GtkDisplayState *s = opaque;
> > @@ -1705,7 +1857,14 @@ static gboolean gd_configure(GtkWidget *widget,
> >                               GdkEventConfigure *cfg, gpointer opaque)
> >  {
> >      VirtualConsole *vc = opaque;
> > +    GtkDisplayState *s = vc->s;
> > +    GtkWidget *parent = gtk_widget_get_parent(widget);
> >
> > +    if (s->opts->u.gtk.has_connector) {
> > +        if (!parent || !GTK_IS_WINDOW(parent)) {
> > +            return FALSE;
> > +        }
> > +    }
> >      gd_set_ui_size(vc, cfg->width, cfg->height);
> >      return FALSE;
> >  }
> > @@ -2038,6 +2197,12 @@ static void gd_connect_signals(GtkDisplayState *s)
> >                       G_CALLBACK(gd_menu_grab_input), s);
> >      g_signal_connect(s->notebook, "switch-page",
> >                       G_CALLBACK(gd_change_page), s);
> > +    if (s->opts->u.gtk.has_connector) {
> > +        g_signal_connect(gtk_widget_get_display(s->window), 
> > "monitor-added",
> > +                         G_CALLBACK(gd_monitor_add), s);
> > +        g_signal_connect(gtk_widget_get_display(s->window), 
> > "monitor-removed",
> > +                         G_CALLBACK(gd_monitor_remove), s);
> > +    }
> >  }
> >
> >  static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
> > @@ -2402,6 +2567,9 @@ static void gtk_display_init(DisplayState *ds, 
> > DisplayOptions
> *opts)
> >          opts->u.gtk.show_tabs) {
> >          gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item));
> >      }
> > +    if (s->opts->u.gtk.has_connector) {
> > +        gd_connectors_init(window_display, s);
> > +    }
> >      gd_clipboard_init(s);
> >  }


reply via email to

[Prev in Thread] Current Thread [Next in Thread]