diff --git a/src/fe-gtk/gtkutil.c b/src/fe-gtk/gtkutil.c index 328f5319..76df18cc 100644 --- a/src/fe-gtk/gtkutil.c +++ b/src/fe-gtk/gtkutil.c @@ -1123,6 +1123,8 @@ gtkutil_tray_icon_supported (GtkWindow *window) #ifdef GDK_WINDOWING_X11 GdkScreen *screen = gtk_window_get_screen (window); GdkDisplay *display = gdk_screen_get_display (screen); + if (!GDK_IS_X11_DISPLAY (display)) + return FALSE; int screen_number = gdk_screen_get_number (screen); Display *xdisplay = gdk_x11_display_get_xdisplay (display); char *selection_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", screen_number); diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c index 23e57b8a..39555ba4 100644 --- a/src/fe-gtk/maingui.c +++ b/src/fe-gtk/maingui.c @@ -2499,7 +2499,7 @@ mg_create_topicbar (session *sess, GtkWidget *box) gui->topic_entry = topic = sexy_spell_entry_new (); gtk_widget_set_name (topic, "zoitechat-inputbox"); sexy_spell_entry_set_checked (SEXY_SPELL_ENTRY (topic), FALSE); - gtk_container_add (GTK_CONTAINER (hbox), topic); + gtk_box_pack_start (GTK_BOX (hbox), topic, TRUE, TRUE, 0); mg_apply_emoji_fallback_widget (topic); g_signal_connect (G_OBJECT (topic), "activate", G_CALLBACK (mg_topic_cb), 0); @@ -2661,13 +2661,13 @@ mg_create_textarea (session *sess, GtkWidget *box) }; vbox = mg_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0); - gtk_container_add (GTK_CONTAINER (box), vbox); + gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 0); inbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 2); - gtk_container_add (GTK_CONTAINER (vbox), inbox); + gtk_box_pack_start (GTK_BOX (vbox), inbox, TRUE, TRUE, 0); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); - gtk_container_add (GTK_CONTAINER (inbox), frame); + gtk_box_pack_start (GTK_BOX (inbox), frame, TRUE, TRUE, 0); palette_get_xtext_colors (xtext_palette, XTEXT_COLS); gui->xtext = gtk_xtext_new (xtext_palette, TRUE); @@ -2715,13 +2715,13 @@ mg_create_infoframe (GtkWidget *box) frame = gtk_frame_new (0); gtk_frame_set_shadow_type ((GtkFrame*)frame, GTK_SHADOW_OUT); - gtk_container_add (GTK_CONTAINER (box), frame); + gtk_box_pack_start (GTK_BOX (box), frame, FALSE, TRUE, 0); hbox = mg_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), hbox); label = gtk_label_new (NULL); - gtk_container_add (GTK_CONTAINER (hbox), label); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); return label; } @@ -2798,7 +2798,7 @@ mg_create_userlist (session_gui *gui, GtkWidget *box) GtkWidget *frame, *ulist, *vbox; vbox = mg_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 1); - gtk_container_add (GTK_CONTAINER (box), vbox); + gtk_box_pack_start (GTK_BOX (box), vbox, TRUE, TRUE, 0); frame = gtk_frame_new (NULL); if (prefs.hex_gui_ulist_count) @@ -2913,7 +2913,7 @@ mg_create_center (session *sess, session_gui *gui, GtkWidget *box) } gtk_paned_pack2 (GTK_PANED (gui->hpane_right), gui->vpane_right, FALSE, TRUE); - gtk_container_add (GTK_CONTAINER (box), gui->hpane_left); + gtk_box_pack_start (GTK_BOX (box), gui->hpane_left, TRUE, TRUE, 0); gui->note_book = book = gtk_notebook_new (); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (book), FALSE); diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c index 1ef0f7ba..55c398a6 100644 --- a/src/fe-gtk/menu.c +++ b/src/fe-gtk/menu.c @@ -2202,7 +2202,27 @@ menu_find_item (GtkWidget *menu, char *name) { labeltext = g_object_get_data (G_OBJECT (item), "name"); if (!labeltext) - labeltext = gtk_label_get_text (GTK_LABEL (child)); + { + if (GTK_IS_LABEL (child)) + labeltext = gtk_label_get_text (GTK_LABEL (child)); +#ifdef HAVE_GTK3 + else if (GTK_IS_CONTAINER (child)) + { + GList *kids, *l; + kids = gtk_container_get_children (GTK_CONTAINER (child)); + for (l = kids; l; l = l->next) + { + if (GTK_IS_LABEL (l->data)) + { + labeltext = gtk_label_get_text (GTK_LABEL (l->data)); + break; + } + } + g_list_free (kids); + } +#endif + } + if (!menu_streq (labeltext, name, 1)) { found = item; diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c index 6b8bb752..ed570733 100644 --- a/src/fe-gtk/sexy-spell-entry.c +++ b/src/fe-gtk/sexy-spell-entry.c @@ -611,7 +611,36 @@ replace_word(GtkWidget *menuitem, SexySpellEntry *entry) get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character); oldword = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end); - newword = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child (GTK_BIN(menuitem)))); + newword = gtk_menu_item_get_label (GTK_MENU_ITEM (menuitem)); + if (!newword) + { + /* GTK3 menu items may have a box child (icon + label). */ + GtkWidget *child = gtk_bin_get_child (GTK_BIN (menuitem)); + if (GTK_IS_LABEL (child)) + { + newword = gtk_label_get_text (GTK_LABEL (child)); + } + else if (GTK_IS_CONTAINER (child)) + { + GList *kids, *l; + kids = gtk_container_get_children (GTK_CONTAINER (child)); + for (l = kids; l; l = l->next) + { + if (GTK_IS_LABEL (l->data)) + { + newword = gtk_label_get_text (GTK_LABEL (l->data)); + break; + } + } + g_list_free (kids); + } + } + if (!newword) + { + g_free (oldword); + return; + } + cursor = gtk_editable_get_position(GTK_EDITABLE(entry)); /* is the cursor at the end? If so, restore it there */ diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c index 427fa4dd..972ac35f 100644 --- a/src/fe-gtk/xtext.c +++ b/src/fe-gtk/xtext.c @@ -108,6 +108,52 @@ enum TARGET_COMPOUND_TEXT }; + +/* Selection targets for PRIMARY selection / copy-paste. + * + * On Wayland, GtkWidget has no GdkWindow until it is realized. Registering + * selection targets during instance init is too early and can crash under the + * Wayland backend (window/display is NULL). + */ +static const GtkTargetEntry gtk_xtext_selection_targets[] = { + { "UTF8_STRING", 0, TARGET_UTF8_STRING }, + { "STRING", 0, TARGET_STRING }, + { "TEXT", 0, TARGET_TEXT }, + { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT } +}; + +static void +gtk_xtext_install_selection_targets_on_realize (GtkWidget *widget, gpointer user_data) +{ + (void)user_data; + + if (gtk_widget_get_window (widget) == NULL) + return; + + gtk_selection_add_targets (widget, + GDK_SELECTION_PRIMARY, + (GtkTargetEntry *)gtk_xtext_selection_targets, + (gint)G_N_ELEMENTS (gtk_xtext_selection_targets)); +} + +static void +gtk_xtext_install_selection_targets (GtkWidget *widget) +{ + if (gtk_widget_get_realized (widget) && gtk_widget_get_window (widget) != NULL) + { + gtk_selection_add_targets (widget, + GDK_SELECTION_PRIMARY, + (GtkTargetEntry *)gtk_xtext_selection_targets, + (gint)G_N_ELEMENTS (gtk_xtext_selection_targets)); + return; + } + + g_signal_connect (widget, + "realize", + G_CALLBACK (gtk_xtext_install_selection_targets_on_realize), + NULL); +} + static guint xtext_signals[LAST_SIGNAL]; G_DEFINE_TYPE (GtkXText, gtk_xtext, GTK_TYPE_WIDGET) @@ -669,18 +715,7 @@ gtk_xtext_init (GtkXText * xtext) xtext->dont_render2 = FALSE; gtk_xtext_scroll_adjustments (xtext, NULL, NULL); - { - static const GtkTargetEntry targets[] = { - { "UTF8_STRING", 0, TARGET_UTF8_STRING }, - { "STRING", 0, TARGET_STRING }, - { "TEXT", 0, TARGET_TEXT }, - { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT } - }; - static const gint n_targets = sizeof (targets) / sizeof (targets[0]); - - gtk_selection_add_targets (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY, - targets, n_targets); - } + gtk_xtext_install_selection_targets (GTK_WIDGET (xtext)); } static void @@ -1527,7 +1562,22 @@ done: static void gtk_xtext_paint (GtkWidget *widget, GdkRectangle *area) { + /* + * On GTK3/Wayland, drawing directly to the window (via a NULL cairo_t here) + * can be buffered without ever being presented. Queue a redraw instead and + * let the widget's ::draw handler do the actual painting. + */ +#if HAVE_GTK3 + if (G_LIKELY (gtk_widget_get_realized (widget))) + { + if (area) + gtk_widget_queue_draw_area (widget, area->x, area->y, area->width, area->height); + else + gtk_widget_queue_draw (widget); + } +#else gtk_xtext_render (widget, area, NULL); +#endif } #if HAVE_GTK3 @@ -4371,6 +4421,24 @@ gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb) static void gtk_xtext_render_page (GtkXText * xtext) { + /* + * GTK3/Wayland is frame-driven. Drawing directly to a GdkWindow outside the + * widget's ::draw handler can result in the compositor never presenting the + * new buffer. Symptom: chat only updates after you move/resize the window. + * + * If we're not currently inside ::draw, xtext->draw_cr is NULL. In that case + * just request a redraw and let the normal GTK paint cycle do the work. + */ +#ifdef HAVE_GTK3 + if (xtext->draw_cr == NULL) + { + GtkWidget *w = GTK_WIDGET (xtext); + if (gtk_widget_get_realized (w)) + gtk_widget_queue_draw (w); + return; + } +#endif + textentry *ent; int line; int lines_max;