@samitouri / QOSamiQemu / commits / 770af5de56

ui/console: simplify registering display/console change listener

Introduce qemu_console_register_listener() which combines setting dcl->con, dcl->ops and calling register_displaychangelistener() into a single call. This removes repetitive boilerplate across all display backends and makes it harder to forget setting one of the fields. Also move the early-return check in unregister_displaychangelistener() before the trace call, so that unregistering a never-registered listener (e.g. on error paths) does not dereference a NULL ops pointer. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Marc-André Lureau committed Mar 3, 2026 at 21:06 UTC 770af5de567cfc26d21fa7f6bab78a3f7ce41146
12 files changed +45 -72
hw/display/qxl.c
+1 -3
@@ -2251,9 +2251,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp)
2251 return;
2252 }
2253
2254 - qxl->ssd.dcl.ops = &display_listener_ops;
2255 - qxl->ssd.dcl.con = vga->con;
2256 - register_displaychangelistener(&qxl->ssd.dcl);
2254 + qemu_console_register_listener(vga->con, &qxl->ssd.dcl, &display_listener_ops);
2255 }
2256
2257 static void qxl_realize_secondary(PCIDevice *dev, Error **errp)
include/ui/console.h
+4 -2
@@ -291,10 +291,12 @@ struct DisplayGLCtx {
291
292 DisplayState *init_displaystate(void);
293
294 -void register_displaychangelistener(DisplayChangeListener *dcl);
294 +void qemu_console_register_listener(QemuConsole *con,
295 + DisplayChangeListener *dcl,
296 + const DisplayChangeListenerOps *ops);
297 void update_displaychangelistener(DisplayChangeListener *dcl,
298 uint64_t interval);
297 -void unregister_displaychangelistener(DisplayChangeListener *dcl);
299 +void qemu_console_unregister_listener(DisplayChangeListener *dcl);
300
301 bool dpy_ui_info_supported(const QemuConsole *con);
302 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con);
ui/cocoa.m
+4 -9
@@ -93,9 +93,7 @@ static const DisplayChangeListenerOps dcl_ops = {
93 .dpy_mouse_set = cocoa_mouse_set,
94 .dpy_cursor_define = cocoa_cursor_define,
95 };
96 -static DisplayChangeListener dcl = {
97 - .ops = &dcl_ops,
98 -};
96 +static DisplayChangeListener dcl;
97 static QKbdState *kbd;
98 static int cursor_hide = 1;
99 static int left_command_key_enabled = 1;
@@ -425,8 +423,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
423
424 unregister_displaychangelistener(&dcl);
425 qkbd_state_switch_console(kbd, con);
428 - dcl.con = con;
429 - register_displaychangelistener(&dcl);
426 + qemu_console_register_listener(con, &dcl, &dcl_ops);
427 [self notifyMouseModeChange];
428 [self updateUIInfo];
429 }
@@ -2145,11 +2142,9 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
2142 add_console_menu_entries();
2143 addRemovableDevicesMenuItems();
2144
2148 - dcl.con = qemu_console_lookup_default();
2145 + qemu_console_register_listener(qemu_console_lookup_default(),
2146 + &dcl, &dcl_ops);
2147 kbd = qkbd_state_init(dcl.con);
2150 -
2151 - // register vga output callbacks
2152 - register_displaychangelistener(&dcl);
2148 qemu_add_mouse_mode_change_notifier(&mouse_mode_change_notifier);
2149 [cocoaView notifyMouseModeChange];
2150 [cocoaView updateUIInfo];
ui/console.c
+8 -3
@@ -572,10 +572,15 @@ dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con)
572 }
573 }
574
575 -void register_displaychangelistener(DisplayChangeListener *dcl)
575 +void qemu_console_register_listener(QemuConsole *con,
576 + DisplayChangeListener *dcl,
577 + const DisplayChangeListenerOps *ops)
578 {
579 assert(!dcl->ds);
580
581 + dcl->con = con;
582 + dcl->ops = ops;
583 +
584 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
585 dcl->ds = get_alloc_displaystate();
586 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
@@ -600,10 +605,10 @@ void update_displaychangelistener(DisplayChangeListener *dcl,
605 }
606 }
607
603 -void unregister_displaychangelistener(DisplayChangeListener *dcl)
608 +void qemu_console_unregister_listener(DisplayChangeListener *dcl)
609 {
610 DisplayState *ds = dcl->ds;
606 - trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
611 + trace_displaychangelistener_unregister(dcl, dcl->ops ? dcl->ops->dpy_name : NULL);
612 if (!ds) {
613 return;
614 }
ui/curses.c
+3 -6
@@ -324,9 +324,8 @@ static void curses_refresh(DisplayChangeListener *dcl)
324 if (con) {
325 erase();
326 wnoutrefresh(stdscr);
327 - unregister_displaychangelistener(dcl);
328 - dcl->con = con;
329 - register_displaychangelistener(dcl);
327 + qemu_console_unregister_listener(dcl);
328 + qemu_console_register_listener(con, dcl, dcl->ops);
329
330 invalidate = 1;
331 }
@@ -805,9 +804,7 @@ static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
804 curses_winch_init();
805
806 dcl = g_new0(DisplayChangeListener, 1);
808 - dcl->con = qemu_console_lookup_default();
809 - dcl->ops = &dcl_ops;
810 - register_displaychangelistener(dcl);
807 + qemu_console_register_listener(qemu_console_lookup_default(), dcl, &dcl_ops);
808
809 invalidate = 1;
810 }
ui/dbus-console.c
+2 -4
@@ -143,7 +143,6 @@ dbus_display_console_init(DBusDisplayConsole *object)
143 DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
144
145 ddc->listeners = g_ptr_array_new_with_free_func(g_object_unref);
146 - ddc->dcl.ops = &dbus_console_dcl_ops;
146 }
147
148 static void
@@ -151,7 +150,7 @@ dbus_display_console_dispose(GObject *object)
150 {
151 DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
152
154 - unregister_displaychangelistener(&ddc->dcl);
153 + qemu_console_unregister_listener(&ddc->dcl);
154 g_clear_object(&ddc->iface_touch);
155 g_clear_object(&ddc->iface_mouse);
156 g_clear_object(&ddc->iface_kbd);
@@ -553,7 +552,6 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
552 "g-object-path", path,
553 NULL);
554 ddc->display = display;
556 - ddc->dcl.con = con;
555 /* handle errors, and skip non graphics? */
556 qemu_console_fill_device_address(
557 con, device_addr, sizeof(device_addr), NULL);
@@ -611,7 +609,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
609 slot->tracking_id = -1;
610 }
611
614 - register_displaychangelistener(&ddc->dcl);
612 + qemu_console_register_listener(con, &ddc->dcl, &dbus_console_dcl_ops);
613 ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change;
614 qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
615 dbus_mouse_update_is_absolute(ddc);
ui/dbus-listener.c
+8 -19
@@ -957,7 +957,7 @@ dbus_display_listener_dispose(GObject *object)
957 {
958 DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
959
960 - unregister_displaychangelistener(&ddl->dcl);
960 + qemu_console_unregister_listener(&ddl->dcl);
961 g_clear_object(&ddl->conn);
962 g_clear_pointer(&ddl->bus_name, g_free);
963 g_clear_object(&ddl->proxy);
@@ -978,28 +978,12 @@ dbus_display_listener_dispose(GObject *object)
978 G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
979 }
980
981 -static void
982 -dbus_display_listener_constructed(GObject *object)
983 -{
984 - DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
985 -
986 - ddl->dcl.ops = &dbus_dcl_ops;
987 -#ifdef CONFIG_OPENGL
988 - if (display_opengl) {
989 - ddl->dcl.ops = &dbus_gl_dcl_ops;
990 - }
991 -#endif
992 -
993 - G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
994 -}
995 -
981 static void
982 dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
983 {
984 GObjectClass *object_class = G_OBJECT_CLASS(klass);
985
986 object_class->dispose = dbus_display_listener_dispose;
1002 - object_class->constructed = dbus_display_listener_constructed;
987 }
988
989 static void
@@ -1258,6 +1242,7 @@ dbus_display_listener_new(const char *bus_name,
1242 GDBusConnection *conn,
1243 DBusDisplayConsole *console)
1244 {
1245 + const DisplayChangeListenerOps *ops = &dbus_dcl_ops;
1246 DBusDisplayListener *ddl;
1247 QemuConsole *con;
1248 g_autoptr(GError) err = NULL;
@@ -1290,8 +1275,12 @@ dbus_display_listener_new(const char *bus_name,
1275
1276 con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
1277 assert(con);
1293 - ddl->dcl.con = con;
1294 - register_displaychangelistener(&ddl->dcl);
1278 +#ifdef CONFIG_OPENGL
1279 + if (display_opengl) {
1280 + ops = &dbus_gl_dcl_ops;
1281 + }
1282 +#endif
1283 + qemu_console_register_listener(con, &ddl->dcl, ops);
1284
1285 return ddl;
1286 }
ui/egl-headless.c
+1 -3
@@ -229,13 +229,11 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
229 }
230
231 edpy = g_new0(egl_dpy, 1);
232 - edpy->dcl.con = con;
233 - edpy->dcl.ops = &egl_ops;
232 edpy->gls = qemu_gl_init_shader();
233 ctx = g_new0(DisplayGLCtx, 1);
234 ctx->ops = &eglctx_ops;
235 qemu_console_set_display_gl_ctx(con, ctx);
238 - register_displaychangelistener(&edpy->dcl);
236 + qemu_console_register_listener(con, &edpy->dcl, &egl_ops);
237 }
238 }
239
ui/gtk.c
+4 -6
@@ -2251,6 +2251,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
2251 QemuConsole *con, int idx,
2252 GSList *group, GtkWidget *view_menu)
2253 {
2254 + const DisplayChangeListenerOps *ops = &dcl_ops;
2255 bool zoom_to_fit = false;
2256 int i;
2257
@@ -2275,7 +2276,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
2276 vc->gfx.drawing_area = gtk_gl_area_new();
2277 g_signal_connect(vc->gfx.drawing_area, "realize",
2278 G_CALLBACK(gl_area_realize), vc);
2278 - vc->gfx.dcl.ops = &dcl_gl_area_ops;
2279 + ops = &dcl_gl_area_ops;
2280 vc->gfx.dgc.ops = &gl_area_ctx_ops;
2281 } else {
2282 #ifdef CONFIG_X11
@@ -2290,7 +2291,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
2291 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2292 gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
2293 #pragma GCC diagnostic pop
2293 - vc->gfx.dcl.ops = &dcl_egl_ops;
2294 + ops = &dcl_egl_ops;
2295 vc->gfx.dgc.ops = &egl_ctx_ops;
2296 vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
2297 #else
@@ -2301,7 +2302,6 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
2302 #endif
2303 {
2304 vc->gfx.drawing_area = gtk_drawing_area_new();
2304 - vc->gfx.dcl.ops = &dcl_ops;
2305 }
2306
2307
@@ -2325,12 +2325,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
2325 vc->tab_item, gtk_label_new(vc->label));
2326
2327 vc->gfx.kbd = qkbd_state_init(con);
2328 - vc->gfx.dcl.con = con;
2329 -
2328 if (display_opengl) {
2329 qemu_console_set_display_gl_ctx(con, &vc->gfx.dgc);
2330 }
2333 - register_displaychangelistener(&vc->gfx.dcl);
2331 + qemu_console_register_listener(con, &vc->gfx.dcl, ops);
2332
2333 gd_connect_vc_gfx_signals(vc);
2334 group = gd_vc_menu_init(s, vc, idx, group, view_menu);
ui/sdl2.c
+3 -5
@@ -934,6 +934,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
934 sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs);
935 for (i = 0; i < sdl2_num_outputs; i++) {
936 QemuConsole *con = qemu_console_lookup_by_index(i);
937 + const DisplayChangeListenerOps *ops = &dcl_2d_ops;
938 assert(con != NULL);
939 if (!qemu_console_is_graphic(con) &&
940 qemu_console_get_index(con) != 0) {
@@ -943,13 +944,11 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
944 sdl2_console[i].opts = o;
945 #ifdef CONFIG_OPENGL
946 sdl2_console[i].opengl = display_opengl;
946 - sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
947 sdl2_console[i].dgc.ops = display_opengl ? &gl_ctx_ops : NULL;
948 + ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
949 #else
950 sdl2_console[i].opengl = 0;
950 - sdl2_console[i].dcl.ops = &dcl_2d_ops;
951 #endif
952 - sdl2_console[i].dcl.con = con;
952 sdl2_console[i].kbd = qkbd_state_init(con);
953 #ifdef CONFIG_OPENGL
954 if (display_opengl) {
@@ -957,8 +956,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
956 sdl2_gl_console_init(&sdl2_console[i]);
957 }
958 #endif
960 - register_displaychangelistener(&sdl2_console[i].dcl);
961 -
959 + qemu_console_register_listener(con, &sdl2_console[i].dcl, ops);
960 #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)
961 if (SDL_GetWindowWMInfo(sdl2_console[i].real_window, &info)) {
962 #if defined(SDL_VIDEO_DRIVER_WINDOWS)
ui/spice-display.c
+3 -5
@@ -1387,13 +1387,13 @@ static void qemu_spice_display_init_one(QemuConsole *con)
1387 SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
1388 Error *err = NULL;
1389 char device_address[256] = "";
1390 + const DisplayChangeListenerOps *ops = &display_listener_ops;
1391
1392 qemu_spice_display_init_common(ssd);
1393
1393 - ssd->dcl.ops = &display_listener_ops;
1394 #ifdef HAVE_SPICE_GL
1395 if (spice_opengl) {
1396 - ssd->dcl.ops = &display_listener_gl_ops;
1396 + ops = &display_listener_gl_ops;
1397 ssd->dgc.ops = &gl_ctx_ops;
1398 ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);
1399 ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
@@ -1403,8 +1403,6 @@ static void qemu_spice_display_init_one(QemuConsole *con)
1403 ssd->have_scanout = false;
1404 }
1405 #endif
1406 - ssd->dcl.con = con;
1407 -
1406 ssd->qxl.base.sif = &dpy_interface.base;
1407 qemu_spice_add_display_interface(&ssd->qxl, con);
1408
@@ -1422,7 +1420,7 @@ static void qemu_spice_display_init_one(QemuConsole *con)
1420 if (spice_opengl) {
1421 qemu_console_set_display_gl_ctx(con, &ssd->dgc);
1422 }
1425 - register_displaychangelistener(&ssd->dcl);
1423 + qemu_console_register_listener(con, &ssd->dcl, ops);
1424 }
1425
1426 void qemu_spice_display_init(void)
ui/vnc.c
+4 -7
@@ -1860,10 +1860,9 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
1860 qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) {
1861 QemuConsole *con = qemu_console_lookup_by_index(qcode - Q_KEY_CODE_1);
1862 if (con) {
1863 - unregister_displaychangelistener(&vs->vd->dcl);
1863 + qemu_console_unregister_listener(&vs->vd->dcl);
1864 qkbd_state_switch_console(vs->vd->kbd, con);
1865 - vs->vd->dcl.con = con;
1866 - register_displaychangelistener(&vs->vd->dcl);
1865 + qemu_console_register_listener(con, &vs->vd->dcl, vs->vd->dcl.ops);
1866 }
1867 return;
1868 }
@@ -3434,7 +3433,6 @@ VncDisplay *vnc_display_new(const char *id, Error **errp)
3433 vd = g_new0(VncDisplay, 1);
3434 qemu_mutex_init(&vd->mutex);
3435 vd->id = g_strdup(id);
3437 - vd->dcl.ops = &dcl_ops;
3436
3437 QTAILQ_INIT(&vd->clients);
3438 vd->expires = TIME_MAX;
@@ -3524,7 +3522,7 @@ void vnc_display_free(VncDisplay *vd)
3522 }
3523
3524 vnc_stop_worker_thread(vd);
3527 - unregister_displaychangelistener(&vd->dcl);
3525 + qemu_console_unregister_listener(&vd->dcl);
3526 qkbd_state_free(vd->kbd);
3527 qemu_del_vm_change_state_handler(vd->vmstate_handler_entry);
3528 kbd_layout_free(vd->kbd_layout);
@@ -4267,8 +4265,7 @@ static bool vnc_display_open(VncDisplay *vd, Error **errp)
4265 con = qemu_console_lookup_default();
4266 }
4267
4270 - vd->dcl.con = con;
4271 - register_displaychangelistener(&vd->dcl);
4268 + qemu_console_register_listener(con, &vd->dcl, &dcl_ops);
4269 vd->kbd = qkbd_state_init(vd->dcl.con);
4270 qkbd_state_set_delay(vd->kbd, key_delay_ms);
4271