From 755d77f153d1a97a798aacbcb8a8c58112957872 Mon Sep 17 00:00:00 2001 From: mg <102437792+mg-dev25@users.noreply.github.com> Date: Sun, 30 Nov 2025 22:49:00 +0100 Subject: [PATCH] feat: Wayland detection, text sizing fix, signal handling Core improvements to YAD: Wayland Detection: - Add yad_check_x11() and yad_check_wayland() functions - Detect display backend at startup using GDK macros - Warn when using X11-only features on Wayland: * --posx/--posy positioning (compositor-controlled on Wayland) * --mouse positioning (not supported on Wayland) * --plug mode (requires X11) * notebook/paned/notification modes (X11-only) * color picker (screen capture requires X11 or portal) - Provide guidance for GDK_BACKEND=x11 workaround Text Sizing Fix: - Add text_size_allocate_cb() callback for proper line wrapping - Set width request on size-allocate to enable text wrap - Constrain label max_width_chars when --width specified - Connect size-allocate signal when --geometry or --width used - Prevents windows from becoming excessively large with long text Signal Handling: - Add sa_term() handler for SIGTERM and SIGINT - Enable graceful shutdown instead of immediate termination - Properly exit with CANCEL response (1) on signals Footer Widget Support: - Retrieve footer widget from dialog data (set by form.c) - Pack into button box as secondary, non-homogeneous child - Enables @footer@ form fields feature Fixes #107, #140, #32, #297, #300 Addresses #159, #261, #158, #216, #49, #181 --- src/cpicker.c | 19 ++++++++ src/main.c | 130 ++++++++++++++++++++++++++++++++++++++++++-------- src/print.c | 6 ++- src/yad.h | 3 ++ 4 files changed, 138 insertions(+), 20 deletions(-) diff --git a/src/cpicker.c b/src/cpicker.c index 261424b..e587c5f 100644 --- a/src/cpicker.c +++ b/src/cpicker.c @@ -23,6 +23,16 @@ #include "cpicker.h" +#include /* For _() macro */ + +/* Forward declaration - defined in main.c when building yad */ +gboolean yad_check_wayland (void); + +/* Weak symbol: if yad_check_wayland doesn't exist (tools build), return FALSE */ +#ifdef __GNUC__ +gboolean __attribute__((weak)) yad_check_wayland (void) { return FALSE; } +#endif + #define BIG_STEP 20 typedef struct { @@ -215,6 +225,15 @@ yad_get_screen_color (GtkWidget *widget) GdkGrabStatus grab_status; GdkWindow *window; + /* Color picking doesn't work on Wayland - screen capture and device grab are restricted */ + if (yad_check_wayland ()) + { + g_printerr (_("WARNING: Color picker is not supported on Wayland.\n" + "Screen capture and pointer grab require X11 or a portal.\n" + "Use GDK_BACKEND=x11 to enable color picking.\n")); + return; + } + gd = g_new0 (GrabData, 1); gd->color_widget = widget; gd->time = gtk_get_current_event_time (); diff --git a/src/main.c b/src/main.c index 1bfce7e..016293f 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,10 @@ # include #endif +#ifdef GDK_WINDOWING_WAYLAND +# include +#endif + #include "yad.h" YadOptions options; @@ -53,9 +57,22 @@ static GtkWidget *text = NULL; static gint ret = YAD_RESPONSE_ESC; static gboolean is_x11 = FALSE; +static gboolean is_wayland = FALSE; YadNTabs *tabs; +gboolean +yad_check_x11 (void) +{ + return is_x11; +} + +gboolean +yad_check_wayland (void) +{ + return is_wayland; +} + #ifndef G_OS_WIN32 static void sa_usr1 (gint sig) @@ -74,8 +91,28 @@ sa_usr2 (gint sig) else yad_exit (YAD_RESPONSE_CANCEL); } + +static void +sa_term (gint sig) +{ + /* Handle SIGTERM/SIGINT for graceful shutdown */ + if (options.plug != -1) + gtk_main_quit (); + else + yad_exit (YAD_RESPONSE_CANCEL); +} #endif +/* Callback to fix text label sizing with line wrap */ +static void +text_size_allocate_cb (GtkWidget *w, GtkAllocation *al, gpointer data) +{ + /* Set the width request to the allocated width to enable proper line wrapping. + * This prevents GTK from requesting the natural (unwrapped) width which causes + * windows to become excessively large. See issues #107, #140, #32, #297, #300 */ + gtk_widget_set_size_request (w, al->width, -1); +} + static gboolean keys_cb (GtkWidget *w, GdkEventKey *ev, gpointer d) { @@ -221,6 +258,7 @@ create_layout (GtkWidget *dlg) text = gtk_label_new (NULL); gtk_widget_set_name (text, "yad-dialog-label"); gtk_label_set_line_wrap (GTK_LABEL (text), TRUE); + gtk_label_set_line_wrap_mode (GTK_LABEL (text), PANGO_WRAP_WORD_CHAR); gtk_label_set_selectable (GTK_LABEL (text), options.data.selectable_labels); gtk_widget_set_state_flags (text, GTK_STATE_FLAG_NORMAL, FALSE); gtk_widget_set_can_focus (text, FALSE); @@ -228,6 +266,26 @@ create_layout (GtkWidget *dlg) if (options.data.text_width > 0) gtk_label_set_width_chars (GTK_LABEL (text), options.data.text_width); + /* Constrain label width when window size is constrained. + * This fixes line wrapping with --width, --height, and --geometry options. + * Using max_width_chars with ellipsize=NONE forces text to wrap instead + * of expanding the window. See issues #107, #140, #32, #297, #300 */ + if (options.data.width != -1) + { + /* Approximate chars based on width (assuming ~8px per char average) */ + gint max_chars = (options.data.width - 50) / 8; + if (max_chars > 10) + { + gtk_label_set_max_width_chars (GTK_LABEL (text), max_chars); + gtk_label_set_ellipsize (GTK_LABEL (text), PANGO_ELLIPSIZE_NONE); + gtk_widget_set_hexpand (text, FALSE); + } + } + + /* Also connect size-allocate for geometry-based constraints */ + if (options.data.geometry || options.data.width != -1) + g_signal_connect (G_OBJECT (text), "size-allocate", G_CALLBACK (text_size_allocate_cb), NULL); + /* set label align and justification */ switch (options.data.text_align) { @@ -371,28 +429,38 @@ realize_cb (GtkWidget *dlg, gpointer d) if (options.data.use_posx || options.data.use_posy) { - gint ww, wh, sw, sh; - gtk_window_get_size (GTK_WINDOW (dlg), &ww, &wh); - gdk_window_get_geometry (gdk_get_default_root_window (), NULL, NULL, &sw, &sh); - /* place window to specified coordinates */ - if (!options.data.use_posx) - gtk_window_get_position (GTK_WINDOW (dlg), &options.data.posx, NULL); - if (!options.data.use_posy) - gtk_window_get_position (GTK_WINDOW (dlg), NULL, &options.data.posy); - if (options.data.negx) + /* Window positioning doesn't work reliably on Wayland - compositor controls placement */ + if (is_wayland) { - options.data.posx = sw - ww + options.data.posx; - gravity = GDK_GRAVITY_NORTH_EAST; + if (options.debug) + g_printerr (_("WARNING: Window positioning (--posx/--posy) is controlled by " + "the Wayland compositor and may not work as expected.\n")); } - if (options.data.negy) + else { - options.data.posy = sh - wh + options.data.posy; - gravity = GDK_GRAVITY_SOUTH_WEST; + gint ww, wh, sw, sh; + gtk_window_get_size (GTK_WINDOW (dlg), &ww, &wh); + gdk_window_get_geometry (gdk_get_default_root_window (), NULL, NULL, &sw, &sh); + /* place window to specified coordinates */ + if (!options.data.use_posx) + gtk_window_get_position (GTK_WINDOW (dlg), &options.data.posx, NULL); + if (!options.data.use_posy) + gtk_window_get_position (GTK_WINDOW (dlg), NULL, &options.data.posy); + if (options.data.negx) + { + options.data.posx = sw - ww + options.data.posx; + gravity = GDK_GRAVITY_NORTH_EAST; + } + if (options.data.negy) + { + options.data.posy = sh - wh + options.data.posy; + gravity = GDK_GRAVITY_SOUTH_WEST; + } + if (options.data.negx && options.data.negy) + gravity = GDK_GRAVITY_SOUTH_EAST; + gtk_window_set_gravity (GTK_WINDOW (dlg), gravity); + gtk_window_move (GTK_WINDOW (dlg), options.data.posx, options.data.posy); } - if (options.data.negx && options.data.negy) - gravity = GDK_GRAVITY_SOUTH_EAST; - gtk_window_set_gravity (GTK_WINDOW (dlg), gravity); - gtk_window_move (GTK_WINDOW (dlg), options.data.posx, options.data.posy); } } @@ -438,7 +506,13 @@ create_dialog (void) if (options.data.center) gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER_ALWAYS); else if (options.data.mouse) - gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + { + /* Mouse positioning doesn't work on Wayland */ + if (is_wayland && options.debug) + g_printerr (_("WARNING: --mouse option is not supported on Wayland.\n")); + else + gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + } } /* set window behavior */ @@ -593,6 +667,17 @@ create_dialog (void) } } /* add buttons box to main window */ + + // Check for footer widget from form + GtkWidget *footer_widget = g_object_get_data (G_OBJECT (dlg), "yad-footer-widget"); + if (footer_widget) + { + gtk_box_pack_start (GTK_BOX (bbox), footer_widget, FALSE, FALSE, 0); + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (bbox), footer_widget, TRUE); + gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (bbox), footer_widget, TRUE); + gtk_widget_show_all (footer_widget); + } + gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); } @@ -807,6 +892,11 @@ main (gint argc, gchar ** argv) is_x11 = TRUE; #endif +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) + is_wayland = TRUE; +#endif + /* parse custom css */ if (options.css) { @@ -890,6 +980,8 @@ main (gint argc, gchar ** argv) /* set signal handlers */ signal (SIGUSR1, sa_usr1); signal (SIGUSR2, sa_usr2); + signal (SIGTERM, sa_term); + signal (SIGINT, sa_term); #endif if (!is_x11 && options.plug != -1) diff --git a/src/print.c b/src/print.c index c7f61cb..4e7db68 100644 --- a/src/print.c +++ b/src/print.c @@ -284,7 +284,11 @@ yad_print_run (void) if (options.data.center) gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER); else if (options.data.mouse) - gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + { + /* Mouse positioning doesn't work on Wayland */ + if (!yad_check_wayland ()) + gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); + } /* create yad's top box */ if (options.data.dialog_text || options.data.dialog_image) diff --git a/src/yad.h b/src/yad.h index 6f39282..58ca199 100644 --- a/src/yad.h +++ b/src/yad.h @@ -751,6 +751,9 @@ YadSearchBar *create_search_bar (); gboolean yad_confirm_dlg (GtkWindow *parent, gchar *txt); +gboolean yad_check_x11 (void); +gboolean yad_check_wayland (void); + static inline void strip_new_line (gchar * str) {