From 0e8d6a76b63c30a5897cae1b12d81709af23bfc1 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 08:27:49 +0200 Subject: [PATCH 01/45] tests: fix GtkBuilder signal callback prototypes Based on the Guix gtk2-fix-builder-test.patch. The callbacks are connected to notify-style signals and must take a GParamSpec pointer, not a GParamSpec by value. This avoids stack/register ABI problems on platforms such as ppc64el. --- gtk/tests/builder.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gtk/tests/builder.c b/gtk/tests/builder.c index ebba220e9..121012704 100644 --- a/gtk/tests/builder.c +++ b/gtk/tests/builder.c @@ -132,7 +132,7 @@ static int object = 0; static int object_after = 0; void /* exported for GtkBuilder */ -signal_normal (GtkWindow *window, GParamSpec spec) +signal_normal (GtkWindow *window, GParamSpec *spec) { g_assert (GTK_IS_WINDOW (window)); g_assert (normal == 0); @@ -142,7 +142,7 @@ signal_normal (GtkWindow *window, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_after (GtkWindow *window, GParamSpec spec) +signal_after (GtkWindow *window, GParamSpec *spec) { g_assert (GTK_IS_WINDOW (window)); g_assert (normal == 1); @@ -152,7 +152,7 @@ signal_after (GtkWindow *window, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_object (GtkButton *button, GParamSpec spec) +signal_object (GtkButton *button, GParamSpec *spec) { g_assert (GTK_IS_BUTTON (button)); g_assert (object == 0); @@ -162,7 +162,7 @@ signal_object (GtkButton *button, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_object_after (GtkButton *button, GParamSpec spec) +signal_object_after (GtkButton *button, GParamSpec *spec) { g_assert (GTK_IS_BUTTON (button)); g_assert (object == 1); @@ -172,28 +172,28 @@ signal_object_after (GtkButton *button, GParamSpec spec) } void /* exported for GtkBuilder */ -signal_first (GtkButton *button, GParamSpec spec) +signal_first (GtkButton *button, GParamSpec *spec) { g_assert (normal == 0); normal = 10; } void /* exported for GtkBuilder */ -signal_second (GtkButton *button, GParamSpec spec) +signal_second (GtkButton *button, GParamSpec *spec) { g_assert (normal == 10); normal = 20; } void /* exported for GtkBuilder */ -signal_extra (GtkButton *button, GParamSpec spec) +signal_extra (GtkButton *button, GParamSpec *spec) { g_assert (normal == 20); normal = 30; } void /* exported for GtkBuilder */ -signal_extra2 (GtkButton *button, GParamSpec spec) +signal_extra2 (GtkButton *button, GParamSpec *spec) { g_assert (normal == 30); normal = 40; From 790b8c2b69c3e6d520e6b53f467fddc2f7bb80cf Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 08:34:50 +0200 Subject: [PATCH 02/45] gtkrc: search XDG data dirs for themes Based on the Guix gtk2-theme-paths.patch by Jookia. Keep the existing ~/.themes and gtk_rc_get_theme_dir() lookup paths, but check g_get_system_data_dirs() in between so profile- and prefix-based theme installations can be found. --- gtk/gtkrc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/gtk/gtkrc.c b/gtk/gtkrc.c index 7bfbea85c..d316a0d90 100644 --- a/gtk/gtkrc.c +++ b/gtk/gtkrc.c @@ -808,6 +808,8 @@ gtk_rc_parse_named (GtkRcContext *context, gchar *path = NULL; const gchar *home_dir; gchar *subpath; + const gchar * const *xdg_data_dirs; + gint i; if (type) subpath = g_strconcat ("gtk-2.0-", type, @@ -829,6 +831,22 @@ gtk_rc_parse_named (GtkRcContext *context, } } + if (!path) + { + xdg_data_dirs = g_get_system_data_dirs (); + for (i = 0; xdg_data_dirs[i]; i++) + { + path = g_build_filename (xdg_data_dirs[i], "themes", name, subpath, NULL); + if (g_file_test (path, G_FILE_TEST_EXISTS)) + break; + else + { + g_free (path); + path = NULL; + } + } + } + if (!path) { gchar *theme_dir = gtk_rc_get_theme_dir (); From d8b26dc0b746228343eb2f1532509b6ac567c041 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 11:25:19 +0200 Subject: [PATCH 03/45] gtktrayicon: use explicit void prototypes for reserved slots Based on the Mageia gtk+-2.24.32-warnings.patch. Declare the reserved GtkTrayIconClass function pointers with explicit void parameter lists to avoid strict-prototype warnings without changing the class layout. --- gtk/gtktrayicon.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gtk/gtktrayicon.h b/gtk/gtktrayicon.h index 9ad1c54f2..ab4eda2f5 100644 --- a/gtk/gtktrayicon.h +++ b/gtk/gtktrayicon.h @@ -46,12 +46,12 @@ struct _GtkTrayIconClass { GtkPlugClass parent_class; - void (*__gtk_reserved1); - void (*__gtk_reserved2); - void (*__gtk_reserved3); - void (*__gtk_reserved4); - void (*__gtk_reserved5); - void (*__gtk_reserved6); + void (*__gtk_reserved1) (void); + void (*__gtk_reserved2) (void); + void (*__gtk_reserved3) (void); + void (*__gtk_reserved4) (void); + void (*__gtk_reserved5) (void); + void (*__gtk_reserved6) (void); }; GType gtk_tray_icon_get_type (void) G_GNUC_CONST; From 292e512bc4a0ba9087dc2732ff51b103eeed2522 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:15:53 +0200 Subject: [PATCH 04/45] build: disable GNU visibility aliases on Apple platforms Based on the MacPorts patch-aliases.diff. Keep the existing GNU visibility alias generation on other platforms, but avoid it on Apple targets where this alias mechanism is not suitable. --- gdk/makegdkalias.pl | 2 +- gtk/makegtkalias.pl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gdk/makegdkalias.pl b/gdk/makegdkalias.pl index 97a6e6007..27646cc0a 100755 --- a/gdk/makegdkalias.pl +++ b/gdk/makegdkalias.pl @@ -14,7 +14,7 @@ print < -#ifdef G_HAVE_GNUC_VISIBILITY +#if defined(G_HAVE_GNUC_VISIBILITY) && !defined(__APPLE__) EOF diff --git a/gtk/makegtkalias.pl b/gtk/makegtkalias.pl index 3f956b05e..67ec7c973 100755 --- a/gtk/makegtkalias.pl +++ b/gtk/makegtkalias.pl @@ -14,7 +14,7 @@ print < -#ifdef G_HAVE_GNUC_VISIBILITY +#if defined(G_HAVE_GNUC_VISIBILITY) && !defined(__APPLE__) EOF From 72dcfc7ca5fc2a78b47b45e3fe67bfe0ed394911 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:16:45 +0200 Subject: [PATCH 05/45] quartz: add newer macOS version constants Based on the MacPorts patch-gdkquartz-osx-versions.diff. Extend the Quartz OS version enum so later macOS-specific code can distinguish Catalina, Big Sur, Monterey, Ventura, Sonoma, and Sequoia while preserving the existing older constants. --- gdk/quartz/gdkquartz.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h index 8206db019..42aac50b9 100644 --- a/gdk/quartz/gdkquartz.h +++ b/gdk/quartz/gdkquartz.h @@ -54,7 +54,13 @@ typedef enum GDK_OSX_SIERRA = 12, GDK_OSX_HIGH_SIERRA = 13, GDK_OSX_MOJAVE = 14, - GDK_OSX_CURRENT = 14, + GDK_OSX_CATALINA = 15, + GDK_OSX_BIG_SUR = 16, + GDK_OSX_MONTEREY = 17, + GDK_OSX_VENTURA = 18, + GDK_OSX_SONOMA = 19, + GDK_OSX_SEQUOIA = 20, + GDK_OSX_CURRENT = 20, GDK_OSX_NEW = 99 } GdkOSXVersion; From 0bc8029032828499c3e8c98488b8c1c89d60254e Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:27:16 +0200 Subject: [PATCH 06/45] quartz: avoid focus locking on Mojave and newer Based on part of the MacPorts Big Sur performance patch for GIMP issue 5989. The NSView focus-locking API is deprecated on newer macOS releases and can cause expensive redraw behaviour. Keep the old path for older macOS versions, but avoid lockFocusIfCanDraw/unlockFocus on Mojave and newer. --- gdk/quartz/gdkdrawable-quartz.c | 3 ++- gdk/quartz/gdkwindow-quartz.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gdk/quartz/gdkdrawable-quartz.c b/gdk/quartz/gdkdrawable-quartz.c index 11dd12737..eb520f08d 100644 --- a/gdk/quartz/gdkdrawable-quartz.c +++ b/gdk/quartz/gdkdrawable-quartz.c @@ -893,7 +893,8 @@ gdk_quartz_drawable_release_context (GdkDrawable *drawable, if (window_impl->in_paint_rect_count == 0) { _gdk_quartz_drawable_flush (drawable); - [window_impl->view unlockFocus]; + if (gdk_quartz_osx_version () < GDK_OSX_MOJAVE) + [window_impl->view unlockFocus]; } } else if (GDK_IS_PIXMAP_IMPL_QUARTZ (drawable)) diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c index 6c7a2facb..205fd85e6 100644 --- a/gdk/quartz/gdkwindow-quartz.c +++ b/gdk/quartz/gdkwindow-quartz.c @@ -104,7 +104,8 @@ gdk_window_impl_quartz_get_context (GdkDrawable *drawable, * and for widgets that send fake expose events like the arrow * buttons in spinbuttons or the position marker in rulers. */ - if (window_impl->in_paint_rect_count == 0) + if (window_impl->in_paint_rect_count == 0 && + gdk_quartz_osx_version () < GDK_OSX_MOJAVE) { if (![window_impl->view lockFocusIfCanDraw]) return NULL; From 8b7fda063e32d8fa920ff76d60ced2cc895ffb13 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:29:27 +0200 Subject: [PATCH 07/45] quartz: map macOS 11 and newer to internal OS versions Based on part of the MacPorts Big Sur performance patch for GIMP issue 5989. GTK2 represents macOS 10.x releases by their minor version. Read the major version as well and map macOS 11 and newer into the same sequence so Big Sur and later releases can be detected correctly. --- gdk/quartz/gdkglobals-quartz.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/gdk/quartz/gdkglobals-quartz.c b/gdk/quartz/gdkglobals-quartz.c index f013f2830..4e596eef5 100644 --- a/gdk/quartz/gdkglobals-quartz.c +++ b/gdk/quartz/gdkglobals-quartz.c @@ -34,9 +34,20 @@ gdk_quartz_osx_version (void) if (minor == GDK_OSX_UNSUPPORTED) { - OSErr err = Gestalt (gestaltSystemVersionMinor, (SInt32*)&minor); + gint32 major = GDK_OSX_UNSUPPORTED; + OSErr err = Gestalt (gestaltSystemVersionMajor, (SInt32*)&major); g_return_val_if_fail (err == noErr, GDK_OSX_UNSUPPORTED); + + err = Gestalt (gestaltSystemVersionMinor, (SInt32*)&minor); + g_return_val_if_fail (err == noErr, GDK_OSX_UNSUPPORTED); + + /* GTK2 encodes macOS 10.x releases by their minor version. + * Map macOS 11+ into the same sequence: + * 11 -> 16, 12 -> 17, 13 -> 18, ... + */ + if (major > 10) + minor = major + 5; } if (minor < GDK_OSX_MIN) From 21fdf2eeaaa02ee2fc18374593ae3d8b01d17b61 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:32:30 +0200 Subject: [PATCH 08/45] quartz: use CGContext accessor on Yosemite and newer Based on part of the MacPorts Big Sur performance patch for GIMP issue 5989. Use the modern NSGraphicsContext CGContext accessor on Yosemite and newer, matching the existing Quartz window code, while preserving graphicsPort for older macOS versions. --- gtk/gtkquartz.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gtk/gtkquartz.c b/gtk/gtkquartz.c index 87ba87745..caf8404f8 100644 --- a/gtk/gtkquartz.c +++ b/gtk/gtkquartz.c @@ -67,7 +67,10 @@ _gtk_quartz_create_image_from_pixbuf (GdkPixbuf *pixbuf) } [nsimage lockFocus]; - context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + if (gdk_quartz_osx_version () < GDK_OSX_YOSEMITE) + context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + else + context = [[NSGraphicsContext currentContext] CGContext]; CGContextDrawImage (context, CGRectMake (0, 0, pixbuf_width, pixbuf_height), image); [nsimage unlockFocus]; From fe45d0944c35268c817aca1be9a5f9d6077f90a7 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:41:36 +0200 Subject: [PATCH 09/45] quartz: avoid screen update suppression on Mojave and newer Based on part of the MacPorts Big Sur performance patch for GIMP issue 5989. NSDisableScreenUpdates and NSEnableScreenUpdates are deprecated on newer macOS releases. Keep using them for older macOS versions, but avoid them on Mojave and newer. --- gdk/quartz/gdkwindow-quartz.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c index 205fd85e6..5c1bde0cd 100644 --- a/gdk/quartz/gdkwindow-quartz.c +++ b/gdk/quartz/gdkwindow-quartz.c @@ -469,7 +469,8 @@ _gdk_windowing_before_process_all_updates (void) { in_process_all_updates = TRUE; - NSDisableScreenUpdates (); + if (gdk_quartz_osx_version () < GDK_OSX_MOJAVE) + NSDisableScreenUpdates (); } void @@ -503,7 +504,8 @@ _gdk_windowing_after_process_all_updates (void) in_process_all_updates = FALSE; - NSEnableScreenUpdates (); + if (gdk_quartz_osx_version () < GDK_OSX_MOJAVE) + NSEnableScreenUpdates (); } static void From 9d7c7e63d614759cdeafd672dd3d53ccef258825 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:49:48 +0200 Subject: [PATCH 10/45] quartz: request 8-bit layer contents on Big Sur and newer Based on part of the MacPorts Big Sur performance patch for GIMP issue 5989. Use a dynamic Objective-C call to request 8-bit layer contents on macOS 11 and newer, avoiding a hard QuartzCore/CALayer compile-time dependency and preserving old behaviour on older macOS releases. --- gdk/quartz/GdkQuartzView.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c index 368e040ec..bad737da2 100644 --- a/gdk/quartz/GdkQuartzView.c +++ b/gdk/quartz/GdkQuartzView.c @@ -599,6 +599,25 @@ gdk_screen_get_rgba_colormap (_gdk_screen); } +-(void)viewWillDraw +{ + /* macOS 11 (Big Sur) and newer may use a higher precision backing + * format for layer contents. For GTK2's 8-bit drawing this can add + * avoidable conversion overhead. Use a dynamic Objective-C call here + * instead of a hard QuartzCore/CALayer dependency, so old SDKs and old + * macOS targets keep building and keep their old behaviour. + */ + if (gdk_quartz_osx_version () >= GDK_OSX_BIG_SUR) + { + id layer = [self layer]; + + if (layer && [layer respondsToSelector: @selector(setContentsFormat:)]) + [layer setValue: @"RGBA8Uint" forKey: @"contentsFormat"]; + } + + [super viewWillDraw]; +} + -(void)drawRect: (NSRect)rect { GdkRectangle gdk_rect; From c1fc6ed6424828c3077a083423de93f4029fcbbf Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:54:51 +0200 Subject: [PATCH 11/45] quartz: avoid extra content invalidation on Big Sur and newer Based on part of the MacPorts Big Sur performance patch for GIMP issue 5989. Keep the existing Mojave-era setNeedsDisplay workaround for macOS 10.14 and 10.15, but avoid the extra full-window invalidation on Big Sur and newer where it can cause expensive redraw behaviour. --- gdk/quartz/gdkwindow-quartz.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c index 5c1bde0cd..38ada80b2 100644 --- a/gdk/quartz/gdkwindow-quartz.c +++ b/gdk/quartz/gdkwindow-quartz.c @@ -491,8 +491,9 @@ _gdk_windowing_after_process_all_updates (void) /* 10.14 needs to be told that the view needs to be redrawn, see * https://gitlab.gnome.org/GNOME/gtk/issues/1479 */ - if (gdk_quartz_osx_version() >= GDK_OSX_MOJAVE) - [[nswindow contentView] setNeedsDisplay:YES]; + if (gdk_quartz_osx_version () >= GDK_OSX_MOJAVE && + gdk_quartz_osx_version () < GDK_OSX_BIG_SUR) + [[nswindow contentView] setNeedsDisplay:YES]; [nswindow enableFlushWindow]; [nswindow flushWindow]; [nswindow release]; From 8d4015ee54b85becc0f2935c3ca6f25905a06508 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 09:59:26 +0200 Subject: [PATCH 12/45] quartz: avoid extra resize-idle updates on Big Sur and newer Based on part of the MacPorts Big Sur performance patch for GIMP issue 5989. Keep the existing gdk_window_process_all_updates() call for non-Quartz targets and for older macOS releases, but avoid this extra resize-idle update path on Big Sur and newer where it can contribute to expensive full-window redraw behaviour. --- gtk/gtkcontainer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c index 2f7007fd2..e4c98c8c2 100644 --- a/gtk/gtkcontainer.c +++ b/gtk/gtkcontainer.c @@ -40,6 +40,9 @@ #include "gtktoolbar.h" #include #include +#ifdef GDK_WINDOWING_QUARTZ +#include "gdk/quartz/gdkquartz.h" +#endif #include "gtkalias.h" @@ -1357,7 +1360,16 @@ gtk_container_idle_sizer (gpointer data) gtk_container_check_resize (GTK_CONTAINER (widget)); } +#ifdef GDK_WINDOWING_QUARTZ + /* macOS 11 and newer can trigger expensive full-window redraws here. + * Keep the traditional update processing path for older Quartz targets, + * but let the normal GDK update idle handle updates on Big Sur and newer. + */ + if (gdk_quartz_osx_version () < GDK_OSX_BIG_SUR) + gdk_window_process_all_updates (); +#else gdk_window_process_all_updates (); +#endif return FALSE; } From 65f949bcded5e103d370f3cba89027318490e6b9 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 10:06:34 +0200 Subject: [PATCH 13/45] win32: improve mouse window lookup through transparent children Based on the MSYS2 0013_fix_mouse_events.patch. Use CWP_SKIPTRANSPARENT while walking from the desktop window, but stop skipping transparent windows after the first level so transparent child windows are still considered correctly. --- gdk/win32/gdkwindow-win32.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c index b7ea0404a..1a21a1b67 100644 --- a/gdk/win32/gdkwindow-win32.c +++ b/gdk/win32/gdkwindow-win32.c @@ -2767,6 +2767,10 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display, * WindowFromPoint() can find our windows, we follow similar logic * here, and ignore invisible and disabled windows. */ + UINT cwp_flags = CWP_SKIPDISABLED | + CWP_SKIPINVISIBLE | + CWP_SKIPTRANSPARENT; + hwnd = GetDesktopWindow (); do { window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwnd); @@ -2777,8 +2781,7 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display, break; screen_to_client (hwnd, screen_pt, &client_pt); - hwndc = ChildWindowFromPointEx (hwnd, client_pt, CWP_SKIPDISABLED | - CWP_SKIPINVISIBLE); + hwndc = ChildWindowFromPointEx (hwnd, client_pt, cwp_flags); /* Verify that we're really inside the client area of the window */ if (hwndc != hwnd) @@ -2789,6 +2792,8 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display, hwndc = hwnd; } + /* Only ignore top-level transparent windows */ + cwp_flags &= ~CWP_SKIPTRANSPARENT; } while (hwndc != hwnd && (hwnd = hwndc, 1)); } From d181fe8fc3d62dab2bc9640c28bd2c44eae24f59 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 10:07:25 +0200 Subject: [PATCH 14/45] icontheme: detect SVG icons by extension on Win32 Based on the MSYS2 0022-icontheme-win32-detect-SVG-files-by-extension.patch. Some Windows systems report .svg files with a generic or incorrect content type. On Win32, fall back to the .svg extension so SVG icons can still be detected. --- gtk/gtkicontheme.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index 0d4435c72..f8aaf9a3b 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -2945,6 +2945,10 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, if (mime_type && strcmp (mime_type, "image/svg+xml") == 0) is_svg = TRUE; +#ifdef G_OS_WIN32 + else if (content_type && strcmp (content_type, ".svg") == 0) + is_svg = TRUE; +#endif } g_object_unref (file_info); From 4d7fc9558334fc36c7783cbd96f3dcdef3b6846b Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 10:08:01 +0200 Subject: [PATCH 15/45] win32: initialize key state before updating keymap Based on the MSYS2 0023-gdkkeys-win32.c-fix-initialisation-of-key_state-in-u.patch. Clear the key_state array before using it during keymap updates so stale state cannot affect layout handling, including Japanese IME input. --- gdk/win32/gdkkeys-win32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c index d8ab30347..9a34083c4 100644 --- a/gdk/win32/gdkkeys-win32.c +++ b/gdk/win32/gdkkeys-win32.c @@ -692,6 +692,7 @@ update_keymap (GdkKeymap *gdk_keymap) if (hkls_len != keymap->layout_handles->len) keymap->keysym_tab = g_renew (guint, keymap->keysym_tab, keysym_tab_size); + memset (key_state, 0, sizeof(key_state)); memset (keymap->keysym_tab, 0, keysym_tab_size); g_array_set_size (keymap->layout_handles, hkls_len); g_array_set_size (keymap->options, hkls_len); From 757774345d1b942dfb056362bd45abfefff4e0df Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 10:09:36 +0200 Subject: [PATCH 16/45] win32: quote icon cache manifest in resource file Based on the MSYS2 0024-gtk-update-icon-cache-rc-quote.patch. Quote the manifest path when generating the gtk-update-icon-cache resource file so windres parses it correctly. --- gtk/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 8f5e496a3..c3c9a1493 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -1139,7 +1139,7 @@ $(GTK_UPDATE_ICON_CACHE_MANIFEST): echo '' ) >$@ $(GTK_UPDATE_ICON_CACHE_RC): - (echo -e '#include \nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST '$(GTK_UPDATE_ICON_CACHE_MANIFEST)) >$@ + (echo -e '#include \nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "'$(GTK_UPDATE_ICON_CACHE_MANIFEST)'"') >$@ $(GTK_UPDATE_ICON_CACHE_MANIFEST_OBJECT): $(GTK_UPDATE_ICON_CACHE_RC) $(GTK_UPDATE_ICON_CACHE_MANIFEST) $(WINDRES) --input $< --output $@ --output-format=coff From b622340b5b447d0094f5726127d9252278bf39cc Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 11:46:52 +0200 Subject: [PATCH 17/45] print: preserve default-printer wait during status updates Based on the openSUSE gtk2-default-printer.patch. Avoid treating printer status updates as user-driven printer selection changes, so the print dialog can continue waiting for a default printer that may appear later. --- gtk/gtkprintunixdialog.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gtk/gtkprintunixdialog.c b/gtk/gtkprintunixdialog.c index da9010b96..af64a5a37 100644 --- a/gtk/gtkprintunixdialog.c +++ b/gtk/gtkprintunixdialog.c @@ -796,7 +796,9 @@ printer_status_cb (GtkPrintBackend *backend, /* When the pause state change then we need to update sensitive property * of GTK_RESPONSE_OK button inside of selected_printer_changed function. */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->printer_treeview)); + priv->internal_printer_change = TRUE; selected_printer_changed (selection, dialog); + priv->internal_printer_change = FALSE; if (gtk_print_backend_printer_list_is_done (backend) && gtk_printer_is_default (printer) && From 00177dd11c69e21e78ec92442cdf189cfc840a47 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 10:55:56 +0200 Subject: [PATCH 18/45] docs: use top_srcdir for GDK X11 header reference Based on the MSYS2 0005-Fix-header-ref.all.patch. Use an explicit top_srcdir-relative path for gdkx.h in the GDK reference documentation instead of a fragile relative path. --- docs/reference/gdk/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/gdk/Makefile.am b/docs/reference/gdk/Makefile.am index c0facc151..43a67508c 100644 --- a/docs/reference/gdk/Makefile.am +++ b/docs/reference/gdk/Makefile.am @@ -37,7 +37,7 @@ IGNORE_HFILES= \ # Extra files to add when scanning (relative to $srcdir) EXTRA_HFILES= \ - ../../../gdk/x11/gdkx.h + $(top_srcdir)/gdk/x11/gdkx.h # CFLAGS and LDFLAGS for compiling scan program. Only needed # if $(DOC_MODULE).types is non-empty. From 71023e03331f1b86bcc4af3aba0b9020d0aaf5b6 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 4 Aug 2026 15:34:12 +0200 Subject: [PATCH 19/45] gdk: add endian-aware 30-bit RGB converters Add specialized converters for both 10:10:10 channel layouts and both X image byte orders. Preserve depth-30 padding bits, avoid invalid negative shifts in the generic converter and select endian conversion at compile time. Validated with a real Xvfb 30-bit A/B pixel test and cross-target endian store checks. Derived from downstream commit 02a2c61f2dfc574a88343c81ddf10fc515136533. Co-authored-by: Daemonratte --- gdk/gdkrgb.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/gdk/gdkrgb.c b/gdk/gdkrgb.c index b0ff66833..dddf1991c 100644 --- a/gdk/gdkrgb.c +++ b/gdk/gdkrgb.c @@ -2242,6 +2242,68 @@ gdk_rgb_convert_8880_br (GdkRgbInfo *image_info, GdkImage *image, } } +#define DEFINE_GDK_RGB_CONVERT_101010(name, high, low, to_order) \ +static void \ +name (GdkRgbInfo *image_info, GdkImage *image, \ + gint x0, gint y0, gint width, gint height, \ + const guchar *buf, int rowstride, \ + gint x_align, gint y_align, GdkRgbCmap *cmap) \ +{ \ + int y, w; \ + guchar *obuf, *p; \ + gint bpl; \ + const guchar *bptr, *bp2; \ + guint32 r, g, b; \ + guint32 pixel; \ + guint32 alpha_mask; \ + \ + alpha_mask = image_info->visual->depth == 32 ? 0xc0000000U : 0; \ + \ + bptr = buf; \ + bpl = image->bpl; \ + obuf = ((guchar *) image->mem) + y0 * bpl + x0 * 4; \ + \ + for (y = 0; y < height; y++) \ + { \ + bp2 = bptr; \ + p = obuf; \ + w = width; \ + \ + while (w--) \ + { \ + r = *bp2++; \ + g = *bp2++; \ + b = *bp2++; \ + \ + r = (r << 2) | (r >> 6); \ + g = (g << 2) | (g >> 6); \ + b = (b << 2) | (b >> 6); \ + \ + pixel = alpha_mask | \ + (high << 20) | \ + (g << 10) | \ + low; \ + \ + *((guint32 *) p) = to_order (pixel); \ + p += 4; \ + } \ + \ + bptr += rowstride; \ + obuf += bpl; \ + } \ +} + +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_r_low_lsb, + b, r, GUINT32_TO_LE) +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_b_low_lsb, + r, b, GUINT32_TO_LE) +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_r_low_msb, + b, r, GUINT32_TO_BE) +DEFINE_GDK_RGB_CONVERT_101010 (gdk_rgb_convert_101010_b_low_msb, + r, b, GUINT32_TO_BE) + +#undef DEFINE_GDK_RGB_CONVERT_101010 + /* Generic truecolor/directcolor conversion function. Slow, but these are oddball modes. */ static void @@ -3195,6 +3257,34 @@ gdk_rgb_select_conv (GdkRgbInfo *image_info) #endif } #endif + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_LSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + conv = gdk_rgb_convert_101010_r_low_lsb; + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_LSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + conv = gdk_rgb_convert_101010_b_low_lsb; + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_MSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + conv = gdk_rgb_convert_101010_r_low_msb; + else if (bpp == 32 && (depth == 30 || depth == 32) && + vtype == GDK_VISUAL_TRUE_COLOR && + byte_order == GDK_MSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + conv = gdk_rgb_convert_101010_b_low_msb; else if (vtype == GDK_VISUAL_TRUE_COLOR && byte_order == GDK_LSB_FIRST) { conv = gdk_rgb_convert_truecolor_lsb; From be64a902a5dfa76aaca4d66ad85505a76ef4016e Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 4 Aug 2026 17:55:45 +0200 Subject: [PATCH 20/45] gdk: optimize native big-endian RGB565 paths Add native big-endian four-pixel converters for RGB565, grayscale RGB565, and dithered RGB565. Select the block converters only where cross-target code generation is favorable: RGB on PowerPC, s390x, ARMv5+, and AArch64; grayscale on PowerPC, ARMv5+, AArch64, and m68k; and dither on PowerPC. Retain the scalar fallbacks everywhere else. The little-endian machine code remains byte-for-byte unchanged. No runtime endian checks, allocations, or additional image buffers are introduced. Validated with a complete build and test suite, deterministic and randomized big-endian formula tests, exact little-endian .text comparison, and cross-target code-generation checks. --- gdk/gdkrgb.c | 146 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 113 insertions(+), 33 deletions(-) diff --git a/gdk/gdkrgb.c b/gdk/gdkrgb.c index dddf1991c..4f2e0d44f 100644 --- a/gdk/gdkrgb.c +++ b/gdk/gdkrgb.c @@ -1464,19 +1464,33 @@ gdk_rgb_convert_gray8_gray (GdkRgbInfo *image_info, GdkImage *image, } } -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define HAIRY_CONVERT_565 + +#if G_BYTE_ORDER == G_BIG_ENDIAN +#if defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__ppc__) || defined(__ppc64__) || \ + defined(__PPC__) || defined(__PPC64__) || \ + defined(__POWERPC__) || defined(_ARCH_PPC) +#define GDK_RGB_FAST_BE565_RGB +#define GDK_RGB_FAST_BE565_GRAY +#define GDK_RGB_FAST_BE565_DITHER +#elif defined(__s390x__) +#define GDK_RGB_FAST_BE565_RGB +#elif defined(__aarch64__) +#define GDK_RGB_FAST_BE565_RGB +#define GDK_RGB_FAST_BE565_GRAY +#elif defined(__arm__) && defined(__ARM_ARCH) && \ + __ARM_ARCH >= 5 +#define GDK_RGB_FAST_BE565_RGB +#define GDK_RGB_FAST_BE565_GRAY +#elif defined(__m68k__) +#define GDK_RGB_FAST_BE565_GRAY +#endif #endif -#ifdef HAIRY_CONVERT_565 /* Render a 24-bit RGB image in buf into the GdkImage, without dithering. - This assumes native byte ordering - what should really be done is to - check whether the image byte_order is consistent with the _ENDIAN - config flag, and if not, use a different function. - - This one is even faster than the one below - its inner loop loads 3 - words (i.e. 4 24-bit pixels), does a lot of shifting and masking, - then writes 2 words. */ + The four-pixel inner loop uses compile-time-specialized native word + layouts for little- and big-endian systems. */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN || defined(GDK_RGB_FAST_BE565_RGB) static void gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1520,6 +1534,7 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, r1b0g0r0 = ((guint32 *)bp2)[0]; g2r2b1g1 = ((guint32 *)bp2)[1]; b3g3r3b2 = ((guint32 *)bp2)[2]; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN ((guint32 *)obptr)[0] = ((r1b0g0r0 & 0xf8) << 8) | ((r1b0g0r0 & 0xfc00) >> 5) | @@ -1534,6 +1549,22 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, ((b3g3r3b2 & 0xf800) << 16) | ((b3g3r3b2 & 0xfc0000) << 3) | ((b3g3r3b2 & 0xf8000000) >> 11); +#else + ((guint32 *)obptr)[0] = + (r1b0g0r0 & 0xf8000000) | + ((r1b0g0r0 & 0x00fc0000) << 3) | + ((r1b0g0r0 & 0x0000f800) << 5) | + ((r1b0g0r0 & 0x000000f8) << 8) | + ((g2r2b1g1 & 0xfc000000) >> 21) | + ((g2r2b1g1 & 0x00f80000) >> 19); + ((guint32 *)obptr)[1] = + ((g2r2b1g1 & 0x0000f800) << 16) | + ((g2r2b1g1 & 0x000000fc) << 19) | + ((b3g3r3b2 & 0xf8000000) >> 11) | + ((b3g3r3b2 & 0x00f80000) >> 8) | + ((b3g3r3b2 & 0x0000fc00) >> 5) | + ((b3g3r3b2 & 0x000000f8) >> 3); +#endif bp2 += 12; obptr += 8; } @@ -1553,27 +1584,6 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, } } #else -/* Render a 24-bit RGB image in buf into the GdkImage, without dithering. - This assumes native byte ordering - what should really be done is to - check whether the image byte_order is consistent with the _ENDIAN - config flag, and if not, use a different function. - - This routine is faster than the one included with Gtk 1.0 for a number - of reasons: - - 1. Shifting instead of lookup tables (less memory traffic). - - 2. Much less register pressure, especially because shifts are - in the code. - - 3. A memcpy is avoided (i.e. the transfer function). - - 4. On big-endian architectures, byte swapping is avoided. - - That said, it wouldn't be hard to make it even faster - just make an - inner loop that reads 3 words (i.e. 4 24-bit pixels), does a lot of - shifting and masking, then writes 2 words. -*/ static void gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1607,7 +1617,7 @@ gdk_rgb_convert_565 (GdkRgbInfo *image_info, GdkImage *image, } #endif -#ifdef HAIRY_CONVERT_565 +#if G_BYTE_ORDER == G_LITTLE_ENDIAN || defined(GDK_RGB_FAST_BE565_GRAY) static void gdk_rgb_convert_565_gray (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1645,6 +1655,7 @@ gdk_rgb_convert_565_gray (GdkRgbInfo *image_info, GdkImage *image, guint32 g3g2g1g0; g3g2g1g0 = ((guint32 *)bp2)[0]; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN ((guint32 *)obptr)[0] = ((g3g2g1g0 & 0xf8) << 8) | ((g3g2g1g0 & 0xfc) << 3) | @@ -1659,6 +1670,22 @@ gdk_rgb_convert_565_gray (GdkRgbInfo *image_info, GdkImage *image, (g3g2g1g0 & 0xf8000000) | ((g3g2g1g0 & 0xfc000000) >> 5) | ((g3g2g1g0 & 0xf8000000) >> 11); +#else + ((guint32 *)obptr)[0] = + (g3g2g1g0 & 0xf8000000) | + ((g3g2g1g0 & 0xfc000000) >> 5) | + ((g3g2g1g0 & 0xf8000000) >> 11) | + ((g3g2g1g0 & 0x00f80000) >> 8) | + ((g3g2g1g0 & 0x00fc0000) >> 13) | + ((g3g2g1g0 & 0x00f80000) >> 19); + ((guint32 *)obptr)[1] = + ((g3g2g1g0 & 0x0000f800) << 16) | + ((g3g2g1g0 & 0x0000fc00) << 11) | + ((g3g2g1g0 & 0x0000f800) << 5) | + ((g3g2g1g0 & 0x000000f8) << 8) | + ((g3g2g1g0 & 0x000000fc) << 3) | + ((g3g2g1g0 & 0x000000f8) >> 3); +#endif bp2 += 4; obptr += 8; } @@ -1745,7 +1772,7 @@ gdk_rgb_convert_565_br (GdkRgbInfo *image_info, GdkImage *image, /* Thanks to Ray Lehtiniemi for a patch that resulted in a ~25% speedup in this mode. */ -#ifdef HAIRY_CONVERT_565 +#if G_BYTE_ORDER == G_LITTLE_ENDIAN || defined(GDK_RGB_FAST_BE565_DITHER) static void gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, @@ -1800,6 +1827,7 @@ gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, r1b0g0r0 = ((guint32 *)bp2)[0]; g2r2b1g1 = ((guint32 *)bp2)[1]; b3g3r3b2 = ((guint32 *)bp2)[2]; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN rgb02 = ((r1b0g0r0 & 0xff) << 20) + ((r1b0g0r0 & 0xff00) << 2) + @@ -1846,6 +1874,54 @@ gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, ((rgb13 & 0x0f800000) << 4) | ((rgb13 & 0x0003f000) << 9) | ((rgb13 & 0x000000f8) << 13); +#else + rgb02 = + ((r1b0g0r0 & 0xff000000) >> 4) + + ((r1b0g0r0 & 0x00ff0000) >> 6) + + ((r1b0g0r0 & 0x0000ff00) >> 8) + + dmp[x & (DM_WIDTH - 1)]; + rgb02 += 0x10040100 + - ((rgb02 & 0x1e0001e0) >> 5) + - ((rgb02 & 0x00070000) >> 6); + rgb13 = + ((r1b0g0r0 & 0x000000ff) << 20) + + ((g2r2b1g1 & 0xff000000) >> 14) + + ((g2r2b1g1 & 0x00ff0000) >> 16) + + dmp[(x + 1) & (DM_WIDTH - 1)]; + rgb13 += 0x10040100 + - ((rgb13 & 0x1e0001e0) >> 5) + - ((rgb13 & 0x00070000) >> 6); + ((guint32 *)obptr)[0] = + ((rgb02 & 0x0f800000) << 4) | + ((rgb02 & 0x0003f000) << 9) | + ((rgb02 & 0x000000f8) << 13) | + ((rgb13 & 0x0f800000) >> 12) | + ((rgb13 & 0x0003f000) >> 7) | + ((rgb13 & 0x000000f8) >> 3); + rgb02 = + ((g2r2b1g1 & 0x0000ff00) << 12) + + ((g2r2b1g1 & 0x000000ff) << 10) + + ((b3g3r3b2 & 0xff000000) >> 24) + + dmp[(x + 2) & (DM_WIDTH - 1)]; + rgb02 += 0x10040100 + - ((rgb02 & 0x1e0001e0) >> 5) + - ((rgb02 & 0x00070000) >> 6); + rgb13 = + ((b3g3r3b2 & 0x00ff0000) << 4) + + ((b3g3r3b2 & 0x0000ff00) << 2) + + (b3g3r3b2 & 0x000000ff) + + dmp[(x + 3) & (DM_WIDTH - 1)]; + rgb13 += 0x10040100 + - ((rgb13 & 0x1e0001e0) >> 5) + - ((rgb13 & 0x00070000) >> 6); + ((guint32 *)obptr)[1] = + ((rgb02 & 0x0f800000) << 4) | + ((rgb02 & 0x0003f000) << 9) | + ((rgb02 & 0x000000f8) << 13) | + ((rgb13 & 0x0f800000) >> 12) | + ((rgb13 & 0x0003f000) >> 7) | + ((rgb13 & 0x000000f8) >> 3); +#endif bp2 += 12; obptr += 8; } @@ -1916,6 +1992,10 @@ gdk_rgb_convert_565_d (GdkRgbInfo *image_info, GdkImage *image, } #endif +#undef GDK_RGB_FAST_BE565_RGB +#undef GDK_RGB_FAST_BE565_GRAY +#undef GDK_RGB_FAST_BE565_DITHER + static void gdk_rgb_convert_555 (GdkRgbInfo *image_info, GdkImage *image, gint x0, gint y0, gint width, gint height, From e92e770558e9ab5a44f8f7f50984412e444e01e9 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 4 Aug 2026 18:46:39 +0200 Subject: [PATCH 21/45] gdk: support 3-bit StaticColor visuals --- gdk/gdkrgb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdk/gdkrgb.c b/gdk/gdkrgb.c index 4f2e0d44f..cd848e902 100644 --- a/gdk/gdkrgb.c +++ b/gdk/gdkrgb.c @@ -3431,7 +3431,7 @@ gdk_rgb_select_conv (GdkRgbInfo *image_info) conv = gdk_rgb_convert_gray4_pack; conv_d = gdk_rgb_convert_gray4_d_pack; } - else if (bpp == 4 && depth == 4 && + else if (bpp == 4 && depth <= 4 && depth >= 3 && vtype == GDK_VISUAL_STATIC_COLOR) conv = gdk_rgb_convert_4_pack; From b58fc237658965b313abb751deb71574584161c2 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 4 Aug 2026 19:08:54 +0200 Subject: [PATCH 22/45] gdk: support 2-bit StaticColor visuals --- gdk/gdkrgb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdk/gdkrgb.c b/gdk/gdkrgb.c index cd848e902..edb88c6d8 100644 --- a/gdk/gdkrgb.c +++ b/gdk/gdkrgb.c @@ -3431,7 +3431,7 @@ gdk_rgb_select_conv (GdkRgbInfo *image_info) conv = gdk_rgb_convert_gray4_pack; conv_d = gdk_rgb_convert_gray4_d_pack; } - else if (bpp == 4 && depth <= 4 && depth >= 3 && + else if (bpp == 4 && depth <= 4 && depth >= 2 && vtype == GDK_VISUAL_STATIC_COLOR) conv = gdk_rgb_convert_4_pack; From a923aa3120628bec953ac12624417cd3378da8f3 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 4 Aug 2026 19:41:58 +0200 Subject: [PATCH 23/45] gdk: fix packed StaticColor dithering --- gdk/gdkrgb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdk/gdkrgb.c b/gdk/gdkrgb.c index edb88c6d8..3da8c820f 100644 --- a/gdk/gdkrgb.c +++ b/gdk/gdkrgb.c @@ -2697,7 +2697,7 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, r = *bp2++; g = *bp2++; b = *bp2++; - dith = (dmp[(x_align + x + 1) & (DM_WIDTH - 1)] << 2) | 3; + dith = (dmp[(x_align + x) & (DM_WIDTH - 1)] << 2) | 3; ix = (((r + dith) & 0x100) >> 2) | (((g + 258 - dith) & 0x100) >> 5) | (((b + dith) & 0x100) >> 8); @@ -2724,7 +2724,7 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, r = *bp2++; g = *bp2++; b = *bp2++; - dith = (dmp[(x_align + x + 1) & (DM_WIDTH - 1)] << 2) | 3; + dith = (dmp[(x_align + x) & (DM_WIDTH - 1)] << 2) | 3; ix = (((r + dith) & 0x100) >> 2) | (((g + 258 - dith) & 0x100) >> 5) | (((b + dith) & 0x100) >> 8); From 8984300bbaa4d8f77ebea668b8cbe416d6bc3546 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 4 Aug 2026 20:24:57 +0200 Subject: [PATCH 24/45] gdk: optimize packed StaticColor conversion --- gdk/gdkrgb.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/gdk/gdkrgb.c b/gdk/gdkrgb.c index 3da8c820f..e4f63ddaf 100644 --- a/gdk/gdkrgb.c +++ b/gdk/gdkrgb.c @@ -2706,7 +2706,7 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, obptr++; x++; } - while (x < width) + while (x + 1 < width) { r = *bp2++; g = *bp2++; @@ -2717,9 +2717,6 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, (((b + dith) & 0x100) >> 8); pix0 = (colorcube_d[ix]); x++; - if (x == width) - pix1 = (*obptr & 0x0F); - else { r = *bp2++; g = *bp2++; @@ -2733,6 +2730,18 @@ gdk_rgb_convert_4_pack (GdkRgbInfo *image_info, GdkImage *image, } *obptr++ = (pix0 << 4) | pix1; } + if (x < width) + { + r = *bp2++; + g = *bp2++; + b = *bp2++; + dith = (dmp[(x_align + x) & (DM_WIDTH - 1)] << 2) | 3; + ix = (((r + dith) & 0x100) >> 2) | + (((g + 258 - dith) & 0x100) >> 5) | + (((b + dith) & 0x100) >> 8); + pix0 = (colorcube_d[ix]); + *obptr = (pix0 << 4) | (*obptr & 0x0F); + } bptr += rowstride; obuf += bpl; } From 73183e1a30cfba4f065ab220b44183fa94e89455 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 5 Aug 2026 12:12:13 +0200 Subject: [PATCH 25/45] gdk: add high-depth RGB image drawing Add RGB48 and RGBX64 drawing APIs with direct conversion for common 15-, 16-, 24-, 30- and 32-bit TrueColor visuals. Preserve the existing GdkRGB path as a fallback for unsupported visuals and maximum dithering. --- docs/reference/gdk/gdk2-sections.txt | 4 + gdk/Makefile.am | 1 + gdk/gdk.symbols | 9 + gdk/gdkrgb.h | 42 ++ gdk/gdkrgb16.c | 742 +++++++++++++++++++++++++++ gdk/makefile.msc | 1 + 6 files changed, 799 insertions(+) create mode 100644 gdk/gdkrgb16.c diff --git a/docs/reference/gdk/gdk2-sections.txt b/docs/reference/gdk/gdk2-sections.txt index c3d6040b7..4987c31ee 100644 --- a/docs/reference/gdk/gdk2-sections.txt +++ b/docs/reference/gdk/gdk2-sections.txt @@ -335,6 +335,10 @@ gdk_draw_indexed_image gdk_draw_gray_image gdk_draw_rgb_32_image gdk_draw_rgb_32_image_dithalign +gdk_draw_rgb_48_image +gdk_draw_rgb_48_image_dithalign +gdk_draw_rgbx_64_image +gdk_draw_rgbx_64_image_dithalign GdkRgbDither diff --git a/gdk/Makefile.am b/gdk/Makefile.am index 1b186afa1..d83d40db8 100644 --- a/gdk/Makefile.am +++ b/gdk/Makefile.am @@ -145,6 +145,7 @@ gdk_c_sources = \ gdkrectangle.c \ gdkregion-generic.c \ gdkrgb.c \ + gdkrgb16.c \ gdkscreen.c \ gdkselection.c \ gdkvisual.c \ diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols index ee1bc0c98..b48eac70b 100644 --- a/gdk/gdk.symbols +++ b/gdk/gdk.symbols @@ -1118,6 +1118,15 @@ gdk_draw_rgb_image_dithalign #endif #endif +#if IN_HEADER(__GDK_RGB_H__) +#if IN_FILE(__GDK_RGB16_C__) +gdk_draw_rgb_48_image +gdk_draw_rgb_48_image_dithalign +gdk_draw_rgbx_64_image +gdk_draw_rgbx_64_image_dithalign +#endif +#endif + #if IN_HEADER(__GDK_SCREEN_H__) #if IN_FILE(__GDK_SCREEN_C__) gdk_screen_get_type G_GNUC_CONST diff --git a/gdk/gdkrgb.h b/gdk/gdkrgb.h index dd71d403f..44c576fb5 100644 --- a/gdk/gdkrgb.h +++ b/gdk/gdkrgb.h @@ -147,6 +147,48 @@ gboolean gdk_rgb_colormap_ditherable (GdkColormap *cmap); #endif #endif /* GDK_DISABLE_DEPRECATED */ +void gdk_draw_rgb_48_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride); +void gdk_draw_rgb_48_image_dithalign (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride, + gint xdith, + gint ydith); +void gdk_draw_rgbx_64_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride); +void gdk_draw_rgbx_64_image_dithalign + (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride, + gint xdith, + gint ydith); + G_END_DECLS diff --git a/gdk/gdkrgb16.c b/gdk/gdkrgb16.c new file mode 100644 index 000000000..2564ef0d8 --- /dev/null +++ b/gdk/gdkrgb16.c @@ -0,0 +1,742 @@ +/* GTK - The GIMP Toolkit + * High-precision GdkRGB image conversion + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + */ + +#include "config.h" + +#include + +#include "gdkinternals.h" +#include "gdkrgb.h" +#include "gdkscreen.h" +#include "gdkalias.h" + +typedef void (*GdkRgb16ConvFunc) (GdkImage *image, + gint x0, + gint y0, + gint width, + gint height, + const guint16 *buf, + gint rowstride); + +#define GDK_RGB16_STORE_RGB(p, r, g, b, a) G_STMT_START { \ + (p)[0] = (r) >> 8; \ + (p)[1] = (g) >> 8; \ + (p)[2] = (b) >> 8; \ + (p) += 3; \ +} G_STMT_END + +#define GDK_RGB16_STORE_BGR(p, r, g, b, a) G_STMT_START { \ + (p)[0] = (b) >> 8; \ + (p)[1] = (g) >> 8; \ + (p)[2] = (r) >> 8; \ + (p) += 3; \ +} G_STMT_END + +#define GDK_RGB16_STORE_BGRA(p, r, g, b, a) G_STMT_START { \ + (p)[0] = (b) >> 8; \ + (p)[1] = (g) >> 8; \ + (p)[2] = (r) >> 8; \ + (p)[3] = 0xff; \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_ARGB(p, r, g, b, a) G_STMT_START { \ + (p)[0] = 0xff; \ + (p)[1] = (r) >> 8; \ + (p)[2] = (g) >> 8; \ + (p)[3] = (b) >> 8; \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_RGB0_NATIVE(p, r, g, b, a) G_STMT_START { \ + guint32 pixel__ = (((b) >> 8) << 16) | \ + (((g) >> 8) << 8) | \ + ((r) >> 8); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_565(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = ((r) & 0xf800) | \ + (((g) & 0xfc00) >> 5) | \ + ((b) >> 11); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_565_BR(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = ((r) & 0xf800) | \ + (((g) & 0xfc00) >> 5) | \ + ((b) >> 11); \ + pixel__ = (pixel__ >> 8) | (pixel__ << 8); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_555(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = (((r) & 0xf800) >> 1) | \ + (((g) & 0xf800) >> 6) | \ + ((b) >> 11); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_555_BR(p, r, g, b, a) G_STMT_START { \ + guint16 pixel__ = (((r) & 0xf800) >> 1) | \ + (((g) & 0xf800) >> 6) | \ + ((b) >> 11); \ + pixel__ = (pixel__ >> 8) | (pixel__ << 8); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 2; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_R_LOW_LE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((b) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((r) >> 6); \ + pixel__ = GUINT32_TO_LE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_B_LOW_LE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((r) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((b) >> 6); \ + pixel__ = GUINT32_TO_LE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_R_LOW_BE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((b) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((r) >> 6); \ + pixel__ = GUINT32_TO_BE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define GDK_RGB16_STORE_101010_B_LOW_BE(p, r, g, b, a) \ +G_STMT_START { \ + guint32 pixel__ = (a) | \ + (((r) >> 6) << 20) | \ + (((g) >> 6) << 10) | \ + ((b) >> 6); \ + pixel__ = GUINT32_TO_BE (pixel__); \ + memcpy ((p), &pixel__, sizeof (pixel__)); \ + (p) += 4; \ +} G_STMT_END + +#define DEFINE_GDK_RGB16_CONVERTER(name, words, pixel_bytes, store) \ +static void \ +name (GdkImage *image, \ + gint x0, \ + gint y0, \ + gint width, \ + gint height, \ + const guint16 *buf, \ + gint rowstride) \ +{ \ + const guchar *row; \ + const guint16 *src; \ + guchar *dst_row; \ + guchar *dst; \ + guint32 r, g, b; \ + guint32 alpha_mask; \ + gint x, y; \ + \ + row = (const guchar *) buf; \ + dst_row = ((guchar *) image->mem) + \ + y0 * image->bpl + x0 * (pixel_bytes); \ + alpha_mask = image->depth == 32 ? 0xc0000000U : 0; \ + (void) alpha_mask; \ + \ + for (y = 0; y < height; y++) \ + { \ + src = (const guint16 *) row; \ + dst = dst_row; \ + \ + for (x = 0; x < width; x++) \ + { \ + r = src[0]; \ + g = src[1]; \ + b = src[2]; \ + src += (words); \ + store (dst, r, g, b, alpha_mask); \ + } \ + \ + row += rowstride; \ + dst_row += image->bpl; \ + } \ +} + +#define DEFINE_GDK_RGB16_PAIR(suffix, pixel_bytes, store) \ + DEFINE_GDK_RGB16_CONVERTER (gdk_rgb48_convert_ ## suffix, \ + 3, pixel_bytes, store) \ + DEFINE_GDK_RGB16_CONVERTER (gdk_rgbx64_convert_ ## suffix, \ + 4, pixel_bytes, store) + +DEFINE_GDK_RGB16_PAIR (rgb, 3, GDK_RGB16_STORE_RGB) +DEFINE_GDK_RGB16_PAIR (bgr, 3, GDK_RGB16_STORE_BGR) +DEFINE_GDK_RGB16_PAIR (bgra, 4, GDK_RGB16_STORE_BGRA) +DEFINE_GDK_RGB16_PAIR (argb, 4, GDK_RGB16_STORE_ARGB) +DEFINE_GDK_RGB16_PAIR (rgb0_native, 4, GDK_RGB16_STORE_RGB0_NATIVE) +DEFINE_GDK_RGB16_PAIR (565, 2, GDK_RGB16_STORE_565) +DEFINE_GDK_RGB16_PAIR (565_br, 2, GDK_RGB16_STORE_565_BR) +DEFINE_GDK_RGB16_PAIR (555, 2, GDK_RGB16_STORE_555) +DEFINE_GDK_RGB16_PAIR (555_br, 2, GDK_RGB16_STORE_555_BR) +DEFINE_GDK_RGB16_PAIR (101010_r_low_le, 4, + GDK_RGB16_STORE_101010_R_LOW_LE) +DEFINE_GDK_RGB16_PAIR (101010_b_low_le, 4, + GDK_RGB16_STORE_101010_B_LOW_LE) +DEFINE_GDK_RGB16_PAIR (101010_r_low_be, 4, + GDK_RGB16_STORE_101010_R_LOW_BE) +DEFINE_GDK_RGB16_PAIR (101010_b_low_be, 4, + GDK_RGB16_STORE_101010_B_LOW_BE) + +#undef DEFINE_GDK_RGB16_PAIR +#undef DEFINE_GDK_RGB16_CONVERTER + +#undef GDK_RGB16_STORE_RGB +#undef GDK_RGB16_STORE_BGR +#undef GDK_RGB16_STORE_BGRA +#undef GDK_RGB16_STORE_ARGB +#undef GDK_RGB16_STORE_RGB0_NATIVE +#undef GDK_RGB16_STORE_565 +#undef GDK_RGB16_STORE_565_BR +#undef GDK_RGB16_STORE_555 +#undef GDK_RGB16_STORE_555_BR +#undef GDK_RGB16_STORE_101010_R_LOW_LE +#undef GDK_RGB16_STORE_101010_B_LOW_LE +#undef GDK_RGB16_STORE_101010_R_LOW_BE +#undef GDK_RGB16_STORE_101010_B_LOW_BE + +static GdkRgb16ConvFunc +gdk_rgb16_select_converter (GdkDrawable *drawable, + gboolean is_rgbx, + GdkRgbDither dith) +{ + GdkVisual *visual; + GdkByteOrder byte_order; + GdkScreen *screen; + guint32 red_mask, green_mask, blue_mask; + gint depth; + gint bits_per_pixel; + gboolean byte_reversed; + gboolean mask_rgb; + gboolean mask_bgr; + +#define SELECT_PAIR(suffix) \ + return is_rgbx ? gdk_rgbx64_convert_ ## suffix \ + : gdk_rgb48_convert_ ## suffix + + /* + * Preserve the existing GdkRGB dither path for maximum dithering + * and for unknown future values. + */ + if (dith != GDK_RGB_DITHER_NONE && + dith != GDK_RGB_DITHER_NORMAL) + return NULL; + + visual = gdk_drawable_get_visual (drawable); + if (visual == NULL) + return NULL; + + if (gdk_visual_get_visual_type (visual) != GDK_VISUAL_TRUE_COLOR) + return NULL; + + screen = gdk_visual_get_screen (visual); + depth = gdk_visual_get_depth (visual); + byte_order = gdk_visual_get_byte_order (visual); + + gdk_visual_get_red_pixel_details (visual, &red_mask, NULL, NULL); + gdk_visual_get_green_pixel_details (visual, &green_mask, NULL, NULL); + gdk_visual_get_blue_pixel_details (visual, &blue_mask, NULL, NULL); + + bits_per_pixel = _gdk_windowing_get_bits_for_depth + (gdk_screen_get_display (screen), depth); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + byte_reversed = byte_order == GDK_LSB_FIRST; +#else + byte_reversed = byte_order == GDK_MSB_FIRST; +#endif + + mask_rgb = red_mask == 0xff0000 && + green_mask == 0x00ff00 && + blue_mask == 0x0000ff; + + mask_bgr = red_mask == 0x0000ff && + green_mask == 0x00ff00 && + blue_mask == 0xff0000; + + if (bits_per_pixel == 16 && + depth == 16 && + red_mask == 0xf800 && + green_mask == 0x07e0 && + blue_mask == 0x001f) + { + if (byte_reversed) + SELECT_PAIR (565_br); + + SELECT_PAIR (565); + } + + if (bits_per_pixel == 16 && + depth == 15 && + red_mask == 0x7c00 && + green_mask == 0x03e0 && + blue_mask == 0x001f) + { + if (byte_reversed) + SELECT_PAIR (555_br); + + SELECT_PAIR (555); + } + + if (bits_per_pixel == 24 && + depth == 24 && + ((mask_rgb && byte_order == GDK_LSB_FIRST) || + (mask_bgr && byte_order == GDK_MSB_FIRST))) + SELECT_PAIR (bgr); + + if (bits_per_pixel == 24 && + depth == 24 && + ((mask_rgb && byte_order == GDK_MSB_FIRST) || + (mask_bgr && byte_order == GDK_LSB_FIRST))) + SELECT_PAIR (rgb); + + if (bits_per_pixel == 32 && + (depth == 24 || depth == 32) && + mask_rgb && + byte_order == GDK_MSB_FIRST) + SELECT_PAIR (argb); + + if (bits_per_pixel == 32 && + (depth == 24 || depth == 32) && + mask_rgb && + byte_order == GDK_LSB_FIRST) + SELECT_PAIR (bgra); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + if (bits_per_pixel == 32 && + depth == 24 && + mask_bgr && + byte_order == GDK_MSB_FIRST) + SELECT_PAIR (rgb0_native); +#else + if (bits_per_pixel == 32 && + depth == 24 && + mask_bgr && + byte_order == GDK_LSB_FIRST) + SELECT_PAIR (rgb0_native); +#endif + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_LSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + SELECT_PAIR (101010_r_low_le); + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_LSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + SELECT_PAIR (101010_b_low_le); + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_MSB_FIRST && + red_mask == 0x000003ff && + green_mask == 0x000ffc00 && + blue_mask == 0x3ff00000) + SELECT_PAIR (101010_r_low_be); + + if (bits_per_pixel == 32 && + (depth == 30 || depth == 32) && + byte_order == GDK_MSB_FIRST && + red_mask == 0x3ff00000 && + green_mask == 0x000ffc00 && + blue_mask == 0x000003ff) + SELECT_PAIR (101010_b_low_be); + +#undef SELECT_PAIR + + return NULL; +} + +static void +gdk_rgb16_draw_direct (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + const guint16 *buf, + gint pixel_words, + gint rowstride, + GdkRgb16ConvFunc converter) +{ + GdkScreen *screen; + GdkImage *image; + const guint16 *tile; + gint depth; + gint x0, y0; + gint xs0, ys0; + gint width1, height1; + + screen = gdk_drawable_get_screen (drawable); + depth = gdk_drawable_get_depth (drawable); + + for (y0 = 0; y0 < height; y0 += GDK_SCRATCH_IMAGE_HEIGHT) + { + height1 = MIN (height - y0, GDK_SCRATCH_IMAGE_HEIGHT); + + for (x0 = 0; x0 < width; x0 += GDK_SCRATCH_IMAGE_WIDTH) + { + width1 = MIN (width - x0, GDK_SCRATCH_IMAGE_WIDTH); + + tile = (const guint16 *) + ((const guchar *) buf + + y0 * rowstride + + x0 * pixel_words * sizeof (guint16)); + + image = _gdk_image_get_scratch (screen, + width1, + height1, + depth, + &xs0, + &ys0); + + converter (image, + xs0, + ys0, + width1, + height1, + tile, + rowstride); + + gdk_draw_image (drawable, + gc, + image, + xs0, + ys0, + x + x0, + y + y0, + width1, + height1); + } + } +} + +static void +gdk_rgb16_draw_fallback (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *buf, + gint pixel_words, + gint rowstride, + gint xdith, + gint ydith) +{ + const guchar *input_row; + const guint16 *input; + guchar *stage; + guchar *output; + gint stage_rowstride; + gint x0, y0; + gint x1, y1; + gint width1, height1; + + stage = g_malloc (MIN (width, GDK_SCRATCH_IMAGE_WIDTH) * + MIN (height, GDK_SCRATCH_IMAGE_HEIGHT) * 3); + + for (y0 = 0; y0 < height; y0 += GDK_SCRATCH_IMAGE_HEIGHT) + { + height1 = MIN (height - y0, GDK_SCRATCH_IMAGE_HEIGHT); + + for (x0 = 0; x0 < width; x0 += GDK_SCRATCH_IMAGE_WIDTH) + { + width1 = MIN (width - x0, GDK_SCRATCH_IMAGE_WIDTH); + stage_rowstride = width1 * 3; + + input_row = (const guchar *) buf + + y0 * rowstride + + x0 * pixel_words * sizeof (guint16); + output = stage; + + for (y1 = 0; y1 < height1; y1++) + { + input = (const guint16 *) input_row; + + for (x1 = 0; x1 < width1; x1++) + { + *output++ = input[0] >> 8; + *output++ = input[1] >> 8; + *output++ = input[2] >> 8; + input += pixel_words; + } + + input_row += rowstride; + } + + gdk_draw_rgb_image_dithalign (drawable, + gc, + x + x0, + y + y0, + width1, + height1, + dith, + stage, + stage_rowstride, + xdith, + ydith); + } + } + + g_free (stage); +} + +static void +gdk_draw_rgb16_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *buf, + gint rowstride, + gint xdith, + gint ydith, + gboolean is_rgbx) +{ + GdkRgb16ConvFunc converter; + gint pixel_words; + + g_return_if_fail (GDK_IS_DRAWABLE (drawable)); + g_return_if_fail (GDK_IS_GC (gc)); + g_return_if_fail (buf != NULL); + g_return_if_fail ((((guintptr) buf) & 1) == 0); + g_return_if_fail ((rowstride & 1) == 0); + + if (width <= 0 || height <= 0) + return; + + pixel_words = is_rgbx ? 4 : 3; + converter = gdk_rgb16_select_converter (drawable, is_rgbx, dith); + + if (converter != NULL) + gdk_rgb16_draw_direct (drawable, + gc, + x, + y, + width, + height, + buf, + pixel_words, + rowstride, + converter); + else + gdk_rgb16_draw_fallback (drawable, + gc, + x, + y, + width, + height, + dith, + buf, + pixel_words, + rowstride, + xdith, + ydith); +} + +/** + * gdk_draw_rgb_48_image: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgb_buf: host-endian RGB data with three 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * + * Draws 48-bit RGB image data. + **/ +void +gdk_draw_rgb_48_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgb_buf, + rowstride, + 0, + 0, + FALSE); +} + +/** + * gdk_draw_rgb_48_image_dithalign: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgb_buf: host-endian RGB data with three 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * @xdith: X dither offset + * @ydith: Y dither offset + * + * Draws 48-bit RGB image data with explicit dither alignment. + **/ +void +gdk_draw_rgb_48_image_dithalign (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgb_buf, + gint rowstride, + gint xdith, + gint ydith) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgb_buf, + rowstride, + xdith, + ydith, + FALSE); +} + +/** + * gdk_draw_rgbx_64_image: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgbx_buf: host-endian RGBX data with four 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * + * Draws 64-bit RGBX image data. The X channel is ignored. + **/ +void +gdk_draw_rgbx_64_image (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgbx_buf, + rowstride, + 0, + 0, + TRUE); +} + +/** + * gdk_draw_rgbx_64_image_dithalign: + * @drawable: a #GdkDrawable + * @gc: a #GdkGC + * @x: destination X coordinate + * @y: destination Y coordinate + * @width: image width + * @height: image height + * @dith: dithering mode + * @rgbx_buf: host-endian RGBX data with four 16-bit channels per pixel + * @rowstride: number of bytes between consecutive rows + * @xdith: X dither offset + * @ydith: Y dither offset + * + * Draws 64-bit RGBX image data with explicit dither alignment. + * The X channel is ignored. + **/ +void +gdk_draw_rgbx_64_image_dithalign (GdkDrawable *drawable, + GdkGC *gc, + gint x, + gint y, + gint width, + gint height, + GdkRgbDither dith, + const guint16 *rgbx_buf, + gint rowstride, + gint xdith, + gint ydith) +{ + gdk_draw_rgb16_image (drawable, + gc, + x, + y, + width, + height, + dith, + rgbx_buf, + rowstride, + xdith, + ydith, + TRUE); +} + +#define __GDK_RGB16_C__ +#include "gdkaliasdef.c" diff --git a/gdk/makefile.msc b/gdk/makefile.msc index e9f816931..6175c3f21 100644 --- a/gdk/makefile.msc +++ b/gdk/makefile.msc @@ -92,6 +92,7 @@ gdk_OBJECTS = \ gdkrectangle.obj \ gdkregion-generic.obj \ gdkrgb.obj \ + gdkrgb16.obj \ gdkscreen.obj \ gdkselection.obj \ gdkvisual.obj \ From 450e7840372255cb9340465f1a9f2086d0667b65 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 5 Aug 2026 14:20:10 +0200 Subject: [PATCH 26/45] gtk: replace deprecated IM module parser helpers Use local compile-time selected parser helpers with a legacy Pango fallback. Avoid temporary string decompression allocations and use unlocked stdio where available. --- configure.ac | 2 +- gtk/gtkimmodule.c | 246 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 211 insertions(+), 37 deletions(-) diff --git a/configure.ac b/configure.ac index 29297423f..101007915 100644 --- a/configure.ac +++ b/configure.ac @@ -469,7 +469,7 @@ if test "x$enable_rebuilds" = "xyes" && \ fi AC_SUBST(REBUILD) -AC_CHECK_FUNCS(lstat mkstemp flockfile getc_unlocked) +AC_CHECK_FUNCS(lstat mkstemp flockfile getc_unlocked _getc_nolock) AC_CHECK_FUNCS(localtime_r) # _NL_TIME_FIRST_WEEKDAY is an enum and not a define diff --git a/gtk/gtkimmodule.c b/gtk/gtkimmodule.c index a3a77c474..07c38f0b4 100644 --- a/gtk/gtkimmodule.c +++ b/gtk/gtkimmodule.c @@ -309,11 +309,186 @@ add_builtin_module (const gchar *module_name, return module; } +/* + * Keep IM module parsing independent of Pango's deprecated helpers. + */ + +/* + * Use unlocked input for the privately owned module-cache stream. + */ +#if defined(HAVE_GETC_UNLOCKED) +#define GTK_IM_MODULE_GETC(stream) getc_unlocked (stream) +#elif defined(HAVE__GETC_NOLOCK) +#define GTK_IM_MODULE_GETC(stream) _getc_nolock (stream) +#else +#define GTK_IM_MODULE_GETC(stream) getc (stream) +#endif + +static gchar * +gtk_im_module_scan_string (const gchar **pos) +{ + const gchar *p; + const gchar *q; + gchar *result; + gchar *out; + gsize len; + + p = *pos; + + while (g_ascii_isspace (*p)) + p++; + + if (*p != '"') + return NULL; + + p++; + q = p; + len = 0; + + while (*q && *q != '"') + { + if (*q == '\\') + { + q++; + + if (!*q) + return NULL; + } + + q++; + len++; + } + + if (*q != '"') + return NULL; + + result = g_malloc (len + 1); + out = result; + + while (p < q) + { + gchar c; + + c = *p++; + + if (c == '\\') + { + c = *p++; + + switch (c) + { + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + default: break; + } + } + + *out++ = c; + } + + *out = '\0'; + *pos = q + 1; + + return result; +} + +static gboolean +gtk_im_module_skip_space (const gchar **pos) +{ + while (g_ascii_isspace (**pos)) + (*pos)++; + + return **pos != '\0'; +} + +static gint +gtk_im_module_read_line (FILE *stream, + GString *str) +{ + gboolean escaped; + gboolean comment; + gint n_read; + gint lines; + gint c; + gint next_c; + + escaped = FALSE; + comment = FALSE; + n_read = 0; + lines = 1; + + g_string_truncate (str, 0); + + while (TRUE) + { + c = GTK_IM_MODULE_GETC (stream); + + if (c == EOF) + { + if (escaped) + g_string_append_c (str, '\\'); + + break; + } + + n_read++; + + if (c == '\r' || c == '\n') + { + next_c = GTK_IM_MODULE_GETC (stream); + + if (next_c != EOF && + !((c == '\r' && next_c == '\n') || + (c == '\n' && next_c == '\r'))) + ungetc (next_c, stream); + + if (escaped) + { + escaped = FALSE; + lines++; + continue; + } + + break; + } + + if (escaped) + { + escaped = FALSE; + + if (c == '#') + g_string_append_c (str, '#'); + else + { + g_string_append_c (str, '\\'); + g_string_append_c (str, c); + } + } + else if (!comment) + { + if (c == '#') + comment = TRUE; + else if (c == '\\') + escaped = TRUE; + else + g_string_append_c (str, c); + } + } + + + return n_read > 0 ? lines : 0; +} + +#undef GTK_IM_MODULE_GETC + static void gtk_im_module_initialize (void) { GString *line_buf = g_string_new (NULL); - GString *tmp_buf = g_string_new (NULL); gchar *filename = gtk_rc_get_im_module_file(); FILE *file; gboolean have_error = FALSE; @@ -387,18 +562,17 @@ gtk_im_module_initialize (void) * we allow no file at all. */ g_string_free (line_buf, TRUE); - g_string_free (tmp_buf, TRUE); g_free (filename); return; } - while (!have_error && pango_read_line (file, line_buf)) + while (!have_error && gtk_im_module_read_line (file, line_buf)) { const char *p; p = line_buf->str; - if (!pango_skip_space (&p)) + if (!gtk_im_module_skip_space (&p)) { /* Blank line marking the end of a module */ @@ -413,61 +587,62 @@ gtk_im_module_initialize (void) } if (!module) - { - /* Read a module location - */ - module = g_object_new (GTK_TYPE_IM_MODULE, NULL); + { + /* Read a module location + */ + module = g_object_new (GTK_TYPE_IM_MODULE, NULL); + module->path = gtk_im_module_scan_string (&p); - if (!pango_scan_string (&p, tmp_buf) || - pango_skip_space (&p)) - { - g_warning ("Error parsing context info in '%s'\n %s", - filename, line_buf->str); - have_error = TRUE; - } + if (!module->path || gtk_im_module_skip_space (&p)) + { + g_warning ("Error parsing context info in '%s'\n %s", + filename, line_buf->str); + have_error = TRUE; + continue; + } - module->path = g_strdup (tmp_buf->str); #ifdef G_OS_WIN32 - correct_libdir_prefix (&module->path); + correct_libdir_prefix (&module->path); #endif - g_type_module_set_name (G_TYPE_MODULE (module), module->path); - } + g_type_module_set_name (G_TYPE_MODULE (module), module->path); + } else { GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1); /* Read information about a context type */ - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->context_id = g_strdup (tmp_buf->str); + info->context_id = gtk_im_module_scan_string (&p); + if (!info->context_id) + goto context_error; - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->context_name = g_strdup (tmp_buf->str); + info->context_name = gtk_im_module_scan_string (&p); + if (!info->context_name) + goto context_error; - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->domain = g_strdup (tmp_buf->str); + info->domain = gtk_im_module_scan_string (&p); + if (!info->domain) + goto context_error; - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->domain_dirname = g_strdup (tmp_buf->str); + info->domain_dirname = gtk_im_module_scan_string (&p); + if (!info->domain_dirname) + goto context_error; #ifdef G_OS_WIN32 correct_localedir_prefix ((char **) &info->domain_dirname); #endif - if (!pango_scan_string (&p, tmp_buf)) - goto context_error; - info->default_locales = g_strdup (tmp_buf->str); + info->default_locales = gtk_im_module_scan_string (&p); + if (!info->default_locales) + goto context_error; - if (pango_skip_space (&p)) + if (gtk_im_module_skip_space (&p)) goto context_error; infos = g_slist_prepend (infos, info); continue; context_error: + free_info (info); g_warning ("Error parsing context info in '%s'\n %s", filename, line_buf->str); have_error = TRUE; @@ -491,7 +666,6 @@ gtk_im_module_initialize (void) fclose (file); g_string_free (line_buf, TRUE); - g_string_free (tmp_buf, TRUE); g_free (filename); } From 672b195592b5c7ea163a80ae10d4977b83930ce3 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 5 Aug 2026 14:20:10 +0200 Subject: [PATCH 27/45] gtk: reuse cached module path list Avoid computing and allocating the module path list a second time. --- gtk/gtkmodules.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk/gtkmodules.c b/gtk/gtkmodules.c index da5568ffa..a325b3e56 100644 --- a/gtk/gtkmodules.c +++ b/gtk/gtkmodules.c @@ -192,7 +192,7 @@ _gtk_get_module_path (const gchar *type) result = g_new (gchar *, count * 5 + 1); count = 0; - for (path = get_module_path (); *path; path++) + for (path = paths; *path; path++) { gint use_version, use_host; From 0911aa2f2f67a1dd850bafdfa2d8024561d4fced Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 5 Aug 2026 14:20:10 +0200 Subject: [PATCH 28/45] gtk: remove unused IM module query function pointers Keep symbol validation while avoiding unused typed variables and assignments. --- gtk/queryimmodules.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gtk/queryimmodules.c b/gtk/queryimmodules.c index d20637e10..e118b9c19 100644 --- a/gtk/queryimmodules.c +++ b/gtk/queryimmodules.c @@ -159,9 +159,6 @@ query_module (const char *dir, const char *name, GString *contents) { void (*list) (const GtkIMContextInfo ***contexts, guint *n_contexts); - G_GNUC_UNUSED void (*init) (GTypeModule *type_module); - G_GNUC_UNUSED void (*exit) (void); - G_GNUC_UNUSED GtkIMContext *(*create) (const gchar *context_id); gpointer list_ptr; gpointer init_ptr; @@ -196,9 +193,6 @@ query_module (const char *dir, const char *name, GString *contents) int i; list = list_ptr; - init = init_ptr; - exit = exit_ptr; - create = create_ptr; print_escaped (contents, path); g_string_append_c (contents, '\n'); From d0c434639651b33e5a8e4b004b7871a0ad4ef6e6 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 5 Aug 2026 22:59:33 +0200 Subject: [PATCH 29/45] cups: use modern HTTP connection helpers when available Detect httpConnect2 and httpReconnect2 at configure time. Preserve blocking, encryption, timeout and reconnect semantics while retaining the legacy CUPS APIs as compile-time fallbacks. --- configure.ac | 1 + modules/printbackends/cups/gtkcupsutils.c | 44 ++++++++++++++---- .../printbackends/cups/gtkprintbackendcups.c | 46 +++++++++++++++++-- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 101007915..c344f7d8a 100644 --- a/configure.ac +++ b/configure.ac @@ -1594,6 +1594,7 @@ else gtk_save_libs="$LIBS" LIBS="$CUPS_LIBS" AC_CHECK_FUNCS(httpGetAuthString) + AC_CHECK_FUNCS(httpConnect2 httpReconnect2) LIBS="$gtk_save_libs" fi fi diff --git a/modules/printbackends/cups/gtkcupsutils.c b/modules/printbackends/cups/gtkcupsutils.c index fdfe21e91..f96b7a5b5 100644 --- a/modules/printbackends/cups/gtkcupsutils.c +++ b/modules/printbackends/cups/gtkcupsutils.c @@ -34,6 +34,11 @@ typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request); +static http_t *gtk_cups_http_connect (const char *host, + int port, + http_encryption_t encryption); +static int gtk_cups_http_reconnect (http_t *http); + static void _connect (GtkCupsRequest *request); static void _post_send (GtkCupsRequest *request); static void _post_write_request (GtkCupsRequest *request); @@ -171,7 +176,7 @@ gtk_cups_request_new_with_username (http_t *connection, else { request->http = NULL; - request->http = httpConnectEncrypt (request->server, + request->http = gtk_cups_http_connect (request->server, ippPort (), cupsEncryption ()); @@ -681,6 +686,29 @@ gtk_cups_request_set_ipp_version (GtkCupsRequest *request, ippSetVersion (request->ipp_request, major, minor); } + +static http_t * +gtk_cups_http_connect (const char *host, + int port, + http_encryption_t encryption) +{ +#ifdef HAVE_HTTPCONNECT2 + return httpConnect2 (host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL); +#else + return httpConnectEncrypt (host, port, encryption); +#endif +} + +static int +gtk_cups_http_reconnect (http_t *http) +{ +#ifdef HAVE_HTTPRECONNECT2 + return httpReconnect2 (http, 30000, NULL); +#else + return httpReconnect (http); +#endif +} + static void _connect (GtkCupsRequest *request) { @@ -689,7 +717,7 @@ _connect (GtkCupsRequest *request) if (request->http == NULL) { - request->http = httpConnectEncrypt (request->server, + request->http = gtk_cups_http_connect (request->server, ippPort (), cupsEncryption ()); @@ -744,7 +772,7 @@ _post_send (GtkCupsRequest *request) if (httpPost (request->http, request->resource)) { - if (httpReconnect (request->http)) + if (gtk_cups_http_reconnect (request->http)) { request->state = GTK_CUPS_POST_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; @@ -1046,7 +1074,7 @@ _post_check (GtkCupsRequest *request) } if (auth_result || - httpReconnect (request->http)) + gtk_cups_http_reconnect (request->http)) { /* if the password has been used, reset password_state * so that we ask for a new one next time around @@ -1105,7 +1133,7 @@ _post_check (GtkCupsRequest *request) request->state = GTK_CUPS_POST_CONNECT; /* Reconnect... */ - httpReconnect (request->http); + gtk_cups_http_reconnect (request->http); /* Upgrade with encryption... */ httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); @@ -1232,7 +1260,7 @@ _get_send (GtkCupsRequest *request) if (httpGet (request->http, request->resource)) { - if (httpReconnect (request->http)) + if (gtk_cups_http_reconnect (request->http)) { request->state = GTK_CUPS_GET_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; @@ -1341,7 +1369,7 @@ _get_check (GtkCupsRequest *request) } if (auth_result || - httpReconnect (request->http)) + gtk_cups_http_reconnect (request->http)) { /* if the password has been used, reset password_state * so that we ask for a new one next time around @@ -1373,7 +1401,7 @@ _get_check (GtkCupsRequest *request) request->state = GTK_CUPS_GET_CONNECT; /* Reconnect... */ - httpReconnect (request->http); + gtk_cups_http_reconnect (request->http); /* Upgrade with encryption... */ httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c index e17d97d21..d07d1bc7a 100644 --- a/modules/printbackends/cups/gtkprintbackendcups.c +++ b/modules/printbackends/cups/gtkprintbackendcups.c @@ -489,6 +489,44 @@ cups_free_print_stream_data (CupsPrintStreamData *data) g_free (data); } + +static http_t * +gtk_cups_backend_http_connect (const char *host, + int port) +{ +#ifdef HAVE_HTTPCONNECT2 + return httpConnect2 (host, + port, + NULL, + AF_UNSPEC, + HTTP_ENCRYPT_IF_REQUESTED, + 1, + 30000, + NULL); +#else + return httpConnect (host, port); +#endif +} + +static http_t * +gtk_cups_backend_http_connect_encrypt (const char *host, + int port, + http_encryption_t encryption) +{ +#ifdef HAVE_HTTPCONNECT2 + return httpConnect2 (host, + port, + NULL, + AF_UNSPEC, + encryption, + 1, + 30000, + NULL); +#else + return httpConnectEncrypt (host, port, encryption); +#endif +} + static void cups_print_cb (GtkPrintBackendCups *print_backend, GtkCupsResult *result, @@ -636,7 +674,9 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend, { http_t *http; - http = httpConnect (cups_printer->hostname, cups_printer->port); + http = gtk_cups_backend_http_connect ( + cups_printer->hostname, + cups_printer->port); if (http) { request = gtk_cups_request_new_with_username (http, @@ -2555,7 +2595,7 @@ cups_request_avahi_printer_info (const gchar *printer_uri, AvahiService *service; http_t *http; - http = httpConnect (host, port); + http = gtk_cups_backend_http_connect (host, port); if (http) { service = (AvahiService *) g_new0 (AvahiService, 1); @@ -3438,7 +3478,7 @@ cups_request_ppd (GtkPrinter *printer) } } - http = httpConnectEncrypt (cups_printer->hostname, + http = gtk_cups_backend_http_connect_encrypt (cups_printer->hostname, cups_printer->port, cupsEncryption ()); From a116a5d13b6f3a838af6d3c37287b649d4d303ef Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Thu, 6 Aug 2026 13:20:18 +0200 Subject: [PATCH 30/45] gail: implement modern AtkValue interface Add compile-time support for the modern AtkValue API available since ATK 2.12 while retaining compatibility with older ATK versions. Implement direct modern value, range, increment and setter methods for adjustments, paned widgets, progress bars, ranges, scale buttons and spin buttons. Keep the implementation private, avoid per-instance memory overhead and preserve the existing exported ABI. --- configure.ac | 5 ++ modules/other/gail/Makefile.am | 1 + modules/other/gail/gailadjustment.c | 82 ++++++++++++---------------- modules/other/gail/gailadjustment.h | 16 ++++++ modules/other/gail/gailpaned.c | 35 ++++++++++++ modules/other/gail/gailprogressbar.c | 18 +++++- modules/other/gail/gailrange.c | 39 +++++++++++-- modules/other/gail/gailscalebutton.c | 27 ++++++++- modules/other/gail/gailspinbutton.c | 31 +++++++++-- modules/other/gail/gailvalue.h | 56 +++++++++++++++++++ 10 files changed, 246 insertions(+), 64 deletions(-) create mode 100644 modules/other/gail/gailvalue.h diff --git a/configure.ac b/configure.ac index c344f7d8a..04536e910 100644 --- a/configure.ac +++ b/configure.ac @@ -399,6 +399,11 @@ PKG_CHECK_MODULES(BASE_DEPENDENCIES, gdk-pixbuf-2.0 >= gdk_pixbuf_required_version dnl fribidi >= fribidi_required_version]) +PKG_CHECK_EXISTS([atk >= 2.12], + [AC_DEFINE([HAVE_ATK_VALUE_2_12], [1], + [Define if ATK provides the 2.12 AtkValue interface])]) + + ## In addition to checking that cairo is present, we also need to ## check that the correct cairo backend is there. E.g. if the GDK ## target is win32 we need the cairo-win32 backend and so on. diff --git a/modules/other/gail/Makefile.am b/modules/other/gail/Makefile.am index 991512dea..1b9a4c6bd 100644 --- a/modules/other/gail/Makefile.am +++ b/modules/other/gail/Makefile.am @@ -72,6 +72,7 @@ libgailincludedir=$(includedir)/gail-1.0/gail gail_private_h_sources = \ gail.h \ + gailvalue.h \ gailadjustment.h \ gailarrow.h \ gailbooleancell.h \ diff --git a/modules/other/gail/gailadjustment.c b/modules/other/gail/gailadjustment.c index f1bb53f2b..021b79357 100644 --- a/modules/other/gail/gailadjustment.c +++ b/modules/other/gail/gailadjustment.c @@ -22,6 +22,7 @@ #include #include #include "gailadjustment.h" +#include "gailvalue.h" static void gail_adjustment_class_init (GailAdjustmentClass *klass); @@ -32,16 +33,25 @@ static void gail_adjustment_real_initialize (AtkObject *obj, static void atk_value_interface_init (AtkValueIface *iface); -static void gail_adjustment_get_current_value (AtkValue *obj, - GValue *value); -static void gail_adjustment_get_maximum_value (AtkValue *obj, - GValue *value); -static void gail_adjustment_get_minimum_value (AtkValue *obj, - GValue *value); -static void gail_adjustment_get_minimum_increment (AtkValue *obj, - GValue *value); -static gboolean gail_adjustment_set_current_value (AtkValue *obj, - const GValue *value); + +#ifdef HAVE_ATK_VALUE_2_12 +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_adjustment, GAIL_ADJUSTMENT (obj)->adjustment, + adjustment->value, adjustment->upper) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_adjustment, GAIL_ADJUSTMENT (obj)->adjustment, + GAIL_VALUE_MINIMUM_INCREMENT (adjustment)) + +static void +gail_adjustment_set_value (AtkValue *obj, gdouble value) +{ + GtkAdjustment *adjustment; + + adjustment = GAIL_ADJUSTMENT (obj)->adjustment; + if (adjustment != NULL) + gtk_adjustment_set_value (adjustment, value); +} +#endif static void gail_adjustment_destroyed (GtkAdjustment *adjustment, GailAdjustment *gail_adjustment); @@ -105,9 +115,15 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_adjustment_get_minimum_value; iface->get_minimum_increment = gail_adjustment_get_minimum_increment; iface->set_current_value = gail_adjustment_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_adjustment_get_value_and_text; + iface->get_range = gail_adjustment_get_range; + iface->get_increment = gail_adjustment_get_increment; + iface->set_value = gail_adjustment_set_value; +#endif } -static void +void gail_adjustment_get_current_value (AtkValue *obj, GValue *value) { @@ -127,7 +143,7 @@ gail_adjustment_get_current_value (AtkValue *obj, g_value_set_double (value,current_value); } -static void +void gail_adjustment_get_maximum_value (AtkValue *obj, GValue *value) { @@ -147,7 +163,7 @@ gail_adjustment_get_maximum_value (AtkValue *obj, g_value_set_double (value, maximum_value); } -static void +void gail_adjustment_get_minimum_value (AtkValue *obj, GValue *value) { @@ -167,48 +183,20 @@ gail_adjustment_get_minimum_value (AtkValue *obj, g_value_set_double (value, minimum_value); } -static void -gail_adjustment_get_minimum_increment (AtkValue *obj, - GValue *value) +void +gail_adjustment_get_minimum_increment (AtkValue *obj, GValue *value) { - GtkAdjustment* adjustment; - gdouble minimum_increment; + GtkAdjustment *adjustment = GAIL_ADJUSTMENT (obj)->adjustment; - adjustment = GAIL_ADJUSTMENT (obj)->adjustment; if (adjustment == NULL) - { - /* State is defunct */ return; - } - if (adjustment->step_increment != 0 && - adjustment->page_increment != 0) - { - if (ABS (adjustment->step_increment) < ABS (adjustment->page_increment)) - minimum_increment = adjustment->step_increment; - else - minimum_increment = adjustment->page_increment; - } - else if (adjustment->step_increment == 0 && - adjustment->page_increment == 0) - { - minimum_increment = 0; - } - else if (adjustment->step_increment == 0) - { - minimum_increment = adjustment->page_increment; - } - else - { - minimum_increment = adjustment->step_increment; - } - - memset (value, 0, sizeof (GValue)); + memset (value, 0, sizeof (GValue)); g_value_init (value, G_TYPE_DOUBLE); - g_value_set_double (value, minimum_increment); + g_value_set_double (value, GAIL_VALUE_MINIMUM_INCREMENT (adjustment)); } -static gboolean +gboolean gail_adjustment_set_current_value (AtkValue *obj, const GValue *value) { diff --git a/modules/other/gail/gailadjustment.h b/modules/other/gail/gailadjustment.h index 790534dc9..0135b3d91 100644 --- a/modules/other/gail/gailadjustment.h +++ b/modules/other/gail/gailadjustment.h @@ -50,6 +50,22 @@ struct _GailAdjustmentClass AtkObject *gail_adjustment_new (GtkAdjustment *adjustment); +G_GNUC_INTERNAL void +gail_adjustment_get_current_value (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL void +gail_adjustment_get_maximum_value (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL void +gail_adjustment_get_minimum_value (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL void +gail_adjustment_get_minimum_increment (AtkValue *obj, + GValue *value); +G_GNUC_INTERNAL gboolean +gail_adjustment_set_current_value (AtkValue *obj, + const GValue *value); + G_END_DECLS #endif /* __GAIL_ADJUSTMENT_H__ */ diff --git a/modules/other/gail/gailpaned.c b/modules/other/gail/gailpaned.c index 5ac2d2340..42ac248da 100644 --- a/modules/other/gail/gailpaned.c +++ b/modules/other/gail/gailpaned.c @@ -22,6 +22,7 @@ #include #include #include "gailpaned.h" +#include "gailvalue.h" static void gail_paned_class_init (GailPanedClass *klass); @@ -44,6 +45,35 @@ static void gail_paned_get_minimum_value (AtkValue *obj, static gboolean gail_paned_set_current_value (AtkValue *obj, const GValue *value); +#ifdef HAVE_ATK_VALUE_2_12 +static void +gail_paned_get_value_and_text (AtkValue *obj, gdouble *value, gchar **text) +{ + GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget; + + if (text != NULL) + *text = NULL; + if (value != NULL) + *value = widget != NULL + ? gtk_paned_get_position (GTK_PANED (widget)) : 0.0; +} + +static AtkRange * +gail_paned_get_range (AtkValue *obj) +{ + GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget; + GtkPaned *paned; + + if (widget == NULL) + return NULL; + paned = GTK_PANED (widget); + return atk_range_new (paned->min_position, paned->max_position, NULL); +} + +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_paned, GTK_PANED, gtk_paned_set_position, (gint) value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailPaned, gail_paned, GAIL_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -112,6 +142,11 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_maximum_value = gail_paned_get_maximum_value; iface->get_minimum_value = gail_paned_get_minimum_value; iface->set_current_value = gail_paned_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_paned_get_value_and_text; + iface->get_range = gail_paned_get_range; + iface->set_value = gail_paned_set_value; +#endif } static void diff --git a/modules/other/gail/gailprogressbar.c b/modules/other/gail/gailprogressbar.c index d70c139be..6a0df280e 100644 --- a/modules/other/gail/gailprogressbar.c +++ b/modules/other/gail/gailprogressbar.c @@ -26,6 +26,7 @@ #include #include "gailprogressbar.h" +#include "gailvalue.h" #include "gailadjustment.h" static void gail_progress_bar_class_init (GailProgressBarClass *klass); @@ -50,6 +51,13 @@ static void gail_progress_bar_get_minimum_value (AtkValue *obj, static void gail_progress_bar_value_changed (GtkAdjustment *adjustment, gpointer data); +#ifdef HAVE_ATK_VALUE_2_12 +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_progress_bar, + GAIL_VALUE_ADJUSTMENT (GAIL_PROGRESS_BAR (obj)->adjustment), + adjustment->value, adjustment->upper) +#endif + G_DEFINE_TYPE_WITH_CODE (GailProgressBar, gail_progress_bar, GAIL_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -108,6 +116,10 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_current_value = gail_progress_bar_get_current_value; iface->get_maximum_value = gail_progress_bar_get_maximum_value; iface->get_minimum_value = gail_progress_bar_get_minimum_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_progress_bar_get_value_and_text; + iface->get_range = gail_progress_bar_get_range; +#endif } static void @@ -125,7 +137,7 @@ gail_progress_bar_get_current_value (AtkValue *obj, */ return; - atk_value_get_current_value (ATK_VALUE (progress_bar->adjustment), value); + gail_adjustment_get_current_value (ATK_VALUE (progress_bar->adjustment), value); } static void @@ -143,7 +155,7 @@ gail_progress_bar_get_maximum_value (AtkValue *obj, */ return; - atk_value_get_maximum_value (ATK_VALUE (progress_bar->adjustment), value); + gail_adjustment_get_maximum_value (ATK_VALUE (progress_bar->adjustment), value); } static void @@ -161,7 +173,7 @@ gail_progress_bar_get_minimum_value (AtkValue *obj, */ return; - atk_value_get_minimum_value (ATK_VALUE (progress_bar->adjustment), value); + gail_adjustment_get_minimum_value (ATK_VALUE (progress_bar->adjustment), value); } static void diff --git a/modules/other/gail/gailrange.c b/modules/other/gail/gailrange.c index 9a91bb333..8170d86d0 100644 --- a/modules/other/gail/gailrange.c +++ b/modules/other/gail/gailrange.c @@ -23,6 +23,7 @@ #include #include #include "gailrange.h" +#include "gailvalue.h" #include "gailadjustment.h" #include "gail-private-macros.h" @@ -70,6 +71,28 @@ static gboolean gail_range_set_description (AtkAction *action, gint i, const gchar *desc); +#ifdef HAVE_ATK_VALUE_2_12 +#define GAIL_RANGE_ADJUSTMENT(obj) \ + (GTK_ACCESSIBLE (obj)->widget != NULL \ + ? GAIL_VALUE_ADJUSTMENT (GAIL_RANGE (obj)->adjustment) : NULL) +#define GAIL_RANGE_UPPER(obj, adjustment) \ + (gtk_range_get_restrict_to_fill_level ( \ + GTK_RANGE (GTK_ACCESSIBLE (obj)->widget)) \ + ? MIN ((adjustment)->upper - (adjustment)->page_size, \ + gtk_range_get_fill_level ( \ + GTK_RANGE (GTK_ACCESSIBLE (obj)->widget))) \ + : (adjustment)->upper - (adjustment)->page_size) + +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_range, GAIL_RANGE_ADJUSTMENT (obj), adjustment->value, + GAIL_RANGE_UPPER (obj, adjustment)) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_range, GAIL_RANGE_ADJUSTMENT (obj), + GAIL_VALUE_MINIMUM_INCREMENT (adjustment)) +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_range, GTK_RANGE, gtk_range_set_value, value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailRange, gail_range, GAIL_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init) G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -163,6 +186,12 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_range_get_minimum_value; iface->get_minimum_increment = gail_range_get_minimum_increment; iface->set_current_value = gail_range_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_range_get_value_and_text; + iface->get_range = gail_range_get_range; + iface->get_increment = gail_range_get_increment; + iface->set_value = gail_range_set_value; +#endif } static void @@ -180,7 +209,7 @@ gail_range_get_current_value (AtkValue *obj, */ return; - atk_value_get_current_value (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_current_value (ATK_VALUE (range->adjustment), value); } static void @@ -201,7 +230,7 @@ gail_range_get_maximum_value (AtkValue *obj, */ return; - atk_value_get_maximum_value (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_maximum_value (ATK_VALUE (range->adjustment), value); gtk_range = GTK_RANGE (gtk_accessible_get_widget (GTK_ACCESSIBLE (range))); g_return_if_fail (gtk_range); @@ -231,7 +260,7 @@ gail_range_get_minimum_value (AtkValue *obj, */ return; - atk_value_get_minimum_value (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_minimum_value (ATK_VALUE (range->adjustment), value); } static void @@ -248,7 +277,7 @@ gail_range_get_minimum_increment (AtkValue *obj, GValue *value) */ return; - atk_value_get_minimum_increment (ATK_VALUE (range->adjustment), value); + gail_adjustment_get_minimum_increment (ATK_VALUE (range->adjustment), value); } static gboolean gail_range_set_current_value (AtkValue *obj, @@ -519,5 +548,3 @@ gail_range_set_description (AtkAction *action, else return FALSE; } - - diff --git a/modules/other/gail/gailscalebutton.c b/modules/other/gail/gailscalebutton.c index f04c69046..75915e43d 100644 --- a/modules/other/gail/gailscalebutton.c +++ b/modules/other/gail/gailscalebutton.c @@ -21,6 +21,7 @@ #include #include "gailscalebutton.h" +#include "gailvalue.h" #include "gailadjustment.h" #include "gail-private-macros.h" @@ -65,6 +66,24 @@ static void gail_scale_button_get_minimum_increment (AtkValue *obj, static gboolean gail_scale_button_set_current_value (AtkValue *obj, const GValue *value); +#ifdef HAVE_ATK_VALUE_2_12 +#define GAIL_SCALE_BUTTON_ADJUSTMENT(obj) \ + (GTK_ACCESSIBLE (obj)->widget != NULL \ + ? gtk_scale_button_get_adjustment ( \ + GTK_SCALE_BUTTON (GTK_ACCESSIBLE (obj)->widget)) : NULL) + +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_scale_button, GAIL_SCALE_BUTTON_ADJUSTMENT (obj), + gtk_scale_button_get_value ( + GTK_SCALE_BUTTON (GTK_ACCESSIBLE (obj)->widget)), + adjustment->upper) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_scale_button, GAIL_SCALE_BUTTON_ADJUSTMENT (obj), + adjustment->step_increment) +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_scale_button, GTK_SCALE_BUTTON, gtk_scale_button_set_value, value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailScaleButton, gail_scale_button, GAIL_TYPE_BUTTON, G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init) G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)); @@ -181,6 +200,12 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_scale_button_get_minimum_value; iface->get_minimum_increment = gail_scale_button_get_minimum_increment; iface->set_current_value = gail_scale_button_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_scale_button_get_value_and_text; + iface->get_range = gail_scale_button_get_range; + iface->get_increment = gail_scale_button_get_increment; + iface->set_value = gail_scale_button_set_value; +#endif } static void @@ -295,5 +320,3 @@ gail_scale_button_notify_gtk (GObject *obj, GAIL_WIDGET_CLASS (gail_scale_button_parent_class)->notify_gtk (obj, pspec); } } - - diff --git a/modules/other/gail/gailspinbutton.c b/modules/other/gail/gailspinbutton.c index 709a413c0..36f2ffa32 100644 --- a/modules/other/gail/gailspinbutton.c +++ b/modules/other/gail/gailspinbutton.c @@ -22,6 +22,7 @@ #include #include #include "gailspinbutton.h" +#include "gailvalue.h" #include "gailadjustment.h" #include "gail-private-macros.h" @@ -49,6 +50,19 @@ static gboolean gail_spin_button_set_current_value (AtkValue *obj, static void gail_spin_button_value_changed (GtkAdjustment *adjustment, gpointer data); +#ifdef HAVE_ATK_VALUE_2_12 +GAIL_DEFINE_ADJUSTMENT_VALUE_READERS ( + gail_spin_button, + GAIL_VALUE_ADJUSTMENT (GAIL_SPIN_BUTTON (obj)->adjustment), + adjustment->value, adjustment->upper) +GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT ( + gail_spin_button, + GAIL_VALUE_ADJUSTMENT (GAIL_SPIN_BUTTON (obj)->adjustment), + GAIL_VALUE_MINIMUM_INCREMENT (adjustment)) +GAIL_DEFINE_WIDGET_VALUE_SETTER ( + gail_spin_button, GTK_SPIN_BUTTON, gtk_spin_button_set_value, value) +#endif + G_DEFINE_TYPE_WITH_CODE (GailSpinButton, gail_spin_button, GAIL_TYPE_ENTRY, G_IMPLEMENT_INTERFACE (ATK_TYPE_VALUE, atk_value_interface_init)) @@ -110,6 +124,12 @@ atk_value_interface_init (AtkValueIface *iface) iface->get_minimum_value = gail_spin_button_get_minimum_value; iface->get_minimum_increment = gail_spin_button_get_minimum_increment; iface->set_current_value = gail_spin_button_set_current_value; +#ifdef HAVE_ATK_VALUE_2_12 + iface->get_value_and_text = gail_spin_button_get_value_and_text; + iface->get_range = gail_spin_button_get_range; + iface->get_increment = gail_spin_button_get_increment; + iface->set_value = gail_spin_button_set_value; +#endif } static void @@ -127,7 +147,7 @@ gail_spin_button_get_current_value (AtkValue *obj, */ return; - atk_value_get_current_value (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_current_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -145,7 +165,7 @@ gail_spin_button_get_maximum_value (AtkValue *obj, */ return; - atk_value_get_maximum_value (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_maximum_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -163,7 +183,7 @@ gail_spin_button_get_minimum_value (AtkValue *obj, */ return; - atk_value_get_minimum_value (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_minimum_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -180,7 +200,7 @@ gail_spin_button_get_minimum_increment (AtkValue *obj, GValue *value) */ return; - atk_value_get_minimum_increment (ATK_VALUE (spin_button->adjustment), value); + gail_adjustment_get_minimum_increment (ATK_VALUE (spin_button->adjustment), value); } static gboolean @@ -198,7 +218,7 @@ gail_spin_button_set_current_value (AtkValue *obj, */ return FALSE; - return atk_value_set_current_value (ATK_VALUE (spin_button->adjustment), value); + return gail_adjustment_set_current_value (ATK_VALUE (spin_button->adjustment), value); } static void @@ -264,4 +284,3 @@ gail_spin_button_value_changed (GtkAdjustment *adjustment, g_object_notify (G_OBJECT (spin_button), "accessible-value"); } - diff --git a/modules/other/gail/gailvalue.h b/modules/other/gail/gailvalue.h new file mode 100644 index 000000000..498453424 --- /dev/null +++ b/modules/other/gail/gailvalue.h @@ -0,0 +1,56 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +#ifndef __GAIL_VALUE_H__ +#define __GAIL_VALUE_H__ + +#include + +#define GAIL_VALUE_ADJUSTMENT(accessible) \ + ((accessible) != NULL ? GAIL_ADJUSTMENT (accessible)->adjustment : NULL) +#define GAIL_VALUE_MINIMUM_INCREMENT(a) \ + ((a)->step_increment == 0.0 ? (a)->page_increment : \ + (a)->page_increment == 0.0 ? (a)->step_increment : \ + ABS ((a)->step_increment) < ABS ((a)->page_increment) \ + ? (a)->step_increment : (a)->page_increment) + +#ifdef HAVE_ATK_VALUE_2_12 +#define GAIL_DEFINE_ADJUSTMENT_VALUE_READERS(prefix, a_expr, value_expr, max_expr) \ +static void prefix##_get_value_and_text (AtkValue *obj, gdouble *value, gchar **text) \ +{ \ + GtkAdjustment *adjustment = (a_expr); \ + if (text != NULL) *text = NULL; \ + if (value != NULL) *value = adjustment != NULL ? (value_expr) : 0.0; \ +} \ +static AtkRange *prefix##_get_range (AtkValue *obj) \ +{ \ + GtkAdjustment *adjustment = (a_expr); \ + return adjustment != NULL \ + ? atk_range_new (adjustment->lower, (max_expr), NULL) : NULL; \ +} + +#define GAIL_DEFINE_ADJUSTMENT_VALUE_INCREMENT(prefix, a_expr, increment_expr) \ +static gdouble prefix##_get_increment (AtkValue *obj) \ +{ \ + GtkAdjustment *adjustment = (a_expr); \ + return adjustment != NULL ? (increment_expr) : 0.0; \ +} + +#define GAIL_DEFINE_WIDGET_VALUE_SETTER(prefix, cast, setter, value_expr) \ +static void prefix##_set_value (AtkValue *obj, gdouble value) \ +{ \ + GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget; \ + if (widget != NULL) setter (cast (widget), (value_expr)); \ +} +#endif + +#endif /* __GAIL_VALUE_H__ */ From 782a2948b8e55a1905e4e006a01c2c28a3278e6c Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Thu, 6 Aug 2026 13:57:34 +0200 Subject: [PATCH 31/45] gail: update AtkValue test modules Use a shared private compatibility helper in the GAIL test modules. Exercise the modern AtkValue API when ATK 2.12 or newer is available while retaining the legacy test path for older ATK versions. Initialize temporary GValues explicitly and keep the helper symbols private to the test modules. --- modules/other/gail/tests/Makefile.am | 2 + modules/other/gail/tests/ferret.c | 10 +-- modules/other/gail/tests/testpaned.c | 11 +-- modules/other/gail/tests/testvalue.h | 98 +++++++++++++++++++++++++++ modules/other/gail/tests/testvalues.c | 18 ++--- 5 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 modules/other/gail/tests/testvalue.h diff --git a/modules/other/gail/tests/Makefile.am b/modules/other/gail/tests/Makefile.am index 993249558..0f0e02317 100644 --- a/modules/other/gail/tests/Makefile.am +++ b/modules/other/gail/tests/Makefile.am @@ -12,6 +12,8 @@ module_LTLIBRARIES = \ libferret.la endif +noinst_HEADERS = testvalue.h + noinst_LTLIBRARIES = \ libtestaction.la \ libtestbutton.la \ diff --git a/modules/other/gail/tests/ferret.c b/modules/other/gail/tests/ferret.c index 711852af0..39eaef441 100644 --- a/modules/other/gail/tests/ferret.c +++ b/modules/other/gail/tests/ferret.c @@ -16,6 +16,7 @@ #include #include "testlib.h" +#include "testvalue.h" typedef enum { @@ -1389,7 +1390,8 @@ _print_text_attributes (AtkText *aobject) static gint _print_value (AtkValue *aobject) { - GValue *value_back, val; + GValue val = { 0, }; + GValue *value_back; gint group_num; TabNumber tab_n = VALUE; @@ -1398,11 +1400,11 @@ _print_value (AtkValue *aobject) group_num = _print_groupname(tab_n, VALUE_INTERFACE, "Value Interface"); - atk_value_get_current_value(aobject, value_back); + gail_test_value_get_current_value(aobject, value_back); _print_value_type(group_num, "Value", value_back); - atk_value_get_minimum_value(aobject, value_back); + gail_test_value_get_minimum_value(aobject, value_back); _print_value_type(group_num, "Minimum Value", value_back); - atk_value_get_maximum_value(aobject, value_back); + gail_test_value_get_maximum_value(aobject, value_back); _print_value_type(group_num, "Maximum Value", value_back); return(group_num); } diff --git a/modules/other/gail/tests/testpaned.c b/modules/other/gail/tests/testpaned.c index 300031955..7e1f27f1c 100644 --- a/modules/other/gail/tests/testpaned.c +++ b/modules/other/gail/tests/testpaned.c @@ -2,6 +2,7 @@ #include #include #include +#include "testvalue.h" static gint _test_paned (gpointer data); static void _check_paned (AtkObject *obj); @@ -31,17 +32,17 @@ static void _property_change_handler (AtkObject *obj, value = &val; memset (value, 0, sizeof (GValue)); - atk_value_get_current_value (ATK_VALUE (obj), value); + gail_test_value_get_current_value (ATK_VALUE (obj), value); g_return_if_fail (G_VALUE_HOLDS_INT (value)); position = g_value_get_int (value); g_print ("Position is %d previous position was %d\n", position, last_position); last_position = position; - atk_value_get_minimum_value (ATK_VALUE (obj), value); + gail_test_value_get_minimum_value (ATK_VALUE (obj), value); g_return_if_fail (G_VALUE_HOLDS_INT (value)); position = g_value_get_int (value); g_print ("Minimum Value is %d\n", position); - atk_value_get_maximum_value (ATK_VALUE (obj), value); + gail_test_value_get_maximum_value (ATK_VALUE (obj), value); g_return_if_fail (G_VALUE_HOLDS_INT (value)); position = g_value_get_int (value); g_print ("Maximum Value is %d\n", position); @@ -61,14 +62,14 @@ static gint _test_paned (gpointer data) value = &val; memset (value, 0, sizeof (GValue)); - atk_value_get_current_value (ATK_VALUE (obj), value); + gail_test_value_get_current_value (ATK_VALUE (obj), value); g_return_val_if_fail (G_VALUE_HOLDS_INT (value), FALSE); position = g_value_get_int (value); g_print ("Position is : %d\n", position); last_position = position; position *= 2; g_value_set_int (value, position); - atk_value_set_current_value (ATK_VALUE (obj), value); + gail_test_value_set_current_value (ATK_VALUE (obj), value); times++; } if (times < 4) diff --git a/modules/other/gail/tests/testvalue.h b/modules/other/gail/tests/testvalue.h new file mode 100644 index 000000000..735278318 --- /dev/null +++ b/modules/other/gail/tests/testvalue.h @@ -0,0 +1,98 @@ +#ifndef __GAIL_TEST_VALUE_H__ +#define __GAIL_TEST_VALUE_H__ + +#include "config.h" + +#include + +#ifdef HAVE_ATK_VALUE_2_12 + +static void +gail_test_value_store_number (AtkValue *value, + GValue *result, + gdouble number) +{ + if (G_VALUE_TYPE (result) == 0) + g_value_init (result, + atk_object_get_role (ATK_OBJECT (value)) == + ATK_ROLE_SPLIT_PANE ? G_TYPE_INT : G_TYPE_DOUBLE); + + if (G_VALUE_HOLDS_INT (result)) + g_value_set_int (result, (gint) number); + else + { + g_return_if_fail (G_VALUE_HOLDS_DOUBLE (result)); + g_value_set_double (result, number); + } +} + +static void +gail_test_value_get_current_value (AtkValue *value, + GValue *result) +{ + gdouble number; + + atk_value_get_value_and_text (value, &number, NULL); + gail_test_value_store_number (value, result, number); +} + +static void +gail_test_value_get_limit (AtkValue *value, + GValue *result, + gboolean upper) +{ + AtkRange *range; + gdouble number; + + range = atk_value_get_range (value); + g_return_if_fail (range != NULL); + + if (upper) + number = atk_range_get_upper_limit (range); + else + number = atk_range_get_lower_limit (range); + + atk_range_free (range); + gail_test_value_store_number (value, result, number); +} + +#define gail_test_value_get_minimum_value(value, result) \ + gail_test_value_get_limit ((value), (result), FALSE) + +#define gail_test_value_get_maximum_value(value, result) \ + gail_test_value_get_limit ((value), (result), TRUE) + +#define GAIL_TEST_VALUE_NUMBER(value) \ + (G_VALUE_HOLDS_INT (value) \ + ? (gdouble) g_value_get_int (value) \ + : g_value_get_double (value)) + +#define gail_test_value_set_current_value(value, new_value) \ + (atk_value_set_value ((value), \ + GAIL_TEST_VALUE_NUMBER (new_value)), \ + TRUE) + +#else + +#define GAIL_TEST_VALUE_IFACE(value) \ + ATK_VALUE_GET_IFACE (value) + +#define gail_test_value_get_current_value(value, result) \ + (GAIL_TEST_VALUE_IFACE (value)->get_current_value ( \ + (value), (result))) + +#define gail_test_value_get_minimum_value(value, result) \ + (GAIL_TEST_VALUE_IFACE (value)->get_minimum_value ( \ + (value), (result))) + +#define gail_test_value_get_maximum_value(value, result) \ + (GAIL_TEST_VALUE_IFACE (value)->get_maximum_value ( \ + (value), (result))) + +#define gail_test_value_set_current_value(value, new_value) \ + (GAIL_TEST_VALUE_IFACE (value)->set_current_value ( \ + (value), (new_value))) + +#endif + +#endif /* __GAIL_TEST_VALUE_H__ */ diff --git a/modules/other/gail/tests/testvalues.c b/modules/other/gail/tests/testvalues.c index 34d91a25a..37155855c 100644 --- a/modules/other/gail/tests/testvalues.c +++ b/modules/other/gail/tests/testvalues.c @@ -1,6 +1,7 @@ #include #include #include +#include "testvalue.h" static void _traverse_children (AtkObject *obj); static void _add_handler (AtkObject *obj); @@ -14,8 +15,8 @@ static void _value_change_handler (AtkObject *obj, AtkPropertyValues *values) { const gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj)); - GValue *value_back, val; - + GValue val = { 0, }; + GValue *value_back; value_back = &val; if (!ATK_IS_VALUE (obj)) { @@ -33,17 +34,17 @@ static void _value_change_handler (AtkObject *obj, g_print("Now calling the AtkValue interface functions\n"); - atk_value_get_current_value (ATK_VALUE(obj), value_back); + gail_test_value_get_current_value (ATK_VALUE(obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print ("atk_value_get_current_value returns %f\n", g_value_get_double (value_back) ); - atk_value_get_maximum_value (ATK_VALUE (obj), value_back); + gail_test_value_get_maximum_value (ATK_VALUE (obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print ("atk_value_get_maximum returns %f\n", g_value_get_double (value_back)); - atk_value_get_minimum_value (ATK_VALUE (obj), value_back); + gail_test_value_get_minimum_value (ATK_VALUE (obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print ("atk_value_get_minimum returns %f\n", g_value_get_double (value_back)); @@ -103,7 +104,8 @@ static void _add_handler (AtkObject *obj) static void _set_values (AtkObject *obj) { - GValue *value_back, val; + GValue val = { 0, }; + GValue *value_back; static gint count = 0; gdouble double_value; @@ -125,7 +127,7 @@ static void _set_values (AtkObject *obj) { text = "5.7"; atk_editable_text_set_text_contents(ATK_EDITABLE_TEXT(obj),text); g_print("Set text to %s\n",text); - atk_value_get_current_value(ATK_VALUE(obj), value_back); + gail_test_value_get_current_value(ATK_VALUE(obj), value_back); g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back)); g_print("atk_value_get_current_value returns %f\n", g_value_get_double( value_back)); @@ -134,7 +136,7 @@ static void _set_values (AtkObject *obj) { memset (value_back, 0, sizeof (GValue)); g_value_init (value_back, G_TYPE_DOUBLE); g_value_set_double (value_back, 10.0); - if (atk_value_set_current_value (ATK_VALUE (obj), value_back)) + if (gail_test_value_set_current_value (ATK_VALUE (obj), value_back)) { double_value = g_value_get_double (value_back); g_print("atk_value_set_current_value returns %f\n", From ea73f6812ce9859313d6677b4265f3b3d7ffa12a Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Fri, 7 Aug 2026 07:38:23 +0200 Subject: [PATCH 32/45] gtk: replace deprecated tooltip timestamp ABI field --- gtk/gtktooltips.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gtk/gtktooltips.h b/gtk/gtktooltips.h index eb1e0a0be..fbafd9939 100644 --- a/gtk/gtktooltips.h +++ b/gtk/gtktooltips.h @@ -74,7 +74,11 @@ struct _GtkTooltips guint have_grab : 1; guint use_sticky_delay : 1; gint timer_tag; - GTimeVal last_popdown; + struct + { + glong tv_sec; + glong tv_usec; + } last_popdown; }; struct _GtkTooltipsClass From f14303e16fcfa08c8651e02669864361d82281c2 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 13 May 2026 10:19:49 +0200 Subject: [PATCH 33/45] introspection: pass compiler and GTK DLL path to scanner --- gtk/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gtk/Makefile.am b/gtk/Makefile.am index c3c9a1493..31cc16730 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -2,6 +2,9 @@ include $(top_srcdir)/Makefile.decl -include $(INTROSPECTION_MAKEFILE) INTROSPECTION_GIRS = +if OS_WIN32 +INTROSPECTION_SCANNER_ENV = PATH="$(builddir):$(builddir)/.libs:$${PATH}" +endif INTROSPECTION_SCANNER_ARGS = \ --warn-all \ --add-include-path=../gdk From 0f4142b31a294258c52c6973a8846323b969c040 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Mon, 10 Aug 2026 12:05:31 +0200 Subject: [PATCH 34/45] introspection: add GdkWin32 GIR generation --- gdk/Makefile.am | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/gdk/Makefile.am b/gdk/Makefile.am index d83d40db8..2b68f98a6 100644 --- a/gdk/Makefile.am +++ b/gdk/Makefile.am @@ -214,6 +214,11 @@ Gdk_2_0_gir_CFLAGS = \ -I$(top_builddir) \ -I$(top_builddir)/gdk \ $(INCLUDES) + +if OS_WIN32 +Gdk_2_0_gir_CFLAGS += -I$(srcdir)/win32 -I$(srcdir) +endif + INTROSPECTION_GIRS += Gdk-2.0.gir if USE_X11 @@ -269,6 +274,69 @@ introspection_files += $(filter-out x11/gdkx.h, $(x11_introspection_files)) endif # USE_X11 +if OS_WIN32 +win32_introspection_files = \ + win32/gdkapplaunchcontext-win32.c \ + win32/gdkcolor-win32.c \ + win32/gdkcursor-win32.c \ + win32/gdkdisplay-win32.c \ + win32/gdkdnd-win32.c \ + win32/gdkdrawable-win32.c \ + win32/gdkdrawable-win32.h \ + win32/gdkevents-win32.c \ + win32/gdkfont-win32.c \ + win32/gdkgc-win32.c \ + win32/gdkgeometry-win32.c \ + win32/gdkglobals-win32.c \ + win32/gdkimage-win32.c \ + win32/gdkim-win32.c \ + win32/gdkinput.c \ + win32/gdkinput-win32.c \ + win32/gdkinput-win32.h \ + win32/gdkkeys-win32.c \ + win32/gdkmain-win32.c \ + win32/gdkpixmap-win32.c \ + win32/gdkpixmap-win32.h \ + win32/gdkprivate-win32.h \ + win32/gdkproperty-win32.c \ + win32/gdkscreen-win32.c \ + win32/gdkselection-win32.c \ + win32/gdkspawn-win32.c \ + win32/gdktestutils-win32.c \ + win32/gdkvisual-win32.c \ + win32/gdkwin32.h \ + win32/gdkwin32id.c \ + win32/gdkwin32keys.h \ + win32/gdkwindow-win32.c \ + win32/gdkwindow-win32.h + +GdkWin32-2.0.gir: $(gdktargetlib) Gdk-2.0.gir Makefile +GdkWin32_2_0_gir_SCANNERFLAGS = --identifier-prefix=Gdk --symbol-prefix=gdk --include-uninstalled=Gdk-2.0.gir +GdkWin32_2_0_gir_INCLUDES = Gio-2.0 GdkPixbuf-2.0 Pango-1.0 +GdkWin32_2_0_gir_LIBS = $(gdktargetlib) +GdkWin32_2_0_gir_FILES = $(filter-out \ + win32/gdkdrawable-win32.h \ + win32/gdkinput-win32.h \ + win32/gdkpixmap-win32.h \ + win32/gdkprivate-win32.h \ + win32/gdkwindow-win32.h, \ + $(win32_introspection_files)) +GdkWin32_2_0_gir_EXPORT_PACKAGES = gdk-win32-2.0 +GdkWin32_2_0_gir_CFLAGS = \ + -DGDK_COMPILATION \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -I$(top_builddir)/gdk \ + -I$(srcdir)/win32 \ + -I$(srcdir) \ + $(INCLUDES) \ + -L$(top_builddir)/gdk +INTROSPECTION_GIRS += GdkWin32-2.0.gir + +introspection_files += $(filter-out win32/gdkwin32.h, $(win32_introspection_files)) + +endif # OS_WIN32 + girdir = $(datadir)/gir-1.0 gir_DATA = $(INTROSPECTION_GIRS) From 212df8a74e7457c80732d6bdd0795202256c4645 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Mon, 10 Aug 2026 14:08:34 +0200 Subject: [PATCH 35/45] fontsel: localize common font style names --- gtk/gtkfontbutton.c | 3 +- gtk/gtkfontsel.c | 87 ++++++++++++++++++++++++++++++++++++++++++++- gtk/gtkprivate.h | 7 ++++ 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/gtk/gtkfontbutton.c b/gtk/gtkfontbutton.c index 08aea103e..68df02b8d 100644 --- a/gtk/gtkfontbutton.c +++ b/gtk/gtkfontbutton.c @@ -889,7 +889,8 @@ gtk_font_button_update_font_info (GtkFontButton *font_button) if (style == NULL || !g_ascii_strcasecmp (style, "Regular")) family_style = g_strdup (family); else - family_style = g_strdup_printf ("%s %s", family, style); + family_style = g_strdup_printf ("%s %s", family, + _gtk_font_style_name_translate (style)); gtk_label_set_text (GTK_LABEL (font_button->priv->font_label), family_style); diff --git a/gtk/gtkfontsel.c b/gtk/gtkfontsel.c index 701d22841..28ede4eb4 100644 --- a/gtk/gtkfontsel.c +++ b/gtk/gtkfontsel.c @@ -76,6 +76,91 @@ #define DEFAULT_FONT_NAME "Sans 10" +#ifdef ENABLE_NLS +/* Pango uses these standard English names for common font faces. + * Keep the msgids explicit for gettext, but only translate exact matches: + * font-specific face names must remain untouched. + */ +#define FONT_STYLE_ULTRA_LIGHT N_("Ultra-Light") +#define FONT_STYLE_LIGHT N_("Light") +#define FONT_STYLE_MEDIUM N_("Medium") +#define FONT_STYLE_NORMAL N_("Normal") +#define FONT_STYLE_REGULAR N_("Regular") +#define FONT_STYLE_ITALIC N_("Italic") +#define FONT_STYLE_OBLIQUE N_("Oblique") +#define FONT_STYLE_SEMI_BOLD N_("Semi-Bold") +#define FONT_STYLE_BOLD N_("Bold") +#define FONT_STYLE_ULTRA_BOLD N_("Ultra-Bold") +#define FONT_STYLE_HEAVY N_("Heavy") +#define FONT_STYLE_BOLD_ITALIC N_("Bold Italic") + +const gchar * +_gtk_font_style_name_translate (const gchar *name) +{ + switch (name[0]) + { + case 'B': + if (strcmp (name, FONT_STYLE_BOLD) == 0) + return _(FONT_STYLE_BOLD); + if (strcmp (name, FONT_STYLE_BOLD_ITALIC) == 0) + return _(FONT_STYLE_BOLD_ITALIC); + break; + + case 'H': + if (strcmp (name, FONT_STYLE_HEAVY) == 0) + return _(FONT_STYLE_HEAVY); + break; + + case 'I': + if (strcmp (name, FONT_STYLE_ITALIC) == 0) + return _(FONT_STYLE_ITALIC); + break; + + case 'L': + if (strcmp (name, FONT_STYLE_LIGHT) == 0) + return _(FONT_STYLE_LIGHT); + break; + + case 'M': + if (strcmp (name, FONT_STYLE_MEDIUM) == 0) + return _(FONT_STYLE_MEDIUM); + break; + + case 'N': + if (strcmp (name, FONT_STYLE_NORMAL) == 0) + return _(FONT_STYLE_NORMAL); + break; + + case 'O': + if (strcmp (name, FONT_STYLE_OBLIQUE) == 0) + return _(FONT_STYLE_OBLIQUE); + break; + + case 'R': + if (strcmp (name, FONT_STYLE_REGULAR) == 0) + return _(FONT_STYLE_REGULAR); + break; + + case 'S': + if (strcmp (name, FONT_STYLE_SEMI_BOLD) == 0) + return _(FONT_STYLE_SEMI_BOLD); + break; + + case 'U': + if (strcmp (name, FONT_STYLE_ULTRA_LIGHT) == 0) + return _(FONT_STYLE_ULTRA_LIGHT); + if (strcmp (name, FONT_STYLE_ULTRA_BOLD) == 0) + return _(FONT_STYLE_ULTRA_BOLD); + break; + + default: + break; + } + + return name; +} +#endif /* ENABLE_NLS */ + /* This is the initial and maximum height of the preview entry (it expands when large font sizes are selected). Initial height is also the minimum. */ #define INITIAL_PREVIEW_HEIGHT 44 @@ -869,7 +954,7 @@ gtk_font_selection_show_available_styles (GtkFontSelection *fontsel) gtk_list_store_append (model, &iter); gtk_list_store_set (model, &iter, FACE_COLUMN, faces[i], - FACE_NAME_COLUMN, str, + FACE_NAME_COLUMN, _gtk_font_style_name_translate (str), -1); if (i == 0) diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h index b2c2df999..cc1b50dfa 100644 --- a/gtk/gtkprivate.h +++ b/gtk/gtkprivate.h @@ -134,6 +134,13 @@ const gchar *_gtk_get_data_prefix (); #endif /* G_OS_WIN32 */ +#ifdef ENABLE_NLS +G_GNUC_INTERNAL +const gchar *_gtk_font_style_name_translate (const gchar *name); +#else +#define _gtk_font_style_name_translate(name) (name) +#endif + gboolean _gtk_fnmatch (const char *pattern, const char *string, gboolean no_leading_period); From efcc412ca2d0c59ffa3de96f3eb8bf833487c68e Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 11 Aug 2026 09:53:27 +0200 Subject: [PATCH 36/45] filechooser: reduce view mode startup overhead Replace the two-item GtkComboBoxText with a lightweight toggle and create icon-view zoom controls only when icon view is first used. Keep the existing icon-view zoom, cache invalidation and settings behavior while avoiding their startup cost for the default list view. Keep the zoom event boxes local instead of storing unused widget pointers. --- gtk/gtkfilechooserdefault.c | 116 ++++++++++++++++++++---------------- gtk/gtkfilechooserprivate.h | 4 +- 2 files changed, 64 insertions(+), 56 deletions(-) diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index 2ce2d6c8f..fabf5913e 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -410,7 +410,8 @@ static void load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_s static void browse_files_center_selected_row (GtkFileChooserDefault *impl); static void view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode); -static void view_mode_combo_box_changed_cb (GtkComboBox *combo, +static void icon_view_scale_create (GtkFileChooserDefault *impl); +static void view_mode_toggle_button_toggled_cb (GtkToggleButton *toggle, GtkFileChooserDefault *impl); static void icon_view_scale_value_changed_cb (GtkRange *range, @@ -5208,12 +5209,22 @@ view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode) return; impl->view_mode = view_mode; - gtk_combo_box_set_active (GTK_COMBO_BOX (impl->view_mode_combo_box), - view_mode); + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (impl->view_mode_toggle_button), + view_mode == VIEW_MODE_ICON); /* Creating the target view */ if (view_mode == VIEW_MODE_ICON) { + if (!impl->icon_view_scale_hbox) + { + icon_view_scale_create (impl); + gtk_box_pack_end ( + GTK_BOX (impl->browse_path_bar_hbox), + impl->icon_view_scale_hbox, + FALSE, FALSE, 0); + } + create_browse_files_icon_view (impl); impl->browse_files_current_view = impl->browse_files_icon_view; old_view = impl->browse_files_tree_view; @@ -5246,7 +5257,8 @@ view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode) impl->browse_files_icon_view = NULL; impl->list_icon_renderer = NULL; impl->list_name_renderer = NULL; - gtk_widget_hide (impl->icon_view_scale_hbox); + if (impl->icon_view_scale_hbox) + gtk_widget_hide (impl->icon_view_scale_hbox); } else g_assert_not_reached (); @@ -5264,12 +5276,14 @@ view_mode_set (GtkFileChooserDefault *impl, ViewMode view_mode) browse_files_center_selected_row (impl); } -/* Callback used when view mode combo box active item is changed */ +/* Callback used when the view mode toggle button is toggled. */ static void -view_mode_combo_box_changed_cb (GtkComboBox *combo, +view_mode_toggle_button_toggled_cb (GtkToggleButton *toggle, GtkFileChooserDefault *impl) { - ViewMode target = gtk_combo_box_get_active (combo); + ViewMode target = gtk_toggle_button_get_active (toggle) + ? VIEW_MODE_ICON + : VIEW_MODE_LIST; view_mode_set (impl, target); } @@ -5698,20 +5712,15 @@ location_button_toggled_cb (GtkToggleButton *toggle, location_mode_set (impl, new_mode, FALSE); } -/* Creates a combo box with two items: List View and Icon View. */ +/* Creates the view mode toggle. */ static void -view_mode_combo_box_create (GtkFileChooserDefault *impl) +view_mode_toggle_button_create (GtkFileChooserDefault *impl) { - impl->view_mode_combo_box = gtk_combo_box_text_new (); - gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(impl->view_mode_combo_box), - _("List View")); /* VIEW_MODE_LIST */ - gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(impl->view_mode_combo_box), - _("Icon View")); /* VIEW_MODE_ICON */ - gtk_combo_box_set_active (GTK_COMBO_BOX(impl->view_mode_combo_box), - VIEW_MODE_LIST); + impl->view_mode_toggle_button = + gtk_toggle_button_new_with_label (_("Icon View")); - g_signal_connect (impl->view_mode_combo_box, "changed", - G_CALLBACK (view_mode_combo_box_changed_cb), impl); + g_signal_connect (impl->view_mode_toggle_button, "toggled", + G_CALLBACK (view_mode_toggle_button_toggled_cb), impl); } /* Creates a hscale for the icon view. */ @@ -5719,46 +5728,48 @@ static void icon_view_scale_create (GtkFileChooserDefault *impl) { GtkObject *adj; + GtkWidget *zoom_icon; GtkWidget *image; impl->icon_view_scale_hbox = gtk_hbox_new (FALSE, 12); - impl->icon_view_scale_zoom_out_icon = gtk_event_box_new (); - gtk_event_box_set_visible_window (GTK_EVENT_BOX (impl->icon_view_scale_zoom_out_icon), FALSE); + zoom_icon = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (zoom_icon), FALSE); image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_OUT, GTK_ICON_SIZE_BUTTON); - gtk_container_add (GTK_CONTAINER (impl->icon_view_scale_zoom_out_icon), image); + gtk_container_add (GTK_CONTAINER (zoom_icon), image); gtk_widget_show (image); - g_object_set_data (G_OBJECT (impl->icon_view_scale_zoom_out_icon), + g_object_set_data (G_OBJECT (zoom_icon), "gtk-file-chooser-zoom-direction", GINT_TO_POINTER (-1)); - gtk_widget_set_tooltip_text (impl->icon_view_scale_zoom_out_icon, _("Zoom out")); - g_signal_connect (impl->icon_view_scale_zoom_out_icon, "button-press-event", + gtk_widget_set_tooltip_text (zoom_icon, _("Zoom out")); + g_signal_connect (zoom_icon, "button-press-event", G_CALLBACK (icon_view_zoom_button_press_cb), impl); - gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->icon_view_scale_zoom_out_icon); - gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), impl->icon_view_scale_zoom_out_icon, FALSE, FALSE, 0); - gtk_widget_show (impl->icon_view_scale_zoom_out_icon); + gtk_size_group_add_widget (impl->browse_path_bar_size_group, zoom_icon); + gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), zoom_icon, FALSE, FALSE, 0); + gtk_widget_show (zoom_icon); - adj = gtk_adjustment_new (32, 32, 256, 16, 16, 0); + adj = gtk_adjustment_new (impl->icon_view_icon_size, + 32, 256, 16, 16, 0); impl->icon_view_scale = gtk_hscale_new (GTK_ADJUSTMENT (adj)); gtk_scale_set_draw_value (GTK_SCALE (impl->icon_view_scale), FALSE); gtk_widget_set_size_request (impl->icon_view_scale, 100, -1); gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), impl->icon_view_scale, FALSE, FALSE, 0); gtk_widget_show (impl->icon_view_scale); - impl->icon_view_scale_zoom_in_icon = gtk_event_box_new (); - gtk_event_box_set_visible_window (GTK_EVENT_BOX (impl->icon_view_scale_zoom_in_icon), FALSE); + zoom_icon = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (zoom_icon), FALSE); image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_BUTTON); - gtk_container_add (GTK_CONTAINER (impl->icon_view_scale_zoom_in_icon), image); + gtk_container_add (GTK_CONTAINER (zoom_icon), image); gtk_widget_show (image); - g_object_set_data (G_OBJECT (impl->icon_view_scale_zoom_in_icon), + g_object_set_data (G_OBJECT (zoom_icon), "gtk-file-chooser-zoom-direction", GINT_TO_POINTER (1)); - gtk_widget_set_tooltip_text (impl->icon_view_scale_zoom_in_icon, _("Zoom in")); - g_signal_connect (impl->icon_view_scale_zoom_in_icon, "button-press-event", + gtk_widget_set_tooltip_text (zoom_icon, _("Zoom in")); + g_signal_connect (zoom_icon, "button-press-event", G_CALLBACK (icon_view_zoom_button_press_cb), impl); - gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->icon_view_scale_zoom_in_icon); - gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), impl->icon_view_scale_zoom_in_icon, FALSE, FALSE, 0); - gtk_widget_show (impl->icon_view_scale_zoom_in_icon); + gtk_size_group_add_widget (impl->browse_path_bar_size_group, zoom_icon); + gtk_box_pack_start (GTK_BOX (impl->icon_view_scale_hbox), zoom_icon, FALSE, FALSE, 0); + gtk_widget_show (zoom_icon); g_signal_connect (impl->icon_view_scale, "value-changed", G_CALLBACK (icon_view_scale_value_changed_cb), impl); @@ -5883,9 +5894,9 @@ path_bar_widgets_create (GtkFileChooserDefault *impl) impl->browse_path_bar_size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); gtk_size_group_set_ignore_hidden (impl->browse_path_bar_size_group, FALSE); - /* View mode combo box */ - view_mode_combo_box_create (impl); - gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->view_mode_combo_box, FALSE, FALSE, 0); + /* View mode selector */ + view_mode_toggle_button_create (impl); + gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->view_mode_toggle_button, FALSE, FALSE, 0); /* Location button */ location_button_create (impl); @@ -5908,10 +5919,6 @@ path_bar_widgets_create (GtkFileChooserDefault *impl) /* Widgets for special modes (recently-used in Open mode, Search mode) */ special_mode_widgets_create (impl); - /* Icon view scale */ - icon_view_scale_create (impl); - gtk_box_pack_end (GTK_BOX (impl->browse_path_bar_hbox), impl->icon_view_scale_hbox, FALSE, FALSE, 0); - /* Create Folder */ impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder")); g_signal_connect (impl->browse_new_folder_button, "clicked", @@ -6477,9 +6484,9 @@ update_appearance (GtkFileChooserDefault *impl) if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) - gtk_widget_show (impl->view_mode_combo_box); + gtk_widget_show (impl->view_mode_toggle_button); else - gtk_widget_hide (impl->view_mode_combo_box); + gtk_widget_hide (impl->view_mode_toggle_button); if (impl->location_entry) _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action); @@ -7043,13 +7050,16 @@ settings_load (GtkFileChooserDefault *impl) location_mode_set (impl, location_mode, TRUE); - g_signal_handlers_block_by_func (impl->icon_view_scale, - G_CALLBACK (icon_view_scale_value_changed_cb), - impl); - gtk_range_set_value (GTK_RANGE (impl->icon_view_scale), icon_view_scale); - g_signal_handlers_unblock_by_func (impl->icon_view_scale, - G_CALLBACK (icon_view_scale_value_changed_cb), - impl); + if (impl->icon_view_scale) + { + g_signal_handlers_block_by_func (impl->icon_view_scale, + G_CALLBACK (icon_view_scale_value_changed_cb), + impl); + gtk_range_set_value (GTK_RANGE (impl->icon_view_scale), icon_view_scale); + g_signal_handlers_unblock_by_func (impl->icon_view_scale, + G_CALLBACK (icon_view_scale_value_changed_cb), + impl); + } view_mode_set (impl, view_mode); diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h index 193dea2e3..6e3456bc6 100644 --- a/gtk/gtkfilechooserprivate.h +++ b/gtk/gtkfilechooserprivate.h @@ -227,11 +227,9 @@ struct _GtkFileChooserDefault GtkWidget *extra_align; GtkWidget *extra_widget; - GtkWidget *view_mode_combo_box; + GtkWidget *view_mode_toggle_button; GtkWidget *icon_view_scale_hbox; GtkWidget *icon_view_scale; - GtkWidget *icon_view_scale_zoom_in_icon; - GtkWidget *icon_view_scale_zoom_out_icon; guint icon_view_scale_timeout_id; guint icon_view_scale_visible_timeout_id; guint icon_view_scale_after_layout_id; From 399e6ee64ecba9b47bd296c909e6105cad32e142 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 11 Aug 2026 10:47:36 +0200 Subject: [PATCH 37/45] cups: reduce authentication allocation overhead Avoid unnecessary copies and heap allocations in the CUPS authentication paths. Use borrowed strings where their lifetime is sufficient, use stack storage for the fixed password prompt fields, replace manual auth-info copying with g_strdupv(), and remove unused per-request hostname processing. Also free auth_info_visible in request_auth_info(). --- .../printbackends/cups/gtkprintbackendcups.c | 120 ++++++------------ 1 file changed, 40 insertions(+), 80 deletions(-) diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c index d07d1bc7a..58f664884 100644 --- a/modules/printbackends/cups/gtkprintbackendcups.c +++ b/modules/printbackends/cups/gtkprintbackendcups.c @@ -61,7 +61,6 @@ #include "gtkcupsutils.h" - typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass; #define GTK_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass)) @@ -979,55 +978,43 @@ gtk_print_backend_cups_set_password (GtkPrintBackend *backend, { GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend); GList *l; - char dispatch_hostname[HTTP_MAX_URI]; - gchar *key; - gchar *username = NULL; - gchar *hostname = NULL; - gchar *password = NULL; - gint length; + gchar *key = NULL; + const gchar *username = NULL; + const gchar *hostname = NULL; + const gchar *password = NULL; gint i; - length = g_strv_length (auth_info_required); - if (auth_info != NULL) - for (i = 0; i < length; i++) + for (i = 0; auth_info_required[i] != NULL; i++) { if (g_strcmp0 (auth_info_required[i], "username") == 0) - username = g_strdup (auth_info[i]); + username = auth_info[i]; else if (g_strcmp0 (auth_info_required[i], "hostname") == 0) - hostname = g_strdup (auth_info[i]); + hostname = auth_info[i]; else if (g_strcmp0 (auth_info_required[i], "password") == 0) - password = g_strdup (auth_info[i]); + password = auth_info[i]; } if (hostname != NULL && username != NULL && password != NULL) { key = g_strconcat (username, "@", hostname, NULL); - g_hash_table_insert (cups_backend->auth, key, g_strdup (password)); + g_hash_table_replace (cups_backend->auth, key, g_strdup (password)); + + GTK_NOTE (PRINTING, + g_print ("CUPS backend: storing password for %s\n", key)); } g_free (cups_backend->username); cups_backend->username = g_strdup (username); - GTK_NOTE (PRINTING, - g_print ("CUPS backend: storing password for %s\n", key)); - for (l = cups_backend->requests; l; l = l->next) { GtkPrintCupsDispatchWatch *dispatch = l->data; - httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname)); - if (is_address_local (dispatch_hostname)) - strcpy (dispatch_hostname, "localhost"); - if (dispatch->request->need_auth_info) { if (auth_info != NULL) - { - dispatch->request->auth_info = g_new0 (gchar *, length + 1); - for (i = 0; i < length; i++) - dispatch->request->auth_info[i] = g_strdup (auth_info[i]); - } + dispatch->request->auth_info = g_strdupv (auth_info); dispatch->backend->authentication_lock = FALSE; dispatch->request->need_auth_info = FALSE; } @@ -1052,12 +1039,14 @@ request_password (gpointer data) gchar *prompt = NULL; gchar *key = NULL; char hostname[HTTP_MAX_URI]; - gchar **auth_info_required; - gchar **auth_info_default; - gchar **auth_info_display; - gboolean *auth_info_visible; - gint length = 3; - gint i; + const gchar *auth_info_required[4] = + { "hostname", "username", "password", NULL }; + const gchar *auth_info_default[4] = + { NULL, NULL, NULL, NULL }; + const gchar *auth_info_display[4] = + { NULL, NULL, NULL, NULL }; + gboolean auth_info_visible[4] = + { FALSE, TRUE, FALSE, FALSE }; if (dispatch->backend->authentication_lock) return FALSE; @@ -1071,21 +1060,11 @@ request_password (gpointer data) else username = cupsUser (); - auth_info_required = g_new0 (gchar*, length + 1); - auth_info_required[0] = g_strdup ("hostname"); - auth_info_required[1] = g_strdup ("username"); - auth_info_required[2] = g_strdup ("password"); + auth_info_default[0] = hostname; + auth_info_default[1] = username; - auth_info_default = g_new0 (gchar*, length + 1); - auth_info_default[0] = g_strdup (hostname); - auth_info_default[1] = g_strdup (username); - - auth_info_display = g_new0 (gchar*, length + 1); - auth_info_display[1] = g_strdup (_("Username:")); - auth_info_display[2] = g_strdup (_("Password:")); - - auth_info_visible = g_new0 (gboolean, length + 1); - auth_info_visible[1] = TRUE; + auth_info_display[1] = _("Username:"); + auth_info_display[2] = _("Password:"); key = g_strconcat (username, "@", hostname, NULL); password = g_hash_table_lookup (dispatch->backend->auth, key); @@ -1105,10 +1084,10 @@ request_password (gpointer data) { const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name"); const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri"); - char *printer_name = NULL; + const char *printer_name = NULL; if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL) - printer_name = g_strdup (strrchr (printer_uri, '/') + 1); + printer_name = strrchr (printer_uri, '/') + 1; if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID) g_hash_table_remove (dispatch->backend->auth, key); @@ -1152,25 +1131,12 @@ request_password (gpointer data) break; } - g_free (printer_name); - g_signal_emit_by_name (dispatch->backend, "request-password", auth_info_required, auth_info_default, auth_info_display, auth_info_visible, prompt); g_free (prompt); } - for (i = 0; i < length; i++) - { - g_free (auth_info_required[i]); - g_free (auth_info_default[i]); - g_free (auth_info_display[i]); - } - - g_free (auth_info_required); - g_free (auth_info_default); - g_free (auth_info_display); - g_free (auth_info_visible); g_free (key); return FALSE; @@ -1270,12 +1236,12 @@ request_auth_info (gpointer user_data) const char *job_title; const char *printer_uri; gchar *prompt = NULL; - char *printer_name = NULL; + const char *printer_name = NULL; gint length; gint i; gboolean *auth_info_visible = NULL; - gchar **auth_info_default = NULL; - gchar **auth_info_display = NULL; + const gchar **auth_info_default = NULL; + const gchar **auth_info_display = NULL; dispatch = (GtkPrintCupsDispatchWatch *) user_data; @@ -1287,35 +1253,35 @@ request_auth_info (gpointer user_data) length = g_strv_length (dispatch->request->auth_info_required); auth_info_visible = g_new0 (gboolean, length); - auth_info_default = g_new0 (gchar *, length + 1); - auth_info_display = g_new0 (gchar *, length + 1); + auth_info_default = g_new0 (const gchar *, length + 1); + auth_info_display = g_new0 (const gchar *, length + 1); for (i = 0; i < length; i++) { if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0) { - auth_info_display[i] = g_strdup (_("Domain:")); - auth_info_default[i] = g_strdup ("WORKGROUP"); + auth_info_display[i] = _("Domain:"); + auth_info_default[i] = "WORKGROUP"; auth_info_visible[i] = TRUE; } else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0) { - auth_info_display[i] = g_strdup (_("Username:")); + auth_info_display[i] = _("Username:"); if (dispatch->backend->username != NULL) - auth_info_default[i] = g_strdup (dispatch->backend->username); + auth_info_default[i] = dispatch->backend->username; else - auth_info_default[i] = g_strdup (cupsUser ()); + auth_info_default[i] = cupsUser (); auth_info_visible[i] = TRUE; } else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0) { - auth_info_display[i] = g_strdup (_("Password:")); + auth_info_display[i] = _("Password:"); auth_info_visible[i] = FALSE; } } if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL) - printer_name = g_strdup (strrchr (printer_uri, '/') + 1); + printer_name = strrchr (printer_uri, '/') + 1; dispatch->backend->authentication_lock = TRUE; @@ -1341,15 +1307,9 @@ request_auth_info (gpointer user_data) auth_info_visible, prompt); - for (i = 0; i < length; i++) - { - g_free (auth_info_default[i]); - g_free (auth_info_display[i]); - } - g_free (auth_info_default); g_free (auth_info_display); - g_free (printer_name); + g_free (auth_info_visible); g_free (prompt); g_idle_add (check_auth_info, user_data); From fa2e5d3293470e1dc896a51316927fa3ab5fcfae Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 12 Aug 2026 19:38:55 +0200 Subject: [PATCH 38/45] cups: remember authentication with Secret Service Look up and store CUPS authentication information through the Secret Service D-Bus API. Enable the feature by default when CUPS support is built, requiring the libsecret development files unless explicitly disabled. Keep the print backend free of a runtime libsecret dependency so authentication continues to work when Secret Service is unavailable. --- configure.ac | 16 + gtk/gtkprintbackend.c | 90 ++ .../printbackends/cups/gtkprintbackendcups.c | 941 ++++++++++++++++++ 3 files changed, 1047 insertions(+) diff --git a/configure.ac b/configure.ac index 04536e910..553ea001c 100644 --- a/configure.ac +++ b/configure.ac @@ -1538,6 +1538,11 @@ LIBS="$old_LIBS" # Printing system checks ################################################################ +AC_ARG_ENABLE(cups-secret-service, + [AS_HELP_STRING([--disable-cups-secret-service] + [disable Secret Service support in the cups print backend])],, + [enable_cups_secret_service=yes]) + AC_ARG_ENABLE(cups, [AS_HELP_STRING([--disable-cups] [disable cups print backend])],, @@ -1584,6 +1589,17 @@ else AM_CONDITIONAL(HAVE_CUPS, true) + if test "x$enable_cups_secret_service" != "xno"; then + PKG_CHECK_EXISTS([libsecret-1], [], + [AC_MSG_ERROR([ +*** libsecret-1 is required when CUPS Secret Service support is enabled. +*** Install the libsecret development files or configure +*** with --disable-cups-secret-service. +])]) + AC_DEFINE([HAVE_CUPS_SECRET_SERVICE], [1], + [Define to 1 if CUPS Secret Service support is enabled]) + fi + gtk_save_cflags="$CFLAGS" CFLAGS="$CUPS_CFLAGS" AC_COMPILE_IFELSE( diff --git a/gtk/gtkprintbackend.c b/gtk/gtkprintbackend.c index 66f5b41a9..5efd124e5 100644 --- a/gtk/gtkprintbackend.c +++ b/gtk/gtkprintbackend.c @@ -48,8 +48,22 @@ struct _GtkPrintBackendPrivate GtkPrintBackendStatus status; char **auth_info_required; char **auth_info; +#ifdef HAVE_CUPS_SECRET_SERVICE + guint store_auth_info : 1; +#endif }; +#ifdef HAVE_CUPS_SECRET_SERVICE +typedef gboolean (*GtkPrintBackendCanStoreAuthInfoFunc) + (GtkPrintBackend *backend); + +typedef void (*GtkPrintBackendSetPasswordFullFunc) + (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info); +#endif + enum { PRINTER_LIST_CHANGED, PRINTER_LIST_DONE, @@ -459,6 +473,9 @@ gtk_print_backend_init (GtkPrintBackend *backend) (GDestroyNotify) g_object_unref); priv->auth_info_required = NULL; priv->auth_info = NULL; +#ifdef HAVE_CUPS_SECRET_SERVICE + priv->store_auth_info = FALSE; +#endif } static void @@ -675,6 +692,41 @@ gtk_print_backend_set_password (GtkPrintBackend *backend, GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password (backend, auth_info_required, auth_info); } + +#ifdef HAVE_CUPS_SECRET_SERVICE +static void +gtk_print_backend_set_password_full (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info) +{ + GtkPrintBackendClass *class; + + class = GTK_PRINT_BACKEND_GET_CLASS (backend); + + if (class->_gtk_reserved2 != NULL) + ((GtkPrintBackendSetPasswordFullFunc) class->_gtk_reserved2) + (backend, + auth_info_required, + auth_info, + store_auth_info); + else + gtk_print_backend_set_password (backend, + auth_info_required, + auth_info); +} + + +static void +store_auth_info_toggled (GtkToggleButton *button, + GtkPrintBackend *backend) +{ + backend->priv->store_auth_info = + gtk_toggle_button_get_active (button); +} +#endif + + static void store_entry (GtkEntry *entry, gpointer user_data) @@ -698,10 +750,23 @@ password_dialog_response (GtkWidget *dialog, GtkPrintBackendPrivate *priv = backend->priv; gint i; +#ifdef HAVE_CUPS_SECRET_SERVICE + if (response_id == GTK_RESPONSE_OK) + gtk_print_backend_set_password_full (backend, + priv->auth_info_required, + priv->auth_info, + priv->store_auth_info); + else + gtk_print_backend_set_password_full (backend, + priv->auth_info_required, + NULL, + FALSE); +#else if (response_id == GTK_RESPONSE_OK) gtk_print_backend_set_password (backend, priv->auth_info_required, priv->auth_info); else gtk_print_backend_set_password (backend, priv->auth_info_required, NULL); +#endif for (i = 0; i < g_strv_length (priv->auth_info_required); i++) if (priv->auth_info[i] != NULL) @@ -738,10 +803,23 @@ request_password (GtkPrintBackend *backend, gchar **ai_default = (gchar **) auth_info_default; gchar **ai_display = (gchar **) auth_info_display; gboolean *ai_visible = (gboolean *) auth_info_visible; +#ifdef HAVE_CUPS_SECRET_SERVICE + GtkPrintBackendClass *class; + gboolean can_store_auth_info = FALSE; +#endif priv->auth_info_required = g_strdupv (ai_required); length = g_strv_length (ai_required); priv->auth_info = g_new0 (gchar *, length + 1); +#ifdef HAVE_CUPS_SECRET_SERVICE + priv->store_auth_info = FALSE; + + class = GTK_PRINT_BACKEND_GET_CLASS (backend); + if (class->_gtk_reserved1 != NULL) + can_store_auth_info = + ((GtkPrintBackendCanStoreAuthInfoFunc) class->_gtk_reserved1) + (backend); +#endif dialog = gtk_dialog_new_with_buttons ( _("Authentication"), NULL, GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, @@ -810,6 +888,18 @@ request_password (GtkPrintBackend *backend, } } +#ifdef HAVE_CUPS_SECRET_SERVICE + if (can_store_auth_info) + { + GtkWidget *remember; + + remember = gtk_check_button_new_with_mnemonic (_("_Remember password")); + gtk_box_pack_start (GTK_BOX (vbox), remember, FALSE, FALSE, 6); + g_signal_connect (remember, "toggled", + G_CALLBACK (store_auth_info_toggled), backend); + } +#endif + if (focus != NULL) { gtk_widget_grab_focus (focus); diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c index 58f664884..c417ba88e 100644 --- a/modules/printbackends/cups/gtkprintbackendcups.c +++ b/modules/printbackends/cups/gtkprintbackendcups.c @@ -143,6 +143,11 @@ struct _GtkPrintBackendCups GHashTable *auth; gchar *username; gboolean authentication_lock; +#ifdef HAVE_CUPS_SECRET_SERVICE + gchar *store_auth_info_uri; + guint secrets_service_available : 1; + guint store_auth_info_allowed : 1; +#endif #ifdef HAVE_CUPS_API_1_6 GDBusConnection *dbus_connection; gchar *avahi_default_printer; @@ -212,10 +217,21 @@ static cairo_surface_t * cups_printer_create_cairo_surface (GtkPrinter static void gtk_print_backend_cups_set_password (GtkPrintBackend *backend, gchar **auth_info_required, gchar **auth_info); +#ifdef HAVE_CUPS_SECRET_SERVICE +static void gtk_print_backend_cups_set_password_full (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info); +static gboolean gtk_print_backend_cups_can_store_auth_info + (GtkPrintBackend *backend); +#endif void overwrite_and_free (gpointer data); static gboolean is_address_local (const gchar *address); static gboolean request_auth_info (gpointer data); +#ifdef HAVE_CUPS_SECRET_SERVICE +static void lookup_auth_info (gpointer data); +#endif #ifdef HAVE_CUPS_API_1_6 static void avahi_request_printer_list (GtkPrintBackendCups *cups_backend); @@ -340,6 +356,12 @@ gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class) backend_class->printer_get_hard_margins = cups_printer_get_hard_margins; backend_class->printer_get_capabilities = cups_printer_get_capabilities; backend_class->set_password = gtk_print_backend_cups_set_password; +#ifdef HAVE_CUPS_SECRET_SERVICE + backend_class->_gtk_reserved1 = + (void (*) (void)) gtk_print_backend_cups_can_store_auth_info; + backend_class->_gtk_reserved2 = + (void (*) (void)) gtk_print_backend_cups_set_password_full; +#endif } static cairo_status_t @@ -806,6 +828,11 @@ gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups) backend_cups->requests = NULL; backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free); backend_cups->authentication_lock = FALSE; +#ifdef HAVE_CUPS_SECRET_SERVICE + backend_cups->store_auth_info_uri = NULL; + backend_cups->secrets_service_available = FALSE; + backend_cups->store_auth_info_allowed = FALSE; +#endif backend_cups->covers = NULL; backend_cups->number_of_covers = 0; @@ -852,6 +879,10 @@ gtk_print_backend_cups_finalize (GObject *object) g_free (backend_cups->username); +#ifdef HAVE_CUPS_SECRET_SERVICE + g_free (backend_cups->store_auth_info_uri); +#endif + #ifdef HAVE_CUPS_API_1_6 g_clear_object (&backend_cups->avahi_cancellable); g_free (backend_cups->avahi_default_printer); @@ -971,10 +1002,22 @@ httpGetHostname(http_t *http, } #endif +#ifdef HAVE_CUPS_SECRET_SERVICE +static void cups_secret_store (gchar **auth_info, + gchar **auth_info_labels, + const gchar *printer_uri); + +static void +gtk_print_backend_cups_set_password_full (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info, + gboolean store_auth_info) +#else static void gtk_print_backend_cups_set_password (GtkPrintBackend *backend, gchar **auth_info_required, gchar **auth_info) +#endif { GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend); GList *l; @@ -1007,6 +1050,18 @@ gtk_print_backend_cups_set_password (GtkPrintBackend *backend, g_free (cups_backend->username); cups_backend->username = g_strdup (username); +#ifdef HAVE_CUPS_SECRET_SERVICE + if (store_auth_info && + password != NULL && + cups_backend->store_auth_info_uri != NULL) + cups_secret_store (auth_info, + auth_info_required, + cups_backend->store_auth_info_uri); + + g_free (cups_backend->store_auth_info_uri); + cups_backend->store_auth_info_uri = NULL; +#endif + for (l = cups_backend->requests; l; l = l->next) { GtkPrintCupsDispatchWatch *dispatch = l->data; @@ -1030,6 +1085,33 @@ gtk_print_backend_cups_set_password (GtkPrintBackend *backend, } } + +#ifdef HAVE_CUPS_SECRET_SERVICE +static void +gtk_print_backend_cups_set_password (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info) +{ + gtk_print_backend_cups_set_password_full (backend, + auth_info_required, + auth_info, + FALSE); +} + + +static gboolean +gtk_print_backend_cups_can_store_auth_info (GtkPrintBackend *backend) +{ + GtkPrintBackendCups *cups_backend; + + cups_backend = GTK_PRINT_BACKEND_CUPS (backend); + + return cups_backend->secrets_service_available && + cups_backend->store_auth_info_allowed; +} +#endif + + static gboolean request_password (gpointer data) { @@ -1229,6 +1311,845 @@ check_auth_info (gpointer user_data) return TRUE; } + +#ifdef HAVE_CUPS_SECRET_SERVICE + +#define CUPS_SECRET_BUS "org.freedesktop.secrets" +#define CUPS_SECRET_PATH "/org/freedesktop/secrets" +#define CUPS_SECRET_DEFAULT_COLLECTION \ + "/org/freedesktop/secrets/aliases/default" +#define CUPS_SECRET_IFACE(x) "org.freedesktop.Secret." x +#define CUPS_SECRET_TIMEOUT 5000 + +typedef enum +{ + CUPS_SECRET_SEARCH, + CUPS_SECRET_UNLOCK, + CUPS_SECRET_PROMPT, + CUPS_SECRET_PROMPT_WAIT, + CUPS_SECRET_ATTRIBUTES, + CUPS_SECRET_SESSION, + CUPS_SECRET_VALUE, + CUPS_SECRET_CREATE +} CupsSecretStep; + +typedef struct +{ + GDBusConnection *connection; + GtkPrintCupsDispatchWatch *dispatch; + gchar *item_path; + gchar *session_path; + gchar *prompt_path; + gchar **auth_info; + GVariant *store_properties; + gchar *store_password; + guint prompt_subscription; + guint length; + guint prompt_completed : 1; + guint prompt_dismissed : 1; + guint store : 1; + guint create_prompt : 1; + gint password_field; + CupsSecretStep step; +} CupsSecretLookup; + + +static void cups_secret_reply (GObject *, GAsyncResult *, gpointer); + + +static void +cups_secret_call (CupsSecretLookup *lookup, + const gchar *path, + const gchar *interface, + const gchar *method, + GVariant *parameters, + const GVariantType *reply_type, + CupsSecretStep step) +{ + lookup->step = step; + + g_dbus_connection_call (lookup->connection, + CUPS_SECRET_BUS, + path, + interface, + method, + parameters, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + CUPS_SECRET_TIMEOUT, + NULL, + cups_secret_reply, + lookup); +} + + +static void +cups_secret_free (CupsSecretLookup *lookup) +{ + guint i; + + if (lookup->prompt_subscription != 0) + g_dbus_connection_signal_unsubscribe (lookup->connection, + lookup->prompt_subscription); + + if (lookup->session_path != NULL) + g_dbus_connection_call (lookup->connection, + CUPS_SECRET_BUS, + lookup->session_path, + CUPS_SECRET_IFACE ("Session"), + "Close", + NULL, + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + CUPS_SECRET_TIMEOUT, + NULL, + NULL, + NULL); + + if (lookup->auth_info != NULL) + { + for (i = 0; i < lookup->length; i++) + overwrite_and_free (lookup->auth_info[i]); + + g_free (lookup->auth_info); + } + + if (lookup->store_properties != NULL) + g_variant_unref (lookup->store_properties); + + if (lookup->store_password != NULL) + overwrite_and_free (lookup->store_password); + + g_free (lookup->item_path); + g_free (lookup->session_path); + g_free (lookup->prompt_path); + + if (lookup->connection != NULL) + g_object_unref (lookup->connection); + + g_free (lookup); +} + + +static void +cups_secret_done (CupsSecretLookup *lookup, + gboolean found, + GError *error) +{ + GtkPrintCupsDispatchWatch *dispatch; + + if (lookup->store) + { + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("CUPS backend: failed to remember password: %s\n", + error->message)); + g_error_free (error); + } + + cups_secret_free (lookup); + return; + } + + dispatch = lookup->dispatch; + + dispatch->backend->secrets_service_available = + error == NULL; + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("CUPS backend: Secret Service lookup failed: %s\n", + error->message)); + g_error_free (error); + } + + if (found) + gtk_print_backend_cups_set_password ( + GTK_PRINT_BACKEND (dispatch->backend), + dispatch->request->auth_info_required, + lookup->auth_info); + else + dispatch->backend->authentication_lock = FALSE; + + cups_secret_free (lookup); + + if (found) + check_auth_info (dispatch); + else + request_auth_info (dispatch); +} + + + +static void +cups_secret_get_attributes (CupsSecretLookup *lookup) +{ + cups_secret_call (lookup, + lookup->item_path, + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + CUPS_SECRET_IFACE ("Item"), + "Attributes"), + G_VARIANT_TYPE ("(v)"), + CUPS_SECRET_ATTRIBUTES); +} + + +static void +cups_secret_open_session (CupsSecretLookup *lookup) +{ + cups_secret_call ( + lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "OpenSession", + g_variant_new ("(sv)", + "plain", + g_variant_new_string ("")), + G_VARIANT_TYPE ("(vo)"), + CUPS_SECRET_SESSION); +} + + +static void +cups_secret_create_item (CupsSecretLookup *lookup) +{ + GVariant *properties; + GVariant *secret; + + if (lookup->store_properties == NULL || + lookup->store_password == NULL || + lookup->session_path == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + properties = lookup->store_properties; + lookup->store_properties = NULL; + + secret = g_variant_new ( + "(oay@ays)", + lookup->session_path, + NULL, + g_variant_new_bytestring (lookup->store_password), + "text/plain"); + + cups_secret_call ( + lookup, + CUPS_SECRET_DEFAULT_COLLECTION, + CUPS_SECRET_IFACE ("Collection"), + "CreateItem", + g_variant_new ("(@a{sv}@(oayays)b)", + properties, + secret, + TRUE), + G_VARIANT_TYPE ("(oo)"), + CUPS_SECRET_CREATE); +} + + +static void +cups_secret_after_prompt (CupsSecretLookup *lookup, + gboolean dismissed) +{ + if (dismissed) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + if (!lookup->store) + { + cups_secret_get_attributes (lookup); + return; + } + + if (lookup->create_prompt) + { + cups_secret_done (lookup, TRUE, NULL); + return; + } + + cups_secret_open_session (lookup); +} + + + +static void +cups_secret_prompt_completed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + CupsSecretLookup *lookup = user_data; + gboolean dismissed; + + (void) connection; + (void) sender_name; + (void) object_path; + (void) interface_name; + (void) signal_name; + + g_dbus_connection_signal_unsubscribe ( + lookup->connection, + lookup->prompt_subscription); + + lookup->prompt_subscription = 0; + + g_free (lookup->prompt_path); + lookup->prompt_path = NULL; + + if (!g_variant_is_of_type (parameters, + G_VARIANT_TYPE ("(bv)"))) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + g_variant_get (parameters, "(bv)", + &dismissed, NULL); + + if (lookup->step == CUPS_SECRET_PROMPT) + { + lookup->prompt_completed = TRUE; + lookup->prompt_dismissed = dismissed; + return; + } + + cups_secret_after_prompt (lookup, dismissed); +} + + +static void +cups_secret_prompt (CupsSecretLookup *lookup) +{ + lookup->prompt_completed = FALSE; + lookup->prompt_dismissed = FALSE; + + lookup->prompt_subscription = + g_dbus_connection_signal_subscribe ( + lookup->connection, + NULL, + CUPS_SECRET_IFACE ("Prompt"), + "Completed", + lookup->prompt_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + cups_secret_prompt_completed, + lookup, + NULL); + + cups_secret_call ( + lookup, + lookup->prompt_path, + CUPS_SECRET_IFACE ("Prompt"), + "Prompt", + g_variant_new ("(s)", "0"), + G_VARIANT_TYPE ("()"), + CUPS_SECRET_PROMPT); +} + + + +static void +cups_secret_reply (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CupsSecretLookup *lookup = user_data; + GError *error = NULL; + GVariant *output; + + output = g_dbus_connection_call_finish ( + G_DBUS_CONNECTION (source_object), res, &error); + + if (output == NULL) + { + cups_secret_done (lookup, FALSE, error); + return; + } + + switch (lookup->step) + { + case CUPS_SECRET_SEARCH: + { + const gchar **unlocked; + const gchar **locked; + const gchar *path; + gboolean is_locked; + + g_variant_get (output, "(^a&o^a&o)", + &unlocked, &locked); + g_variant_unref (output); + + if (unlocked[0] != NULL) + { + path = unlocked[0]; + is_locked = FALSE; + } + else + { + path = locked[0]; + is_locked = TRUE; + } + + if (path != NULL) + lookup->item_path = g_strdup (path); + + g_free (unlocked); + g_free (locked); + + if (lookup->item_path == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + if (!is_locked) + { + cups_secret_get_attributes (lookup); + return; + } + + { + const gchar *paths[2] = + { lookup->item_path, NULL }; + + cups_secret_call (lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "Unlock", + g_variant_new ("(^ao)", paths), + G_VARIANT_TYPE ("(aoo)"), + CUPS_SECRET_UNLOCK); + } + + return; + } + + case CUPS_SECRET_UNLOCK: + { + g_variant_get (output, "(@aoo)", + NULL, &lookup->prompt_path); + g_variant_unref (output); + + if (strcmp (lookup->prompt_path, "/") == 0) + { + g_free (lookup->prompt_path); + lookup->prompt_path = NULL; + cups_secret_after_prompt (lookup, FALSE); + return; + } + + cups_secret_prompt (lookup); + return; + } + + case CUPS_SECRET_PROMPT: + g_variant_unref (output); + + if (lookup->prompt_completed) + cups_secret_after_prompt (lookup, + lookup->prompt_dismissed); + else + lookup->step = CUPS_SECRET_PROMPT_WAIT; + + return; + + case CUPS_SECRET_PROMPT_WAIT: + break; + + case CUPS_SECRET_ATTRIBUTES: + { + GVariant *attributes; + gchar **required; + const gchar *value; + guint i; + + g_variant_get (output, "(v)", &attributes); + g_variant_unref (output); + + if (!g_variant_is_of_type (attributes, + G_VARIANT_TYPE ("a{ss}"))) + { + g_variant_unref (attributes); + cups_secret_done (lookup, FALSE, NULL); + return; + } + + required = lookup->dispatch->request->auth_info_required; + lookup->auth_info = + g_new0 (gchar *, lookup->length + 1); + lookup->password_field = -1; + + for (i = 0; i < lookup->length; i++) + { + value = NULL; + + if (strcmp (required[i], "password") == 0) + { + lookup->password_field = i; + continue; + } + + if (strcmp (required[i], "username") == 0) + { + if (!g_variant_lookup (attributes, + "user", + "&s", + &value)) + g_variant_lookup (attributes, + "username", + "&s", + &value); + } + else if (strcmp (required[i], "hostname") == 0) + { + if (!g_variant_lookup (attributes, + "server", + "&s", + &value)) + g_variant_lookup (attributes, + "hostname", + "&s", + &value); + } + else if (strcmp (required[i], "domain") == 0) + { + g_variant_lookup (attributes, + "domain", + "&s", + &value); + } + + if (value == NULL) + { + g_variant_unref (attributes); + cups_secret_done (lookup, FALSE, NULL); + return; + } + + lookup->auth_info[i] = g_strdup (value); + } + + g_variant_unref (attributes); + + if (lookup->password_field < 0) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + cups_secret_open_session (lookup); + return; + } + + case CUPS_SECRET_SESSION: + { + g_variant_get (output, "(vo)", + NULL, &lookup->session_path); + g_variant_unref (output); + + if (strcmp (lookup->session_path, "/") == 0) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + if (lookup->store) + { + cups_secret_create_item (lookup); + return; + } + + cups_secret_call ( + lookup, + lookup->item_path, + CUPS_SECRET_IFACE ("Item"), + "GetSecret", + g_variant_new ("(o)", + lookup->session_path), + G_VARIANT_TYPE ("((oayays))"), + CUPS_SECRET_VALUE); + + return; + } + + case CUPS_SECRET_VALUE: + { + GVariant *value; + gconstpointer password; + gsize length; + guint i; + + g_variant_get (output, + "((o@ay@ays))", + NULL, NULL, &value, NULL); + g_variant_unref (output); + + password = g_variant_get_fixed_array ( + value, &length, sizeof (guchar)); + + if (password != NULL) + lookup->auth_info[lookup->password_field] = + g_strndup (password, length); + + g_variant_unref (value); + + for (i = 0; i < lookup->length; i++) + if (lookup->auth_info[i] == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + cups_secret_done (lookup, TRUE, NULL); + return; + } + + case CUPS_SECRET_CREATE: + { + const gchar *item; + const gchar *prompt; + + g_variant_get (output, "(&o&o)", + &item, &prompt); + + if (strcmp (item, "/") != 0) + { + g_variant_unref (output); + cups_secret_done (lookup, TRUE, NULL); + return; + } + + if (strcmp (prompt, "/") == 0) + { + g_variant_unref (output); + cups_secret_done (lookup, FALSE, NULL); + return; + } + + g_free (lookup->prompt_path); + lookup->prompt_path = g_strdup (prompt); + lookup->create_prompt = TRUE; + + g_variant_unref (output); + + cups_secret_prompt (lookup); + return; + } + } + + g_variant_unref (output); + cups_secret_done (lookup, FALSE, NULL); +} + + +static void +cups_secret_connection (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CupsSecretLookup *lookup = user_data; + GVariantBuilder builder; + GError *error = NULL; + const gchar *uri; + + (void) source_object; + + lookup->connection = g_bus_get_finish (res, &error); + + if (lookup->connection == NULL) + { + cups_secret_done (lookup, FALSE, error); + return; + } + + if (lookup->store) + { + const gchar *paths[2] = + { CUPS_SECRET_DEFAULT_COLLECTION, NULL }; + + cups_secret_call ( + lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "Unlock", + g_variant_new ("(^ao)", paths), + G_VARIANT_TYPE ("(aoo)"), + CUPS_SECRET_UNLOCK); + + return; + } + + uri = gtk_cups_request_ipp_get_string ( + lookup->dispatch->request, + IPP_TAG_URI, + "printer-uri"); + + if (uri == NULL) + { + cups_secret_done (lookup, FALSE, NULL); + return; + } + + g_variant_builder_init (&builder, + G_VARIANT_TYPE ("a{ss}")); + g_variant_builder_add (&builder, + "{ss}", + "uri", + uri); + + cups_secret_call ( + lookup, + CUPS_SECRET_PATH, + CUPS_SECRET_IFACE ("Service"), + "SearchItems", + g_variant_new ("(@a{ss})", + g_variant_builder_end (&builder)), + G_VARIANT_TYPE ("(aoao)"), + CUPS_SECRET_SEARCH); +} + + +static void +lookup_auth_info (gpointer data) +{ + GtkPrintCupsDispatchWatch *dispatch = data; + CupsSecretLookup *lookup; + gchar **required; + guint i; + + if (dispatch->backend->authentication_lock) + return; + + required = dispatch->request->auth_info_required; + + for (i = 0; required[i] != NULL; i++) + if (strcmp (required[i], "password") == 0) + break; + + if (required[i] == NULL || + gtk_cups_request_ipp_get_string ( + dispatch->request, + IPP_TAG_URI, + "printer-uri") == NULL) + { + request_auth_info (dispatch); + return; + } + + lookup = g_new0 (CupsSecretLookup, 1); + lookup->dispatch = dispatch; + lookup->length = g_strv_length (required); + + dispatch->backend->authentication_lock = TRUE; + + g_bus_get (G_BUS_TYPE_SESSION, + NULL, + cups_secret_connection, + lookup); +} + + +static void +cups_secret_store (gchar **auth_info, + gchar **auth_info_labels, + const gchar *printer_uri) +{ + CupsSecretLookup *lookup; + GVariantBuilder attributes; + GVariantBuilder properties; + const gchar *password = NULL; + const gchar *key; + guint i; + + if (auth_info == NULL || + auth_info_labels == NULL || + printer_uri == NULL) + return; + + g_variant_builder_init (&attributes, + G_VARIANT_TYPE ("a{ss}")); + + g_variant_builder_add (&attributes, + "{ss}", + "uri", + printer_uri); + + for (i = 0; auth_info_labels[i] != NULL; i++) + { + if (auth_info[i] == NULL) + continue; + + key = NULL; + + if (strcmp (auth_info_labels[i], "password") == 0) + { + password = auth_info[i]; + continue; + } + + if (strcmp (auth_info_labels[i], "username") == 0) + key = "user"; + else if (strcmp (auth_info_labels[i], "hostname") == 0) + key = "server"; + else if (strcmp (auth_info_labels[i], "domain") == 0) + key = "domain"; + + if (key != NULL) + g_variant_builder_add (&attributes, + "{ss}", + key, + auth_info[i]); + } + + if (password == NULL) + { + g_variant_builder_clear (&attributes); + return; + } + + g_variant_builder_init (&properties, + G_VARIANT_TYPE ("a{sv}")); + + g_variant_builder_add ( + &properties, + "{sv}", + CUPS_SECRET_IFACE ("Item.Label"), + g_variant_new_string (printer_uri)); + + g_variant_builder_add ( + &properties, + "{sv}", + CUPS_SECRET_IFACE ("Item.Attributes"), + g_variant_builder_end (&attributes)); + + lookup = g_new0 (CupsSecretLookup, 1); + lookup->store = TRUE; + + lookup->store_properties = + g_variant_ref_sink ( + g_variant_builder_end (&properties)); + + lookup->store_password = + g_strdup (password); + + g_bus_get (G_BUS_TYPE_SESSION, + NULL, + cups_secret_connection, + lookup); +} +#endif + + static gboolean request_auth_info (gpointer user_data) { @@ -1300,6 +2221,18 @@ request_auth_info (gpointer user_data) prompt = g_strdup ( _("Authentication is required to print this document")); } +#ifdef HAVE_CUPS_SECRET_SERVICE + g_free (dispatch->backend->store_auth_info_uri); + dispatch->backend->store_auth_info_uri = NULL; + + dispatch->backend->store_auth_info_allowed = + dispatch->backend->secrets_service_available && + printer_uri != NULL; + + if (dispatch->backend->store_auth_info_allowed) + dispatch->backend->store_auth_info_uri = g_strdup (printer_uri); +#endif + g_signal_emit_by_name (dispatch->backend, "request-password", dispatch->request->auth_info_required, auth_info_default, @@ -1307,6 +2240,10 @@ request_auth_info (gpointer user_data) auth_info_visible, prompt); +#ifdef HAVE_CUPS_SECRET_SERVICE + dispatch->backend->store_auth_info_allowed = FALSE; +#endif + g_free (auth_info_default); g_free (auth_info_display); g_free (auth_info_visible); @@ -1514,7 +2451,11 @@ cups_request_execute (GtkPrintBackendCups *print_backend, { dispatch->callback = callback; dispatch->callback_data = user_data; +#ifdef HAVE_CUPS_SECRET_SERVICE + lookup_auth_info (dispatch); +#else request_auth_info (dispatch); +#endif } else { From f8ff879dd288ec8442bc0a2544c3d762d57d13a8 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 12 Aug 2026 20:59:11 +0200 Subject: [PATCH 39/45] x11: size available depth array correctly Visual initialization considers eight possible depths, but GdkScreenX11 only reserved space for seven. Reserve all eight entries so a server exposing every considered depth cannot write past the array. --- gdk/x11/gdkscreen-x11.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdk/x11/gdkscreen-x11.h b/gdk/x11/gdkscreen-x11.h index 7df8e0e7f..35d5d6c13 100644 --- a/gdk/x11/gdkscreen-x11.h +++ b/gdk/x11/gdkscreen-x11.h @@ -73,7 +73,7 @@ struct _GdkScreenX11 GdkVisualPrivate *system_visual; GdkVisualPrivate **visuals; gint nvisuals; - gint available_depths[7]; + gint available_depths[8]; gint navailable_depths; GdkVisualType available_types[6]; gint navailable_types; From be71ea43fca6a9dcbf4e509aa722c2901690ad68 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 12 Aug 2026 21:54:38 +0200 Subject: [PATCH 40/45] updateiconcache: make directory scans deterministic and leaner Sort directory entries before scanning so cache layout does not depend on filesystem enumeration order. Classify entries with one stat operation, reuse the sorted name buffers for suffix and basename handling, and release transient scan and cache-building resources once writing is complete. This preserves the sorted cache output while reducing filesystem metadata calls and eliminating observed leaks. Provide compile-time S_ISDIR and S_ISREG fallbacks for MSVC. Based on the openSUSE gtk2-updateiconcache_sort.patch. --- gtk/updateiconcache.c | 204 +++++++++++++++++++++++++++++------------- 1 file changed, 140 insertions(+), 64 deletions(-) diff --git a/gtk/updateiconcache.c b/gtk/updateiconcache.c index 4a3158e12..e167d2f8f 100644 --- a/gtk/updateiconcache.c +++ b/gtk/updateiconcache.c @@ -25,6 +25,15 @@ #include #include #include + +#ifdef _MSC_VER +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & _S_IFMT) == _S_IFREG) +#endif +#endif #include #ifdef HAVE_UNISTD_H #include @@ -608,6 +617,16 @@ replace_backslashes_with_slashes (gchar *path) path[i] = '/'; } +static gint +compare_string_pointers (gconstpointer a, + gconstpointer b) +{ + const gchar * const *str_a = a; + const gchar * const *str_b = b; + + return strcmp (*str_a, *str_b); +} + static GList * scan_directory (const gchar *base_path, const gchar *subdir, @@ -617,10 +636,13 @@ scan_directory (const gchar *base_path, { GHashTable *dir_hash; GDir *dir; - const gchar *name; + GPtrArray *names; + const gchar *entry_name; + gchar *name; gchar *dir_path; gboolean dir_added = FALSE; guint dir_index = 0xffff; + guint i; dir_path = g_build_path ("/", base_path, subdir, NULL); @@ -628,91 +650,103 @@ scan_directory (const gchar *base_path, dir = g_dir_open (dir_path, 0, NULL); if (!dir) - return directories; + { + g_free (dir_path); + return directories; + } + + names = g_ptr_array_new_with_free_func (g_free); + + while ((entry_name = g_dir_read_name (dir))) + g_ptr_array_add (names, g_strdup (entry_name)); + + g_dir_close (dir); + + g_ptr_array_sort (names, compare_string_pointers); dir_hash = g_hash_table_new (g_str_hash, g_str_equal); - while ((name = g_dir_read_name (dir))) + for (i = 0; i < names->len; i++) { + GStatBuf stat_buf; gchar *path; - gboolean retval; - int flags = 0; - Image *image; - gchar *basename, *dot; + name = g_ptr_array_index (names, i); path = g_build_filename (dir_path, name, NULL); - retval = g_file_test (path, G_FILE_TEST_IS_DIR); - if (retval) - { - gchar *subsubdir; + if (g_stat (path, &stat_buf) == 0) + { + if (S_ISDIR (stat_buf.st_mode)) + { + gchar *subsubdir; - if (subdir) - subsubdir = g_build_path ("/", subdir, name, NULL); - else - subsubdir = g_strdup (name); - directories = scan_directory (base_path, subsubdir, files, - directories, depth + 1); - g_free (subsubdir); + if (subdir) + subsubdir = g_build_path ("/", subdir, name, NULL); + else + subsubdir = g_strdup (name); - continue; - } + directories = scan_directory (base_path, subsubdir, files, + directories, depth + 1); + g_free (subsubdir); + } + else if (subdir != NULL && S_ISREG (stat_buf.st_mode)) + { + gint flags = 0; + Image *image; + gchar *dot; - /* ignore images in the toplevel directory */ - if (subdir == NULL) - continue; + dot = strrchr (name, '.'); - retval = g_file_test (path, G_FILE_TEST_IS_REGULAR); - if (retval) - { - if (g_str_has_suffix (name, ".png")) - flags |= HAS_SUFFIX_PNG; - else if (g_str_has_suffix (name, ".svg")) - flags |= HAS_SUFFIX_SVG; - else if (g_str_has_suffix (name, ".xpm")) - flags |= HAS_SUFFIX_XPM; - else if (g_str_has_suffix (name, ".icon")) - flags |= HAS_ICON_FILE; + if (dot != NULL) + { + if (strcmp (dot, ".png") == 0) + flags = HAS_SUFFIX_PNG; + else if (strcmp (dot, ".svg") == 0) + flags = HAS_SUFFIX_SVG; + else if (strcmp (dot, ".xpm") == 0) + flags = HAS_SUFFIX_XPM; + else if (strcmp (dot, ".icon") == 0) + flags = HAS_ICON_FILE; + } - if (flags == 0) - continue; + if (flags != 0) + { + *dot = '\0'; - basename = g_strdup (name); - dot = strrchr (basename, '.'); - *dot = '\0'; + image = g_hash_table_lookup (dir_hash, name); + if (!image) + { + if (!dir_added) + { + dir_added = TRUE; + dir_index = g_list_length (directories); + directories = + g_list_append (directories, + g_strdup (subdir)); + } - image = g_hash_table_lookup (dir_hash, basename); - if (!image) - { - if (!dir_added) - { - dir_added = TRUE; - if (subdir) - { - dir_index = g_list_length (directories); - directories = g_list_append (directories, g_strdup (subdir)); - } - else - continue; - } + image = g_new0 (Image, 1); + image->dir_index = dir_index; + g_hash_table_insert (dir_hash, + g_strdup (name), + image); + } - image = g_new0 (Image, 1); - image->dir_index = dir_index; - g_hash_table_insert (dir_hash, g_strdup (basename), image); - } + image->flags |= flags; - image->flags |= flags; + maybe_cache_image_data (image, path); + maybe_cache_icon_data (image, path); - maybe_cache_image_data (image, path); - maybe_cache_icon_data (image, path); - - g_free (basename); - } + *dot = '.'; + } + } + } g_free (path); } - g_dir_close (dir); + g_ptr_array_free (names, TRUE); + g_free (dir_path); /* Move dir into the big file hash */ g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files); @@ -1375,6 +1409,34 @@ write_dir_index (FILE *cache, int offset, GList *directories) return TRUE; } +static void +free_hash_context (HashContext *context) +{ + gint i; + + for (i = 0; i < context->size; i++) + { + HashNode *node = context->nodes[i]; + + while (node) + { + HashNode *next = node->next; + GList *l; + + for (l = node->image_list; l; l = l->next) + g_free (l->data); + + g_list_free (node->image_list); + g_free (node->name); + g_free (node); + + node = next; + } + } + + g_free (context->nodes); +} + static gboolean write_file (FILE *cache, GHashTable *files, GList *directories) { @@ -1418,6 +1480,11 @@ write_file (FILE *cache, GHashTable *files, GList *directories) return FALSE; } + g_hash_table_destroy (string_pool); + string_pool = NULL; + + free_hash_context (&context); + return TRUE; } @@ -1544,6 +1611,8 @@ opentmp: exit (1); } + g_hash_table_destroy (files); + if (!safe_fclose (cache)) { g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno)); @@ -1623,6 +1692,9 @@ opentmp: if (!quiet) g_printerr (_("Cache file created successfully.\n")); + + g_free (tmp_cache_path); + g_free (cache_path); } static void @@ -1658,6 +1730,9 @@ write_csource (const gchar *path) } g_printf ("0x%02x\n};\n", (guint8)data[i]); + + g_free (data); + g_free (cache_path); } static GOptionEntry args[] = { @@ -1721,6 +1796,7 @@ main (int argc, char **argv) g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE); g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); path = argv[1]; #ifdef G_OS_WIN32 From 09131ada3a5fd6bd165497de75f34718890f74d1 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Wed, 12 Aug 2026 23:17:34 +0200 Subject: [PATCH 41/45] fontsel: reduce font list sorting overhead Precompute locale collation keys for font families instead of repeatedly collating names from the qsort comparator. Describe each font face once before sorting rather than allocating and freeing descriptions for every comparison, and populate the list stores with insert_with_values. Keep selection callbacks immediate and preserve GTK2-NG's restricted translation of standard font style names. This reduces FontSelection creation work substantially while keeping the resulting family, face and size models unchanged. --- gtk/gtkfontsel.c | 144 +++++++++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 54 deletions(-) diff --git a/gtk/gtkfontsel.c b/gtk/gtkfontsel.c index 28ede4eb4..6bc2ce7ae 100644 --- a/gtk/gtkfontsel.c +++ b/gtk/gtkfontsel.c @@ -819,13 +819,22 @@ gtk_font_selection_select_font (GtkTreeSelection *selection, } } -static int -cmp_families (const void *a, const void *b) +typedef struct { - const char *a_name = pango_font_family_get_name (*(PangoFontFamily **)a); - const char *b_name = pango_font_family_get_name (*(PangoFontFamily **)b); + PangoFontFamily *family; + const gchar *name; + gchar *collate_key; +} FontFamilySortInfo; - return g_utf8_collate (a_name, b_name); +static int +cmp_families (const void *a, + const void *b) +{ + const FontFamilySortInfo *family_a = a; + const FontFamilySortInfo *family_b = b; + + return strcmp (family_a->collate_key, + family_b->collate_key); } static void @@ -833,6 +842,7 @@ gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel) { GtkListStore *model; PangoFontFamily **families; + FontFamilySortInfo *sorted_families; PangoFontFamily *match_family = NULL; gint n_families, i; GtkTreeIter match_row; @@ -840,27 +850,37 @@ gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel) model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fontsel->family_list))); pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)), - &families, &n_families); - qsort (families, n_families, sizeof (PangoFontFamily *), cmp_families); + &families, &n_families); + + sorted_families = g_new (FontFamilySortInfo, n_families); + for (i = 0; i < n_families; i++) + { + const gchar *name = pango_font_family_get_name (families[i]); + + sorted_families[i].family = families[i]; + sorted_families[i].name = name; + sorted_families[i].collate_key = g_utf8_collate_key (name ? name : "", -1); + } + + qsort (sorted_families, n_families, sizeof (FontFamilySortInfo), cmp_families); gtk_list_store_clear (model); - for (i=0; ifamily_list), &match_row); #ifdef INCLUDE_FONT_ENTRIES gtk_entry_set_text (GTK_ENTRY (fontsel->font_entry), - pango_font_family_get_name (match_family)); + pango_font_family_get_name (match_family)); #endif /* INCLUDE_FONT_ENTRIES */ } + for (i = 0; i < n_families; i++) + g_free (sorted_families[i].collate_key); + + g_free (sorted_families); g_free (families); } @@ -898,18 +922,22 @@ compare_font_descriptions (const PangoFontDescription *a, const PangoFontDescrip return 0; } -static int -faces_sort_func (const void *a, const void *b) +typedef struct { - PangoFontDescription *desc_a = pango_font_face_describe (*(PangoFontFace **)a); - PangoFontDescription *desc_b = pango_font_face_describe (*(PangoFontFace **)b); + PangoFontFace *face; + PangoFontDescription *desc; + const gchar *name; +} FontFaceSortInfo; - int ord = compare_font_descriptions (desc_a, desc_b); +static int +faces_sort_func (const void *a, + const void *b) +{ + const FontFaceSortInfo *face_a = a; + const FontFaceSortInfo *face_b = b; - pango_font_description_free (desc_a); - pango_font_description_free (desc_b); - - return ord; + return compare_font_descriptions (face_a->desc, + face_b->desc); } static gboolean @@ -929,6 +957,7 @@ gtk_font_selection_show_available_styles (GtkFontSelection *fontsel) { gint n_faces, i; PangoFontFace **faces; + FontFaceSortInfo *sorted_faces; PangoFontDescription *old_desc; GtkListStore *model; GtkTreeIter match_row; @@ -942,38 +971,40 @@ gtk_font_selection_show_available_styles (GtkFontSelection *fontsel) old_desc= NULL; pango_font_family_list_faces (fontsel->family, &faces, &n_faces); - qsort (faces, n_faces, sizeof (PangoFontFace *), faces_sort_func); + + sorted_faces = g_new (FontFaceSortInfo, n_faces); + for (i = 0; i < n_faces; i++) + { + sorted_faces[i].face = faces[i]; + sorted_faces[i].desc = pango_font_face_describe (faces[i]); + sorted_faces[i].name = pango_font_face_get_face_name (faces[i]); + } + + qsort (sorted_faces, n_faces, sizeof (FontFaceSortInfo), faces_sort_func); gtk_list_store_clear (model); - for (i=0; i < n_faces; i++) + for (i = 0; i < n_faces; i++) { GtkTreeIter iter; - const gchar *str = pango_font_face_get_face_name (faces[i]); + const gchar *str = sorted_faces[i].name; - gtk_list_store_append (model, &iter); - gtk_list_store_set (model, &iter, - FACE_COLUMN, faces[i], - FACE_NAME_COLUMN, _gtk_font_style_name_translate (str), - -1); + gtk_list_store_insert_with_values (model, &iter, -1, + FACE_COLUMN, sorted_faces[i].face, + FACE_NAME_COLUMN, _gtk_font_style_name_translate (str), + -1); if (i == 0) - { - match_row = iter; - match_face = faces[i]; - } - else if (old_desc) - { - PangoFontDescription *tmp_desc = pango_font_face_describe (faces[i]); - - if (font_description_style_equal (tmp_desc, old_desc)) - { - match_row = iter; - match_face = faces[i]; - } - - pango_font_description_free (tmp_desc); - } + { + match_row = iter; + match_face = sorted_faces[i].face; + } + else if (old_desc && + font_description_style_equal (sorted_faces[i].desc, old_desc)) + { + match_row = iter; + match_face = sorted_faces[i].face; + } } if (old_desc) @@ -990,6 +1021,10 @@ gtk_font_selection_show_available_styles (GtkFontSelection *fontsel) set_cursor_to_iter (GTK_TREE_VIEW (fontsel->face_list), &match_row); } + for (i = 0; i < n_faces; i++) + pango_font_description_free (sorted_faces[i].desc); + + g_free (sorted_faces); g_free (faces); } @@ -1060,8 +1095,9 @@ gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel, { GtkTreeIter iter; - gtk_list_store_append (model, &iter); - gtk_list_store_set (model, &iter, SIZE_COLUMN, font_sizes[i], -1); + gtk_list_store_insert_with_values (model, &iter, -1, + SIZE_COLUMN, font_sizes[i], + -1); if (font_sizes[i] * PANGO_SCALE == fontsel->size) set_cursor_to_iter (GTK_TREE_VIEW (fontsel->size_list), &iter); From 6ad2ee66d9992896e997e05d29306db19110e5ee Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Thu, 13 Aug 2026 00:06:32 +0200 Subject: [PATCH 42/45] gtkfilesystemmodel: avoid sorting newly queried files twice Remember the number of model nodes before updating asynchronously queried file information. If update_file added a previously unknown file, add_file already sorted the model, so skip the redundant second full sort. Existing files still trigger the required resort after their metadata changes. Using the node count also avoids the additional node lookup required by the original warning-cleanups implementation. --- gtk/gtkfilesystemmodel.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c index 52fd17a08..4c619c2a3 100644 --- a/gtk/gtkfilesystemmodel.c +++ b/gtk/gtkfilesystemmodel.c @@ -1230,7 +1230,7 @@ gtk_file_system_model_query_done (GObject * object, GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */ GFile *file = G_FILE (object); GFileInfo *info; - guint id; + guint n_files; info = g_file_query_info_finish (file, res, NULL); if (info == NULL) @@ -1238,10 +1238,12 @@ gtk_file_system_model_query_done (GObject * object, gdk_threads_enter (); + n_files = model->files->len; + _gtk_file_system_model_update_file (model, file, info); - id = node_get_for_file (model, file); - gtk_file_system_model_sort_node (model, id); + if (model->files->len == n_files) + gtk_file_system_model_sort (model); g_object_unref (info); From 240b7d0a71d887dff2fb03515255cc272fcfa2e4 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Thu, 13 Aug 2026 00:11:57 +0200 Subject: [PATCH 43/45] gail: avoid formatting string attribute values Duplicate Pango string attribute values directly instead of routing an unchanged string through g_strdup_printf. This preserves the allocated result while avoiding unnecessary format parsing and varargs handling. --- modules/other/gail/libgail-util/gailmisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/other/gail/libgail-util/gailmisc.c b/modules/other/gail/libgail-util/gailmisc.c index 9dc143d07..8213c716a 100644 --- a/modules/other/gail/libgail-util/gailmisc.c +++ b/modules/other/gail/libgail-util/gailmisc.c @@ -251,7 +251,7 @@ gail_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set, if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY)) != NULL) { - value = g_strdup_printf("%s", pango_string->value); + value = g_strdup (pango_string->value); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value); From 6d7bb4c86afb6f2363da3604a2d1544fc77092e7 Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Thu, 13 Aug 2026 00:55:35 +0200 Subject: [PATCH 44/45] gtkcalendar: preserve strftime calendar names Format weekday and month names with strftime() and convert the locale-encoded results to UTF-8 instead of creating temporary GDateTime objects during calendar initialization. Keep the corrected Sunday-to-Saturday weekday ordering. If %OB is unsupported, immediately reformat January with %B so the fallback also preserves the first month. --- gtk/gtkcalendar.c | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index 2e453db04..e6f6be63f 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -688,8 +688,7 @@ gtk_calendar_init (GtkCalendar *calendar) #else static const char *month_format = NULL; char buffer[255]; - GDateTime *tmp_dt; - gchar *tmp_str; + time_t tmp_time; #endif GtkCalendarPrivate *priv; gchar *year_before; @@ -710,15 +709,9 @@ gtk_calendar_init (GtkCalendar *calendar) for (i=0; i<7; i++) { #ifndef G_OS_WIN32 - tmp_dt = g_date_time_new_utc (2001, 1, i + 7, 12, 0, 0.0); - if (tmp_dt) - { - tmp_str = g_date_time_format (tmp_dt, "%a"); - default_abbreviated_dayname[i] = tmp_str ? tmp_str : g_strdup (""); - g_date_time_unref (tmp_dt); - } - else - default_abbreviated_dayname[i] = g_strdup (""); + tmp_time = (i + 3) * 86400; + strftime (buffer, sizeof (buffer), "%a", gmtime (&tmp_time)); + default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL); #else if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7, wbuffer, G_N_ELEMENTS (wbuffer))) @@ -732,30 +725,24 @@ gtk_calendar_init (GtkCalendar *calendar) for (i=0; i<12; i++) { #ifndef G_OS_WIN32 - tmp_dt = g_date_time_new_utc (2001, i + 1, 1, 12, 0, 0.0); + tmp_time = i * 2764800; if (G_UNLIKELY (month_format == NULL)) { buffer[0] = '\0'; month_format = "%OB"; - tmp_str = tmp_dt ? g_date_time_format (tmp_dt, month_format) : NULL; - if (tmp_str) - { - g_strlcpy (buffer, tmp_str, sizeof (buffer)); - g_free (tmp_str); - } + strftime (buffer, sizeof (buffer), month_format, gmtime (&tmp_time)); + /* "%OB" is not supported in Linux with glibc < 2.27 */ if (!strcmp (buffer, "%OB") || !strcmp (buffer, "OB") || !strcmp (buffer, "")) - month_format = "%B"; - } - - if (tmp_dt) - { - tmp_str = g_date_time_format (tmp_dt, month_format); - default_monthname[i] = tmp_str ? tmp_str : g_strdup (""); - g_date_time_unref (tmp_dt); + { + month_format = "%B"; + strftime (buffer, sizeof (buffer), month_format, gmtime (&tmp_time)); + } } else - default_monthname[i] = g_strdup (""); + strftime (buffer, sizeof (buffer), month_format, gmtime (&tmp_time)); + + default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL); #else if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i, wbuffer, G_N_ELEMENTS (wbuffer))) From fc608281a2f0b466e27a92a268713a6c69fa217e Mon Sep 17 00:00:00 2001 From: Daemonratte Date: Tue, 18 Aug 2026 19:49:23 +0200 Subject: [PATCH 45/45] build: support glibtoolize on Darwin --- autogen.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index dac175742..e055be4b1 100755 --- a/autogen.sh +++ b/autogen.sh @@ -12,7 +12,11 @@ FILE=gdk DIE=0 -if libtoolize --version < /dev/null > /dev/null 2>&1 ; then : ; else +if libtoolize --version < /dev/null > /dev/null 2>&1 ; then + LIBTOOLIZE=libtoolize +elif glibtoolize --version < /dev/null > /dev/null 2>&1 ; then + LIBTOOLIZE=glibtoolize +else echo echo "You must have libtool installed to compile $PROJECT." echo "Install the appropriate package for your distribution," @@ -138,7 +142,7 @@ rm -rf autom4te.cache $ACLOCAL -I m4 $ACLOCAL_FLAGS || exit $? -libtoolize --copy --force || exit $? +$LIBTOOLIZE --copy --force || exit $? gtkdocize --copy || exit $? autoheader || exit $?