diff options
| author | Marc-André Lureau <marcandre.lureau@redhat.com> | 2026-06-23 11:44:44 +0400 |
|---|---|---|
| committer | Marc-André Lureau <marcandre.lureau@redhat.com> | 2026-06-24 15:41:15 +0400 |
| commit | b1e8bf92386dc9868303963aec8b8624ce571f96 (patch) | |
| tree | 2ebb522af3f4262d21d2b75efc8a25b64340c392 /ui | |
| parent | 5ac0793ac9f9f71b7468272736126d5504edf198 (diff) | |
| download | qemu-b1e8bf92386dc9868303963aec8b8624ce571f96.tar.gz qemu-b1e8bf92386dc9868303963aec8b8624ce571f96.zip | |
ui/gtk: centralize console menu and shortcut management
Replace the per-console gd_vc_menu_init() with gd_rebuild_vc_menu()
that tears down and rebuilds all console radio menu items and
Ctrl+Alt+N accelerators at once. This is called from initialization
and whenever consoles are detached or reattached.
Shortcuts now skip detached (windowed) consoles, so they always map
to reachable tabs. Rename gd_vc_gfx_init() to add_gfx_console()
and simplify the init function signatures now that menu creation is
decoupled.
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20260623-b4-ui-v4-29-4656aec3398d@redhat.com>
Diffstat (limited to 'ui')
| -rw-r--r-- | ui/gtk.c | 127 |
1 files changed, 84 insertions, 43 deletions
@@ -142,6 +142,7 @@ static void gd_grab_pointer(VirtualConsole *vc, const char *reason); static void gd_ungrab_pointer(GtkDisplayState *s); static void gd_grab_keyboard(VirtualConsole *vc, const char *reason); static void gd_ungrab_keyboard(GtkDisplayState *s); +static void gd_rebuild_vc_menu(GtkDisplayState *s); /** Utility Functions **/ @@ -1488,7 +1489,6 @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, GtkDisplayState *s = vc->s; int page; - gtk_widget_set_sensitive(vc->menu_item, true); g_object_ref(vc->tab_item); gtk_container_remove(GTK_CONTAINER(vc->window), vc->tab_item); page = gd_vc_notebook_pos(s, vc); @@ -1508,6 +1508,8 @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, } #endif + gd_rebuild_vc_menu(s); + if (vc == gd_vc_find_by_menu(s)) { gtk_widget_grab_focus(vc->focus); } @@ -1539,7 +1541,6 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) FALSE); } if (!vc->window) { - gtk_widget_set_sensitive(vc->menu_item, false); vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #if defined(CONFIG_OPENGL) if (vc->gfx.esurface) { @@ -1566,6 +1567,7 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb); } + gd_rebuild_vc_menu(s); gd_update_geometry_hints(vc); gd_update_caption(s); } @@ -1906,22 +1908,73 @@ static gboolean gd_configure(GtkWidget *widget, /** Virtual Console Callbacks **/ -static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, - int idx, GSList *group, GtkWidget *view_menu) +static void gd_rebuild_vc_menu(GtkDisplayState *s) { - vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label); - gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx, - HOTKEY_MODIFIERS, 0, - g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL)); - gtk_accel_label_set_accel( - GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))), - GDK_KEY_1 + idx, HOTKEY_MODIFIERS); + GSList *group = NULL; + VirtualConsole *vc; + GList *children; + gint insert_pos; + int shortcut_idx = 0; + guint i; + + for (i = 0; i < s->vcs->len; i++) { + vc = g_ptr_array_index(s->vcs, i); + if (vc->menu_item) { + gtk_widget_destroy(vc->menu_item); + vc->menu_item = NULL; + } + } + + for (i = 0; i < 9; i++) { + gtk_accel_group_disconnect_key(s->accel_group, + GDK_KEY_1 + i, HOTKEY_MODIFIERS); + } + + /* find insertion position (just before vc_menu_separator) */ + children = gtk_container_get_children(GTK_CONTAINER(s->view_menu)); + insert_pos = g_list_index(children, s->vc_menu_separator); + g_list_free(children); + + /* create new menu items for each console */ + for (i = 0; i < s->vcs->len; i++) { + vc = g_ptr_array_index(s->vcs, i); - g_signal_connect(vc->menu_item, "activate", - G_CALLBACK(gd_menu_switch_vc), s); - gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); + vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, + vc->label); + group = gtk_radio_menu_item_get_group( + GTK_RADIO_MENU_ITEM(vc->menu_item)); + + if (vc->window) { + gtk_widget_set_sensitive(vc->menu_item, false); + } else if (shortcut_idx < 9) { + guint key = GDK_KEY_1 + shortcut_idx; + gtk_accel_group_connect(s->accel_group, key, + HOTKEY_MODIFIERS, 0, + g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), + vc, NULL)); + gtk_accel_label_set_accel( + GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))), + key, HOTKEY_MODIFIERS); + shortcut_idx++; + } - return gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); + g_signal_connect(vc->menu_item, "activate", + G_CALLBACK(gd_menu_switch_vc), s); + gtk_menu_shell_insert(GTK_MENU_SHELL(s->view_menu), + vc->menu_item, insert_pos + i); + gtk_widget_show(vc->menu_item); + } + + /* sync active menu item with current notebook page */ + vc = gd_vc_find_current(s); + if (vc && vc->menu_item) { + g_signal_handlers_block_by_func(vc->menu_item, + gd_menu_switch_vc, s); + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); + g_signal_handlers_unblock_by_func(vc->menu_item, + gd_menu_switch_vc, s); + } } #if defined(CONFIG_VTE) @@ -2064,9 +2117,8 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, return TRUE; } -static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, - Chardev *chr, int idx, - GSList *group, GtkWidget *view_menu) +static void gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, + Chardev *chr, int idx) { char buffer[32]; GtkWidget *box; @@ -2082,7 +2134,6 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, snprintf(buffer, sizeof(buffer), "vc%d", idx); vc->label = g_strdup(vc->vte.chr->label ? : buffer); - group = gd_vc_menu_init(s, vc, idx, group, view_menu); vc->vte.terminal = vte_terminal_new(); g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); @@ -2128,20 +2179,16 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, gtk_label_new(vc->label)); qemu_chr_be_event(vc->vte.chr, CHR_EVENT_OPENED); - - return group; } -static void gd_vcs_init(GtkDisplayState *s, GSList *group, - GtkWidget *view_menu) +static void gd_vcs_init(GtkDisplayState *s) { int i; for (i = 0; i < nb_vcs; i++) { VirtualConsole *vc = g_new0(VirtualConsole, 1); g_ptr_array_add(s->vcs, vc); - group = gd_vc_vte_init(s, vc, vcs[i], s->vcs->len - 1, - group, view_menu); + gd_vc_vte_init(s, vc, vcs[i], s->vcs->len - 1); } } #endif /* CONFIG_VTE */ @@ -2285,12 +2332,12 @@ static bool gd_scale_valid(double scale) return scale >= VC_SCALE_MIN && scale <= VC_SCALE_MAX; } -static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, - QemuConsole *con, int idx, - GSList *group, GtkWidget *view_menu) +static void add_gfx_console(GtkDisplayState *s, QemuConsole *con) { + VirtualConsole *vc = g_new0(VirtualConsole, 1); const DisplayChangeListenerOps *ops = &dcl_ops; + g_ptr_array_add(s->vcs, vc); vc->label = qemu_console_get_label(con); vc->s = s; vc->gfx.preferred_scale = 1.0; @@ -2367,14 +2414,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, qemu_console_register_listener(con, &vc->gfx.dcl, ops); gd_connect_vc_gfx_signals(vc); - group = gd_vc_menu_init(s, vc, idx, group, view_menu); - - return group; } -static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts) +static void gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts) { - GSList *group = NULL; GtkWidget *view_menu; GtkWidget *separator; QemuConsole *con; @@ -2382,6 +2425,7 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts) int vc, i; view_menu = gtk_menu_new(); + s->view_menu = view_menu; gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group); s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen")); @@ -2445,14 +2489,11 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts) /* gfx */ for (vc = 0;; vc++) { - VirtualConsole *v; con = qemu_console_lookup_by_index(vc); if (!con) { break; } - v = g_new0(VirtualConsole, 1); - g_ptr_array_add(s->vcs, v); - group = gd_vc_gfx_init(s, v, con, vc, group, view_menu); + add_gfx_console(s, con); if (qemu_console_ui_info_supported(con)) { zoom_to_fit = true; } @@ -2478,11 +2519,13 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts) #if defined(CONFIG_VTE) /* vte */ - gd_vcs_init(s, group, view_menu); + gd_vcs_init(s); #endif - separator = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); + s->vc_menu_separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vc_menu_separator); + + gd_rebuild_vc_menu(s); s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); @@ -2501,8 +2544,6 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts) GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))), GDK_KEY_m, HOTKEY_MODIFIERS); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item); - - return view_menu; } static void gd_create_menus(GtkDisplayState *s, DisplayOptions *opts) @@ -2511,7 +2552,7 @@ static void gd_create_menus(GtkDisplayState *s, DisplayOptions *opts) s->accel_group = gtk_accel_group_new(); s->machine_menu = gd_create_menu_machine(s); - s->view_menu = gd_create_menu_view(s, opts); + gd_create_menu_view(s, opts); s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), |
