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