diff --git a/0001-Add-gnome-qr-library.patch b/0001-Add-gnome-qr-library.patch new file mode 100644 index 0000000..0f34618 --- /dev/null +++ b/0001-Add-gnome-qr-library.patch @@ -0,0 +1,2294 @@ +From 5b0d028d35bfff54dd68c8b157bf34ac47532c75 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 27 Nov 2025 18:03:16 +0100 +Subject: [PATCH 01/20] meson: Add QR-Code-generator as a subproject + +Will be used by next commit to generate QR codes +--- + .gitignore | 1 + + meson.build | 3 +++ + subprojects/packagefiles/qrcodegen/meson.build | 10 ++++++++++ + subprojects/qrcodegen.wrap | 5 +++++ + 4 files changed, 19 insertions(+) + create mode 100644 .gitignore + create mode 100644 subprojects/packagefiles/qrcodegen/meson.build + create mode 100644 subprojects/qrcodegen.wrap + +diff --git a/.gitignore b/.gitignore +new file mode 100644 +index 00000000..97b1a628 +--- /dev/null ++++ b/.gitignore +@@ -0,0 +1 @@ ++subprojects/qrcodegen +diff --git a/meson.build b/meson.build +index 0c3c7c1f..a4876a84 100644 +--- a/meson.build ++++ b/meson.build +@@ -91,6 +91,9 @@ if not cc.has_function('clock_gettime', dependencies: rt_dep) + rt_dep = dependency('', required: false) + endif + ++qrcodegen = subproject('qrcodegen') ++qrcodegen_dep = qrcodegen.get_variable('qrcodegen_dep') ++ + subdir('libgnome-desktop') + subdir('po') + subdir('tests') +diff --git a/subprojects/packagefiles/qrcodegen/meson.build b/subprojects/packagefiles/qrcodegen/meson.build +new file mode 100644 +index 00000000..0a66ccb5 +--- /dev/null ++++ b/subprojects/packagefiles/qrcodegen/meson.build +@@ -0,0 +1,10 @@ ++project('qrcodegen', 'c') ++ ++libqrcodegen = static_library('qrcodegen', ++ 'c/qrcodegen.c', ++ include_directories: include_directories('c') ++) ++qrcodegen_dep = declare_dependency( ++ link_with: libqrcodegen, ++ include_directories: include_directories('c') ++) +diff --git a/subprojects/qrcodegen.wrap b/subprojects/qrcodegen.wrap +new file mode 100644 +index 00000000..9080ca48 +--- /dev/null ++++ b/subprojects/qrcodegen.wrap +@@ -0,0 +1,5 @@ ++[wrap-git] ++url = https://github.com/nayuki/QR-Code-generator.git ++revision = head ++depth = 1 ++patch_directory = qrcodegen +-- +2.52.0 + + +From 19f107f0695db1bb685aa644191439e9f287c7ff Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 20 Nov 2025 19:54:34 +0100 +Subject: [PATCH 02/20] Introduce gnome-qr library + +g-c-c and now gnome-shell need to generate QR codes. + +Originaly g-c-c needed to create QR codes and used a copy of the qrcodegen +library implementation to avoid dependencies. + +Now gnome-shell needs to generate QR codes too, so it makes sense to +port the g-c-c QR code generation to libgnome-desktop. +--- + .../gnome-desktop3-sections.txt | 14 + + libgnome-desktop/gnome-qr/gnome-qr.c | 306 ++++++++++++++++++ + libgnome-desktop/gnome-qr/gnome-qr.h | 125 +++++++ + libgnome-desktop/gnome-qr/meson.build | 41 +++ + libgnome-desktop/meson.build | 1 + + 5 files changed, 487 insertions(+) + create mode 100644 libgnome-desktop/gnome-qr/gnome-qr.c + create mode 100644 libgnome-desktop/gnome-qr/gnome-qr.h + create mode 100644 libgnome-desktop/gnome-qr/meson.build + +diff --git a/docs/reference/gnome-desktop3/gnome-desktop3-sections.txt b/docs/reference/gnome-desktop3/gnome-desktop3-sections.txt +index 4f189fcd..0b6b6ec6 100644 +--- a/docs/reference/gnome-desktop3/gnome-desktop3-sections.txt ++++ b/docs/reference/gnome-desktop3/gnome-desktop3-sections.txt +@@ -359,3 +359,17 @@ GNOME_TYPE_PNP_IDS + GnomePnpIdsPrivate + gnome_pnp_ids_get_type + ++ ++
++gnome-qr ++QR Code Generation ++GnomeQrEccLevel ++GnomeQrPixelFormat ++GnomeQrColor ++GNOME_QR_BYTES_PER_FORMAT ++GNOME_QR_COLOR_WHITE ++GNOME_QR_COLOR_BLACK ++gnome_qr_generate_qr_code_sync ++gnome_qr_generate_qr_code_async ++gnome_qr_generate_qr_code_finish ++
+diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +new file mode 100644 +index 00000000..e22bb1f4 +--- /dev/null ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -0,0 +1,306 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* ++ * Copyright 2024-2025 Red Hat, Inc ++ * Copyright 2024-2025 Canonical Ltd ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * ++ * 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.1 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. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ * ++ * Author: Ray Strode ++ * Marco Trevisan ++ * Joan Torres Lopez ++ */ ++ ++#include "config.h" ++ ++#include "gnome-qr.h" ++ ++#include ++ ++typedef struct ++{ ++ char *text; ++ size_t size; ++ size_t out_size; ++ GnomeQrColor *bg_color; ++ GnomeQrColor *fg_color; ++ GnomeQrPixelFormat format; ++ GnomeQrEccLevel ecc; ++} GnomeQrCodeData; ++ ++static void ++gnome_qr_code_data_free (GnomeQrCodeData *data) ++{ ++ g_clear_pointer (&data->text, g_free); ++ g_clear_pointer (&data->bg_color, g_free); ++ g_clear_pointer (&data->fg_color, g_free); ++ g_free (data); ++} ++ ++static enum qrcodegen_Ecc ++gnome_qr_ecc_level_to_qrcodegen_ecc (GnomeQrEccLevel ecc) ++{ ++ switch (ecc) { ++ case GNOME_QR_ECC_LEVEL_LOW: ++ return qrcodegen_Ecc_LOW; ++ case GNOME_QR_ECC_LEVEL_MEDIUM: ++ return qrcodegen_Ecc_MEDIUM; ++ case GNOME_QR_ECC_LEVEL_QUARTILE: ++ return qrcodegen_Ecc_QUARTILE; ++ case GNOME_QR_ECC_LEVEL_HIGH: ++ return qrcodegen_Ecc_HIGH; ++ } ++ g_assert_not_reached (); ++} ++ ++static void ++fill_pixel (GByteArray *array, ++ const GnomeQrColor *color, ++ GnomeQrPixelFormat format, ++ int pixel_size) ++{ ++ guint i; ++ ++ for (i = 0; i < pixel_size; i++) { ++ g_byte_array_append (array, &color->red, 1); ++ g_byte_array_append (array, &color->green, 1); ++ g_byte_array_append (array, &color->blue, 1); ++ ++ if (format == GNOME_QR_PIXEL_FORMAT_RGBA_8888) ++ g_byte_array_append (array, &color->alpha, 1); ++ } ++} ++ ++/** ++ * gnome_qr_generate_qr_code_sync: ++ * @text: the text of which generate the qr code ++ * @size: The requested size in pixels (width and height) for the square QR code ++ * @bg_color: (nullable): The background color of the code ++ * or %NULL to use default (white) ++ * @fg_color: (nullable): The foreground color of the code ++ * or %NULL to use default (black) ++ * @format: The pixel format for the output image data ++ * @ecc: The error correction level ++ * @out_size: (out) (nullable): location to store the actual output size in pixels (width and height) of the square QR code, or %NULL ++ * @cancellable: (nullable): A #GCancellable to cancel the operation ++ * @error: #GError for error reporting ++ * ++ * Generates the QrCode synchronously. ++ * ++ * Returns: (transfer full): The pixel data or %NULL on error ++ */ ++GBytes * ++gnome_qr_generate_qr_code_sync (const char *text, ++ size_t size, ++ const GnomeQrColor *bg_color, ++ const GnomeQrColor *fg_color, ++ GnomeQrPixelFormat format, ++ GnomeQrEccLevel ecc, ++ size_t *out_size, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_autoptr (GByteArray) qr_matrix = NULL; ++ uint8_t qr_code[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; ++ uint8_t temp_buf[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; ++ gint pixel_size, qr_size, total_size; ++ gint column, row, i; ++ ++ g_return_val_if_fail (text != NULL, NULL); ++ g_return_val_if_fail (size > 0, NULL); ++ ++ if (format == GNOME_QR_PIXEL_FORMAT_RGB_888) { ++ g_return_val_if_fail (!bg_color || bg_color->alpha == 255, NULL); ++ g_return_val_if_fail (!fg_color || fg_color->alpha == 255, NULL); ++ } ++ ++ if (g_cancellable_set_error_if_cancelled (cancellable, error)) ++ return NULL; ++ ++ if (!qrcodegen_encodeText (text, ++ temp_buf, ++ qr_code, ++ gnome_qr_ecc_level_to_qrcodegen_ecc (ecc), ++ qrcodegen_VERSION_MIN, ++ qrcodegen_VERSION_MAX, ++ qrcodegen_Mask_AUTO, ++ FALSE)) { ++ g_set_error (error, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "QRCode generation failed for content %s", ++ text); ++ return NULL; ++ } ++ ++ if (g_cancellable_set_error_if_cancelled (cancellable, error)) ++ return NULL; ++ ++ if (!bg_color) ++ bg_color = &GNOME_QR_COLOR_WHITE; ++ ++ if (!fg_color) ++ fg_color = &GNOME_QR_COLOR_BLACK; ++ ++ qr_size = qrcodegen_getSize (qr_code); ++ pixel_size = MAX (1, size / qr_size); ++ total_size = qr_size * pixel_size; ++ ++ qr_matrix = g_byte_array_sized_new (total_size * total_size * pixel_size * ++ GNOME_QR_BYTES_PER_FORMAT (format)); ++ ++ for (column = 0; column < total_size; ++column) { ++ for (i = 0; i < pixel_size; ++i) { ++ for (row = 0; row < qr_size; ++row) { ++ if (qrcodegen_getModule (qr_code, column, row)) ++ fill_pixel (qr_matrix, fg_color, format, pixel_size); ++ else ++ fill_pixel (qr_matrix, bg_color, format, pixel_size); ++ } ++ } ++ ++ if (g_cancellable_set_error_if_cancelled (cancellable, error)) ++ return NULL; ++ } ++ ++ if (out_size) ++ *out_size = total_size; ++ ++ return g_byte_array_free_to_bytes (g_steal_pointer (&qr_matrix)); ++} ++ ++ ++static void ++generate_qr_code_in_thread (GTask *task, ++ gpointer source, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GnomeQrCodeData *data = task_data; ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GBytes) icon = NULL; ++ ++ icon = gnome_qr_generate_qr_code_sync (data->text, ++ data->size, ++ data->bg_color, ++ data->fg_color, ++ data->format, ++ data->ecc, ++ &data->out_size, ++ cancellable, ++ &error); ++ if (!icon) { ++ g_task_return_error (task, g_steal_pointer (&error)); ++ return; ++ } ++ ++ g_task_return_pointer (task, g_steal_pointer (&icon), (GDestroyNotify) g_bytes_unref); ++} ++ ++/** ++ * gnome_qr_generate_qr_code_async: ++ * @text: the text of which generate the qr code ++ * @size: The requested size in pixels (width and height) for the square QR code ++ * @bg_color: (nullable): The background color of the code ++ * or %NULL to use default (white) ++ * @fg_color: (nullable): The foreground color of the code ++ * or %NULL to use default (black) ++ * @format: The pixel format for the output image data ++ * @ecc: The error correction level ++ * @cancellable: (nullable): A #GCancellable to cancel the operation ++ * @callback: (scope async): function to call returning success or failure ++ * of the async grabbing ++ * @user_data: the data to pass to callback function ++ * ++ * Generates the QrCode asynchronously. ++ * ++ * Use gnome_qr_generate_qr_code_finish() to complete it. ++ */ ++void ++gnome_qr_generate_qr_code_async (const char *text, ++ size_t size, ++ const GnomeQrColor *bg_color, ++ const GnomeQrColor *fg_color, ++ GnomeQrPixelFormat format, ++ GnomeQrEccLevel ecc, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ g_autoptr (GTask) task = NULL; ++ GnomeQrCodeData *data; ++ ++ if (!text || *text == '\0') { ++ if (callback) ++ g_task_report_new_error (NULL, ++ callback, ++ user_data, ++ gnome_qr_generate_qr_code_async, ++ G_IO_ERROR, ++ G_IO_ERROR_INVALID_DATA, ++ "No valid QR code text is provided"); ++ return; ++ } ++ ++ data = g_new0 (GnomeQrCodeData, 1); ++ data->text = g_strdup (text); ++ data->size = size; ++ data->bg_color = g_memdup2 (bg_color, sizeof (GnomeQrColor)); ++ data->fg_color = g_memdup2 (fg_color, sizeof (GnomeQrColor)); ++ data->format = format; ++ data->ecc = ecc; ++ ++ task = g_task_new (NULL, cancellable, callback, user_data); ++ g_task_set_source_tag (task, gnome_qr_generate_qr_code_async); ++ g_task_set_task_data (task, data, ++ (GDestroyNotify) gnome_qr_code_data_free); ++ g_task_set_return_on_cancel (task, TRUE); ++ g_task_run_in_thread (task, generate_qr_code_in_thread); ++} ++ ++/** ++ * gnome_qr_generate_qr_code_finish: ++ * @result: the #GAsyncResult that was provided to the callback ++ * @out_size: (out) (nullable): location to store the actual output size in pixels (width and height) of the square QR code, or %NULL ++ * @error: #GError for error reporting ++ * ++ * Finish the asynchronous operation started by ++ * gnome_qr_generate_qr_code_async() and obtain its result. ++ * ++ * Returns: (transfer full): The pixel data or %NULL on error ++ */ ++GBytes * ++gnome_qr_generate_qr_code_finish (GAsyncResult *result, ++ size_t *out_size, ++ GError **error) ++{ ++ GnomeQrCodeData *data; ++ g_autoptr (GBytes) pixel_data = NULL; ++ ++ g_return_val_if_fail (G_IS_TASK (result), FALSE); ++ g_return_val_if_fail (g_async_result_is_tagged (result, ++ gnome_qr_generate_qr_code_async), ++ NULL); ++ ++ pixel_data = g_task_propagate_pointer (G_TASK (result), error); ++ if (!pixel_data) ++ return NULL; ++ ++ data = g_task_get_task_data (G_TASK (result)); ++ if (out_size) ++ *out_size = data->out_size; ++ ++ return g_steal_pointer (&pixel_data); ++} +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.h b/libgnome-desktop/gnome-qr/gnome-qr.h +new file mode 100644 +index 00000000..4d89c59d +--- /dev/null ++++ b/libgnome-desktop/gnome-qr/gnome-qr.h +@@ -0,0 +1,125 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* ++ * Copyright 2024-2025 Red Hat, Inc ++ * Copyright 2024-2025 Canonical Ltd ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * ++ * 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.1 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. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ * ++ * Author: Ray Strode ++ * Marco Trevisan ++ * Joan Torres Lopez ++ */ ++ ++#pragma once ++ ++#include ++#include ++#include ++ ++/** ++ * GNOME_QR_BYTES_PER_FORMAT: ++ * @format: a #GnomeQrPixelFormat ++ * ++ * Calculates the number of bytes per pixel for a given pixel format. ++ * ++ * Returns: the number of bytes ++ */ ++#define GNOME_QR_BYTES_PER_FORMAT(format) \ ++ ((format) == GNOME_QR_PIXEL_FORMAT_RGB_888 ? 3 : 4) ++ ++/** ++ * GNOME_QR_COLOR_WHITE: ++ * ++ * Predefined white color (255, 255, 255, 255). ++ */ ++#define GNOME_QR_COLOR_WHITE ((GnomeQrColor) { 255, 255, 255, 255 }) ++ ++/** ++ * GNOME_QR_COLOR_BLACK: ++ * ++ * Predefined black color (0, 0, 0, 255). ++ */ ++#define GNOME_QR_COLOR_BLACK ((GnomeQrColor) { 0, 0, 0, 255 }) ++ ++/** ++ * GnomeQrEccLevel: ++ * @GNOME_QR_ECC_LEVEL_LOW: Low error correction (~7% recovery capability) ++ * @GNOME_QR_ECC_LEVEL_MEDIUM: Medium error correction (~15% recovery capability) ++ * @GNOME_QR_ECC_LEVEL_QUARTILE: Quartile error correction (~25% recovery capability) ++ * @GNOME_QR_ECC_LEVEL_HIGH: High error correction (~30% recovery capability) ++ * ++ * Error correction levels for QR codes. Higher levels provide better error ++ * recovery at the cost of requiring bigger dimensions. ++ */ ++typedef enum { ++ GNOME_QR_ECC_LEVEL_LOW, ++ GNOME_QR_ECC_LEVEL_MEDIUM, ++ GNOME_QR_ECC_LEVEL_QUARTILE, ++ GNOME_QR_ECC_LEVEL_HIGH, ++} GnomeQrEccLevel; ++ ++/** ++ * GnomeQrPixelFormat: ++ * @GNOME_QR_PIXEL_FORMAT_RGB_888: 24-bit RGB format (3 bytes per pixel) ++ * @GNOME_QR_PIXEL_FORMAT_RGBA_8888: 32-bit RGBA format (4 bytes per pixel) ++ * ++ * Pixel formats for the generated QR code image data. ++ */ ++typedef enum { ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888, ++} GnomeQrPixelFormat; ++ ++/** ++ * GnomeQrColor: ++ * @red: Red component (0-255) ++ * @green: Green component (0-255) ++ * @blue: Blue component (0-255) ++ * @alpha: Alpha component (0-255, where 255 is fully opaque). ++ * Must be 255 when using %GNOME_QR_PIXEL_FORMAT_RGB_888. ++ * ++ * Represents an RGBA color for QR code generation. ++ */ ++typedef struct { ++ guint8 red; ++ guint8 green; ++ guint8 blue; ++ guint8 alpha; ++} GnomeQrColor; ++ ++GBytes * gnome_qr_generate_qr_code_sync (const char *text, ++ size_t size, ++ const GnomeQrColor *bg_color, ++ const GnomeQrColor *fg_color, ++ GnomeQrPixelFormat format, ++ GnomeQrEccLevel ecc, ++ size_t *out_size, ++ GCancellable *cancellable, ++ GError **error); ++ ++void gnome_qr_generate_qr_code_async (const char *text, ++ size_t size, ++ const GnomeQrColor *bg_color, ++ const GnomeQrColor *fg_color, ++ GnomeQrPixelFormat format, ++ GnomeQrEccLevel ecc, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++GBytes * gnome_qr_generate_qr_code_finish (GAsyncResult *result, ++ size_t *out_size, ++ GError **error); +diff --git a/libgnome-desktop/gnome-qr/meson.build b/libgnome-desktop/gnome-qr/meson.build +new file mode 100644 +index 00000000..f8ff3068 +--- /dev/null ++++ b/libgnome-desktop/gnome-qr/meson.build +@@ -0,0 +1,41 @@ ++libgnome_qr_deps = [ ++ glib_dep, ++ gio_dep, ++ qrcodegen_dep, ++] ++ ++libgnome_qr_sources = files([ ++ 'gnome-qr.c', ++ 'gnome-qr.h', ++]) ++ ++libgnome_qr_headers = files([ ++ 'gnome-qr.h', ++]) ++ ++install_headers(libgnome_qr_headers, ++ subdir: 'gnome-desktop-4.0/gnome-qr' ++) ++ ++libgnome_qr = library('gnome-qr-4', ++ sources: libgnome_qr_sources, ++ dependencies: libgnome_qr_deps, ++ soversion: soversion, ++ version: libversion, ++ c_args: libargs, ++ install: true, ++ include_directories: [ ++ include_directories('.'), ++ include_directories('..'), ++ ], ++) ++ ++pkg.generate( ++ libgnome_qr, ++ requires: ['gsettings-desktop-schemas'], ++ version: meson.project_version(), ++ name: 'gnome-qr-4', ++ filebase: 'gnome-qr-4', ++ description: 'Qr code utility library for GNOME desktop components', ++ subdirs: 'gnome-desktop-4.0', ++) +diff --git a/libgnome-desktop/meson.build b/libgnome-desktop/meson.build +index 708a4ae8..cbf3fc74 100644 +--- a/libgnome-desktop/meson.build ++++ b/libgnome-desktop/meson.build +@@ -167,6 +167,7 @@ if get_option('build_gtk4') + + subdir('gnome-bg') + subdir('gnome-rr') ++ subdir('gnome-qr') + endif + + ### Legacy ### +-- +2.52.0 + + +From 6a8a8455d857331b47aa35210fcc2bc5c7db2773 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 4 Dec 2025 22:07:57 +0100 +Subject: [PATCH 03/20] subprojects/qrcodegen: Pin the library version + +We cannot just allow any revision to be picked by meson without some +human review, so let's just lock the version to the latest that we +checked rather than just accept any new version +--- + subprojects/qrcodegen.wrap | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/subprojects/qrcodegen.wrap b/subprojects/qrcodegen.wrap +index 9080ca48..9421ab21 100644 +--- a/subprojects/qrcodegen.wrap ++++ b/subprojects/qrcodegen.wrap +@@ -1,5 +1,5 @@ + [wrap-git] + url = https://github.com/nayuki/QR-Code-generator.git +-revision = head ++revision = 8329a7108fc22be3e1eec0a9f9318978579e3621 + depth = 1 + patch_directory = qrcodegen +-- +2.52.0 + + +From ab22410332d89074e9c63635c32ab877a68d9a50 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 4 Dec 2025 22:27:52 +0100 +Subject: [PATCH 04/20] subprojects/qrcodegen: Move meson overlays to the + proper folders + +So we don't have to repeat it +--- + subprojects/packagefiles/qrcodegen/c/meson.build | 8 ++++++++ + subprojects/packagefiles/qrcodegen/meson.build | 9 +-------- + 2 files changed, 9 insertions(+), 8 deletions(-) + create mode 100644 subprojects/packagefiles/qrcodegen/c/meson.build + +diff --git a/subprojects/packagefiles/qrcodegen/c/meson.build b/subprojects/packagefiles/qrcodegen/c/meson.build +new file mode 100644 +index 00000000..1f819005 +--- /dev/null ++++ b/subprojects/packagefiles/qrcodegen/c/meson.build +@@ -0,0 +1,8 @@ ++libqrcodegen = static_library('qrcodegen', ++ 'qrcodegen.c', ++) ++ ++qrcodegen_dep = declare_dependency( ++ link_with: libqrcodegen, ++ include_directories: include_directories('.') ++) +diff --git a/subprojects/packagefiles/qrcodegen/meson.build b/subprojects/packagefiles/qrcodegen/meson.build +index 0a66ccb5..e2ba7785 100644 +--- a/subprojects/packagefiles/qrcodegen/meson.build ++++ b/subprojects/packagefiles/qrcodegen/meson.build +@@ -1,10 +1,3 @@ + project('qrcodegen', 'c') + +-libqrcodegen = static_library('qrcodegen', +- 'c/qrcodegen.c', +- include_directories: include_directories('c') +-) +-qrcodegen_dep = declare_dependency( +- link_with: libqrcodegen, +- include_directories: include_directories('c') +-) ++subdir('c') +-- +2.52.0 + + +From 643d5616fce9ad77a256b249266ac297e1e877b8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 4 Dec 2025 22:40:00 +0100 +Subject: [PATCH 05/20] subprojects/qrcodegen: Run upstream tests + +--- + subprojects/packagefiles/qrcodegen/c/meson.build | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/subprojects/packagefiles/qrcodegen/c/meson.build b/subprojects/packagefiles/qrcodegen/c/meson.build +index 1f819005..ca221bca 100644 +--- a/subprojects/packagefiles/qrcodegen/c/meson.build ++++ b/subprojects/packagefiles/qrcodegen/c/meson.build +@@ -6,3 +6,13 @@ qrcodegen_dep = declare_dependency( + link_with: libqrcodegen, + include_directories: include_directories('.') + ) ++ ++test('qrcodegen', ++ executable('qrcodegen-test', ++ sources: [ ++ 'qrcodegen.c', ++ 'qrcodegen-test.c', ++ ], ++ c_args: ['-DQRCODEGEN_TEST'], ++ ), ++) +-- +2.52.0 + + +From b834c23a3580ba1fc32eb34ed57f16e8aa111b0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 4 Dec 2025 22:46:10 +0100 +Subject: [PATCH 06/20] gnome-qr: Always use spaces for indentation + +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 6 +++--- + libgnome-desktop/gnome-qr/gnome-qr.h | 8 ++++---- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index e22bb1f4..59fd42e3 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -282,9 +282,9 @@ gnome_qr_generate_qr_code_async (const char *text, + * Returns: (transfer full): The pixel data or %NULL on error + */ + GBytes * +-gnome_qr_generate_qr_code_finish (GAsyncResult *result, +- size_t *out_size, +- GError **error) ++gnome_qr_generate_qr_code_finish (GAsyncResult *result, ++ size_t *pixel_size_out, ++ GError **error) + { + GnomeQrCodeData *data; + g_autoptr (GBytes) pixel_data = NULL; +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.h b/libgnome-desktop/gnome-qr/gnome-qr.h +index 4d89c59d..b95480c3 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.h ++++ b/libgnome-desktop/gnome-qr/gnome-qr.h +@@ -65,10 +65,10 @@ + * recovery at the cost of requiring bigger dimensions. + */ + typedef enum { +- GNOME_QR_ECC_LEVEL_LOW, +- GNOME_QR_ECC_LEVEL_MEDIUM, +- GNOME_QR_ECC_LEVEL_QUARTILE, +- GNOME_QR_ECC_LEVEL_HIGH, ++ GNOME_QR_ECC_LEVEL_LOW, ++ GNOME_QR_ECC_LEVEL_MEDIUM, ++ GNOME_QR_ECC_LEVEL_QUARTILE, ++ GNOME_QR_ECC_LEVEL_HIGH, + } GnomeQrEccLevel; + + /** +-- +2.52.0 + + +From 07955fdefa7bb0572b9415d52f0521642de9afdd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 4 Dec 2025 23:14:07 +0100 +Subject: [PATCH 07/20] gnome-qr: Mark the out_size as optional + +It's not nullable actually, but optional being an out value +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index 59fd42e3..5977842f 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -93,7 +93,8 @@ fill_pixel (GByteArray *array, + * or %NULL to use default (black) + * @format: The pixel format for the output image data + * @ecc: The error correction level +- * @out_size: (out) (nullable): location to store the actual output size in pixels (width and height) of the square QR code, or %NULL ++ * @out_size: (out) (optional): location to store the actual output size ++ * in pixels (width and height) of the square QR code, or %NULL + * @cancellable: (nullable): A #GCancellable to cancel the operation + * @error: #GError for error reporting + * +@@ -273,7 +274,8 @@ gnome_qr_generate_qr_code_async (const char *text, + /** + * gnome_qr_generate_qr_code_finish: + * @result: the #GAsyncResult that was provided to the callback +- * @out_size: (out) (nullable): location to store the actual output size in pixels (width and height) of the square QR code, or %NULL ++ * @out_size: (out) (optional): location to store the actual output size ++ * in pixels (width and height) of the square QR code, or %NULL + * @error: #GError for error reporting + * + * Finish the asynchronous operation started by +-- +2.52.0 + + +From 09fe6b43a56af35174f0d5bbeb591c04b1d918d9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 00:59:47 +0100 +Subject: [PATCH 08/20] gnome-qr: Clarify the input and output size values + +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 63 +++++++++++++++------------- + libgnome-desktop/gnome-qr/gnome-qr.h | 8 ++-- + 2 files changed, 38 insertions(+), 33 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index 5977842f..f2e7b152 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -32,8 +32,8 @@ + typedef struct + { + char *text; +- size_t size; +- size_t out_size; ++ size_t requested_size; ++ size_t pixel_size; + GnomeQrColor *bg_color; + GnomeQrColor *fg_color; + GnomeQrPixelFormat format; +@@ -66,14 +66,14 @@ gnome_qr_ecc_level_to_qrcodegen_ecc (GnomeQrEccLevel ecc) + } + + static void +-fill_pixel (GByteArray *array, ++fill_block (GByteArray *array, + const GnomeQrColor *color, + GnomeQrPixelFormat format, +- int pixel_size) ++ int block_size) + { + guint i; + +- for (i = 0; i < pixel_size; i++) { ++ for (i = 0; i < block_size; i++) { + g_byte_array_append (array, &color->red, 1); + g_byte_array_append (array, &color->green, 1); + g_byte_array_append (array, &color->blue, 1); +@@ -86,15 +86,17 @@ fill_pixel (GByteArray *array, + /** + * gnome_qr_generate_qr_code_sync: + * @text: the text of which generate the qr code +- * @size: The requested size in pixels (width and height) for the square QR code ++ * @requested_size: The requested size (width and height) in pixels of the QR code. ++ * Only square QR codes are supported. + * @bg_color: (nullable): The background color of the code + * or %NULL to use default (white) + * @fg_color: (nullable): The foreground color of the code + * or %NULL to use default (black) + * @format: The pixel format for the output image data + * @ecc: The error correction level +- * @out_size: (out) (optional): location to store the actual output size +- * in pixels (width and height) of the square QR code, or %NULL ++ * @pixel_size_out: (out) (optional): The square QR code size ++ * (width and height) in pixels, or %NULL. ++ * Note that it may not match @requested_size. + * @cancellable: (nullable): A #GCancellable to cancel the operation + * @error: #GError for error reporting + * +@@ -104,23 +106,23 @@ fill_pixel (GByteArray *array, + */ + GBytes * + gnome_qr_generate_qr_code_sync (const char *text, +- size_t size, ++ size_t requested_size, + const GnomeQrColor *bg_color, + const GnomeQrColor *fg_color, + GnomeQrPixelFormat format, + GnomeQrEccLevel ecc, +- size_t *out_size, ++ size_t *pixel_size_out, + GCancellable *cancellable, + GError **error) + { + g_autoptr (GByteArray) qr_matrix = NULL; + uint8_t qr_code[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; + uint8_t temp_buf[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; +- gint pixel_size, qr_size, total_size; ++ gint qr_size, block_size, total_size; + gint column, row, i; + + g_return_val_if_fail (text != NULL, NULL); +- g_return_val_if_fail (size > 0, NULL); ++ g_return_val_if_fail (requested_size > 0, NULL); + + if (format == GNOME_QR_PIXEL_FORMAT_RGB_888) { + g_return_val_if_fail (!bg_color || bg_color->alpha == 255, NULL); +@@ -156,19 +158,19 @@ gnome_qr_generate_qr_code_sync (const char *text, + fg_color = &GNOME_QR_COLOR_BLACK; + + qr_size = qrcodegen_getSize (qr_code); +- pixel_size = MAX (1, size / qr_size); +- total_size = qr_size * pixel_size; ++ block_size = MAX (1, requested_size / qr_size); ++ total_size = qr_size * block_size; + +- qr_matrix = g_byte_array_sized_new (total_size * total_size * pixel_size * ++ qr_matrix = g_byte_array_sized_new (total_size * total_size * block_size * + GNOME_QR_BYTES_PER_FORMAT (format)); + + for (column = 0; column < total_size; ++column) { +- for (i = 0; i < pixel_size; ++i) { ++ for (i = 0; i < block_size; ++i) { + for (row = 0; row < qr_size; ++row) { + if (qrcodegen_getModule (qr_code, column, row)) +- fill_pixel (qr_matrix, fg_color, format, pixel_size); ++ fill_block (qr_matrix, fg_color, format, block_size); + else +- fill_pixel (qr_matrix, bg_color, format, pixel_size); ++ fill_block (qr_matrix, bg_color, format, block_size); + } + } + +@@ -176,8 +178,8 @@ gnome_qr_generate_qr_code_sync (const char *text, + return NULL; + } + +- if (out_size) +- *out_size = total_size; ++ if (pixel_size_out) ++ *pixel_size_out = total_size; + + return g_byte_array_free_to_bytes (g_steal_pointer (&qr_matrix)); + } +@@ -194,12 +196,12 @@ generate_qr_code_in_thread (GTask *task, + g_autoptr (GBytes) icon = NULL; + + icon = gnome_qr_generate_qr_code_sync (data->text, +- data->size, ++ data->requested_size, + data->bg_color, + data->fg_color, + data->format, + data->ecc, +- &data->out_size, ++ &data->pixel_size, + cancellable, + &error); + if (!icon) { +@@ -213,7 +215,8 @@ generate_qr_code_in_thread (GTask *task, + /** + * gnome_qr_generate_qr_code_async: + * @text: the text of which generate the qr code +- * @size: The requested size in pixels (width and height) for the square QR code ++ * @requested_size: The requested size (width and height) in pixels of the QR code. ++ * Only square QR codes are supported. + * @bg_color: (nullable): The background color of the code + * or %NULL to use default (white) + * @fg_color: (nullable): The foreground color of the code +@@ -231,7 +234,7 @@ generate_qr_code_in_thread (GTask *task, + */ + void + gnome_qr_generate_qr_code_async (const char *text, +- size_t size, ++ size_t requested_size, + const GnomeQrColor *bg_color, + const GnomeQrColor *fg_color, + GnomeQrPixelFormat format, +@@ -257,7 +260,7 @@ gnome_qr_generate_qr_code_async (const char *text, + + data = g_new0 (GnomeQrCodeData, 1); + data->text = g_strdup (text); +- data->size = size; ++ data->requested_size = requested_size; + data->bg_color = g_memdup2 (bg_color, sizeof (GnomeQrColor)); + data->fg_color = g_memdup2 (fg_color, sizeof (GnomeQrColor)); + data->format = format; +@@ -274,8 +277,10 @@ gnome_qr_generate_qr_code_async (const char *text, + /** + * gnome_qr_generate_qr_code_finish: + * @result: the #GAsyncResult that was provided to the callback +- * @out_size: (out) (optional): location to store the actual output size +- * in pixels (width and height) of the square QR code, or %NULL ++ * @pixel_size_out: (out) (optional): The square QR code size ++ * (width and height) in pixels, or %NULL. ++ * Note that it may not match the @requested_size in ++ * gnome_qr_generate_qr_code_async(). + * @error: #GError for error reporting + * + * Finish the asynchronous operation started by +@@ -301,8 +306,8 @@ gnome_qr_generate_qr_code_finish (GAsyncResult *result, + return NULL; + + data = g_task_get_task_data (G_TASK (result)); +- if (out_size) +- *out_size = data->out_size; ++ if (pixel_size_out) ++ *pixel_size_out = data->pixel_size; + + return g_steal_pointer (&pixel_data); + } +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.h b/libgnome-desktop/gnome-qr/gnome-qr.h +index b95480c3..d7dbf52a 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.h ++++ b/libgnome-desktop/gnome-qr/gnome-qr.h +@@ -101,17 +101,17 @@ typedef struct { + } GnomeQrColor; + + GBytes * gnome_qr_generate_qr_code_sync (const char *text, +- size_t size, ++ size_t requested_size, + const GnomeQrColor *bg_color, + const GnomeQrColor *fg_color, + GnomeQrPixelFormat format, + GnomeQrEccLevel ecc, +- size_t *out_size, ++ size_t *pixel_size_out, + GCancellable *cancellable, + GError **error); + + void gnome_qr_generate_qr_code_async (const char *text, +- size_t size, ++ size_t requested_size, + const GnomeQrColor *bg_color, + const GnomeQrColor *fg_color, + GnomeQrPixelFormat format, +@@ -121,5 +121,5 @@ void gnome_qr_generate_qr_code_async (const char *text, + gpointer user_data); + + GBytes * gnome_qr_generate_qr_code_finish (GAsyncResult *result, +- size_t *out_size, ++ size_t *pixel_size_out, + GError **error); +-- +2.52.0 + + +From 8846d0b6899d8dff5d81554f3bb177f5c7390ad2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 01:10:00 +0100 +Subject: [PATCH 09/20] gnome-qr: Docs cleanups + +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 4 ++-- + libgnome-desktop/gnome-qr/meson.build | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index f2e7b152..ce9fd406 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -85,7 +85,7 @@ fill_block (GByteArray *array, + + /** + * gnome_qr_generate_qr_code_sync: +- * @text: the text of which generate the qr code ++ * @text: the text of which generate the QR code + * @requested_size: The requested size (width and height) in pixels of the QR code. + * Only square QR codes are supported. + * @bg_color: (nullable): The background color of the code +@@ -214,7 +214,7 @@ generate_qr_code_in_thread (GTask *task, + + /** + * gnome_qr_generate_qr_code_async: +- * @text: the text of which generate the qr code ++ * @text: The text of which generate the QR code + * @requested_size: The requested size (width and height) in pixels of the QR code. + * Only square QR codes are supported. + * @bg_color: (nullable): The background color of the code +diff --git a/libgnome-desktop/gnome-qr/meson.build b/libgnome-desktop/gnome-qr/meson.build +index f8ff3068..5a9a2cea 100644 +--- a/libgnome-desktop/gnome-qr/meson.build ++++ b/libgnome-desktop/gnome-qr/meson.build +@@ -36,6 +36,6 @@ pkg.generate( + version: meson.project_version(), + name: 'gnome-qr-4', + filebase: 'gnome-qr-4', +- description: 'Qr code utility library for GNOME desktop components', ++ description: 'QR code utility library for GNOME desktop components', + subdirs: 'gnome-desktop-4.0', + ) +-- +2.52.0 + + +From 0d61e1a58aa1b6fe97a5ca5f5c2c2c522a842d1d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 01:11:57 +0100 +Subject: [PATCH 10/20] gnome-qr: Use the same pre-conditions in the sync and + async versions + +We have exactly the same requirements, however we were not checking the +async case until the thread was actually spinned, and this might be too +late to figure out what's wrong. + +So let's just replicate the same logic in both versions, rather than +returning errors that are not real errors (but rather programmer +errors). +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index ce9fd406..50860bee 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -122,6 +122,7 @@ gnome_qr_generate_qr_code_sync (const char *text, + gint column, row, i; + + g_return_val_if_fail (text != NULL, NULL); ++ g_return_val_if_fail (*text != '\0', NULL); + g_return_val_if_fail (requested_size > 0, NULL); + + if (format == GNOME_QR_PIXEL_FORMAT_RGB_888) { +@@ -143,7 +144,7 @@ gnome_qr_generate_qr_code_sync (const char *text, + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, +- "QRCode generation failed for content %s", ++ "QRCode generation failed for content '%s'", + text); + return NULL; + } +@@ -246,16 +247,13 @@ gnome_qr_generate_qr_code_async (const char *text, + g_autoptr (GTask) task = NULL; + GnomeQrCodeData *data; + +- if (!text || *text == '\0') { +- if (callback) +- g_task_report_new_error (NULL, +- callback, +- user_data, +- gnome_qr_generate_qr_code_async, +- G_IO_ERROR, +- G_IO_ERROR_INVALID_DATA, +- "No valid QR code text is provided"); +- return; ++ g_return_if_fail (text != NULL); ++ g_return_if_fail (*text != '\0'); ++ g_return_if_fail (requested_size > 0); ++ ++ if (format == GNOME_QR_PIXEL_FORMAT_RGB_888) { ++ g_return_if_fail (!bg_color || bg_color->alpha == 255); ++ g_return_if_fail (!fg_color || fg_color->alpha == 255); + } + + data = g_new0 (GnomeQrCodeData, 1); +-- +2.52.0 + + +From 3c84ff4ece790b629f26c6d4c83c187d389e4edc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 03:06:51 +0100 +Subject: [PATCH 11/20] tests/languages: Plug leaks for pleasing CI + +--- + tests/languages.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tests/languages.c b/tests/languages.c +index 34c1cc02..42271bda 100644 +--- a/tests/languages.c ++++ b/tests/languages.c +@@ -29,9 +29,9 @@ + static void + test_using_null_locale (void) + { +- const char *translated_territory; +- const char *translated_language; +- const char *translated_modifier; ++ g_autofree char *translated_territory = NULL; ++ g_autofree char *translated_language = NULL; ++ g_autofree char *translated_modifier = NULL; + + translated_territory = gnome_get_country_from_code("US", NULL); + g_assert (translated_territory != NULL); +-- +2.52.0 + + +From 2195f0c15535580dc8698a4f2fc6690b932e37eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 03:15:48 +0100 +Subject: [PATCH 12/20] gnome-languages: Do not leak the directory entries + +--- + libgnome-desktop/gnome-languages.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/libgnome-desktop/gnome-languages.c b/libgnome-desktop/gnome-languages.c +index 8a0485c8..f85adcf6 100644 +--- a/libgnome-desktop/gnome-languages.c ++++ b/libgnome-desktop/gnome-languages.c +@@ -480,6 +480,8 @@ collect_locales_from_directory (void) + for (cnt = 0; cnt < ndirents; ++cnt) { + if (add_locale (dirents[cnt]->d_name, TRUE)) + found_locales = TRUE; ++ ++ free (dirents[cnt]); + } + + if (ndirents > 0) { +-- +2.52.0 + + +From b23c1293150be248aab2dc83ffc207cf01de32b9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 01:44:26 +0100 +Subject: [PATCH 13/20] build/ci: Enable tests again + +--- + tests/meson.build | 26 +++++++++++++++----------- + tests/wall-clock.c | 3 +++ + 2 files changed, 18 insertions(+), 11 deletions(-) + +diff --git a/tests/meson.build b/tests/meson.build +index 6982c543..752d07cf 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -11,30 +11,34 @@ test_env = [ + 'G_ENABLE_DIAGNOSTIC=0' + ] + +-test_programs = [] ++test_programs = {} + test_metas = [] + + # XXX: The current batch of tests relies on the legacy library + if get_option('legacy_library') +- test_programs += [ +- 'bg-slide-show', +- 'wall-clock', +- 'wallclock-reftest', +- 'languages' +- ] ++ test_programs += { ++ 'bg-slide-show': {'local-test': false}, ++ 'wall-clock': {}, ++ 'languages': {}, ++ } + + test_metas += [] + endif + +-foreach program: test_programs +- exe = executable(program, program + '.c', ++foreach program, options: test_programs ++ installed = get_option('installed_tests') and options.get('installed', true) ++ test_exe = executable(program, program + '.c', + dependencies: libgnome_desktop_dep, + c_args: test_args, + install_dir: test_execdir, +- install: get_option('installed_tests') ++ install: installed + ) + +- if get_option('installed_tests') ++ if options.get('local-test', true) ++ test(program, test_exe, env: test_env) ++ endif ++ ++ if installed + meta_data = configuration_data() + meta_data.set('TEST_EXEC', join_paths(test_execdir, program)) + +diff --git a/tests/wall-clock.c b/tests/wall-clock.c +index 44bc93bb..b0f47107 100644 +--- a/tests/wall-clock.c ++++ b/tests/wall-clock.c +@@ -96,6 +96,9 @@ test_colon_vs_ratio (void) + static void + test_space_vs_en_space (void) + { ++ g_test_skip ("FIXME: This test is failing"); ++ return; ++ + test_utf8_character (EN_SPACE, SPACE); + } + +-- +2.52.0 + + +From 1c3fa0595643f865c4b4d777a4eb8e05fcf51376 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 03:08:20 +0100 +Subject: [PATCH 14/20] gnome-qr: Do not create unnecessarily big array + +We were multiplying the total pixel size twice + +Also re-order the for logic to be clearer +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index 50860bee..0c597e6a 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -159,15 +159,17 @@ gnome_qr_generate_qr_code_sync (const char *text, + fg_color = &GNOME_QR_COLOR_BLACK; + + qr_size = qrcodegen_getSize (qr_code); ++ g_assert (qr_size > 0); ++ + block_size = MAX (1, requested_size / qr_size); + total_size = qr_size * block_size; + +- qr_matrix = g_byte_array_sized_new (total_size * total_size * block_size * ++ qr_matrix = g_byte_array_sized_new (total_size * total_size * + GNOME_QR_BYTES_PER_FORMAT (format)); + +- for (column = 0; column < total_size; ++column) { ++ for (row = 0; row < qr_size; ++row) { + for (i = 0; i < block_size; ++i) { +- for (row = 0; row < qr_size; ++row) { ++ for (column = 0; column < qr_size; ++column) { + if (qrcodegen_getModule (qr_code, column, row)) + fill_block (qr_matrix, fg_color, format, block_size); + else +-- +2.52.0 + + +From f86b6221970984bb18f734b6fe008dd2ccf9be65 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 03:09:19 +0100 +Subject: [PATCH 15/20] gnome-qr: Do not access to potentially temporary data + +The colors needs to be statically saved or we may read from temporary +values +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index 0c597e6a..3fbea659 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -118,6 +118,8 @@ gnome_qr_generate_qr_code_sync (const char *text, + g_autoptr (GByteArray) qr_matrix = NULL; + uint8_t qr_code[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; + uint8_t temp_buf[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; ++ static const GnomeQrColor default_bg_color = GNOME_QR_COLOR_WHITE; ++ static const GnomeQrColor default_fg_color = GNOME_QR_COLOR_BLACK; + gint qr_size, block_size, total_size; + gint column, row, i; + +@@ -153,10 +155,10 @@ gnome_qr_generate_qr_code_sync (const char *text, + return NULL; + + if (!bg_color) +- bg_color = &GNOME_QR_COLOR_WHITE; ++ bg_color = &default_bg_color; + + if (!fg_color) +- fg_color = &GNOME_QR_COLOR_BLACK; ++ fg_color = &default_fg_color; + + qr_size = qrcodegen_getSize (qr_code); + g_assert (qr_size > 0); +-- +2.52.0 + + +From 9867e68039c0d72120ef9ace502a42e6f50dbc6e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 03:10:02 +0100 +Subject: [PATCH 16/20] gnome-qr: Accept a 0-sized qr-code + +It will be just the minimal size that is required for this. + +UIs can then just stretch it using more optimized rendering, without +wasting memory +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index 3fbea659..936b43a7 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -125,7 +125,6 @@ gnome_qr_generate_qr_code_sync (const char *text, + + g_return_val_if_fail (text != NULL, NULL); + g_return_val_if_fail (*text != '\0', NULL); +- g_return_val_if_fail (requested_size > 0, NULL); + + if (format == GNOME_QR_PIXEL_FORMAT_RGB_888) { + g_return_val_if_fail (!bg_color || bg_color->alpha == 255, NULL); +@@ -253,7 +252,6 @@ gnome_qr_generate_qr_code_async (const char *text, + + g_return_if_fail (text != NULL); + g_return_if_fail (*text != '\0'); +- g_return_if_fail (requested_size > 0); + + if (format == GNOME_QR_PIXEL_FORMAT_RGB_888) { + g_return_if_fail (!bg_color || bg_color->alpha == 255); +-- +2.52.0 + + +From 72ce693100d638964ffdf9532fd5dbbab6e7e039 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 03:11:49 +0100 +Subject: [PATCH 17/20] tests: Add basic qr-code tests + +--- + libgnome-desktop/gnome-qr/meson.build | 7 + + meson.build | 2 +- + tests/meson.build | 11 +- + tests/qr-code.c | 535 ++++++++++++++++++++++++++ + 4 files changed, 553 insertions(+), 2 deletions(-) + create mode 100644 tests/qr-code.c + +diff --git a/libgnome-desktop/gnome-qr/meson.build b/libgnome-desktop/gnome-qr/meson.build +index 5a9a2cea..63b5de03 100644 +--- a/libgnome-desktop/gnome-qr/meson.build ++++ b/libgnome-desktop/gnome-qr/meson.build +@@ -39,3 +39,10 @@ pkg.generate( + description: 'QR code utility library for GNOME desktop components', + subdirs: 'gnome-desktop-4.0', + ) ++ ++gnome_qr_dep = declare_dependency( ++ include_directories: [ ++ include_directories('.'), ++ ], ++ link_with: libgnome_qr, ++) +diff --git a/meson.build b/meson.build +index a4876a84..4f0e11a2 100644 +--- a/meson.build ++++ b/meson.build +@@ -1,5 +1,5 @@ + project('gnome-desktop', 'c', +- meson_version: '>= 0.56.2', ++ meson_version: '>= 0.60.0', + version: '44.1', + license: ['GPL-2.0-or-later', 'LGPL-2.1-or-later'], + default_options: [ +diff --git a/tests/meson.build b/tests/meson.build +index 752d07cf..63e15e4c 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -25,10 +25,18 @@ if get_option('legacy_library') + test_metas += [] + endif + ++if get_option('build_gtk4') ++ test_programs += { ++ 'qr-code': { ++ 'dependencies': gnome_qr_dep, ++ }, ++ } ++endif ++ + foreach program, options: test_programs + installed = get_option('installed_tests') and options.get('installed', true) + test_exe = executable(program, program + '.c', +- dependencies: libgnome_desktop_dep, ++ dependencies: [libgnome_desktop_dep] + options.get('dependencies', []), + c_args: test_args, + install_dir: test_execdir, + install: installed +@@ -68,4 +76,5 @@ if get_option('installed_tests') + ) + install_data(test_data, install_dir: test_execdir) + install_data(test_metas, install_dir: test_metadir) ++ install_subdir('data', install_dir: test_execdir) + endif +diff --git a/tests/qr-code.c b/tests/qr-code.c +new file mode 100644 +index 00000000..80c121ad +--- /dev/null ++++ b/tests/qr-code.c +@@ -0,0 +1,535 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* ++ * Copyright 2025 Canonical Ltd ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * ++ * 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.1 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. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, see . ++ * ++ * Author: Marco Trevisan ++ */ ++ ++#include "gnome-qr/gnome-qr.h" ++ ++ ++/* FIXME: Ideally we want to check against visible PNGs so that we can verify ++ * the actual rendering, but for now we just compare the raw data because ++ * GNOME OS image in CI does not seem to provide a proper png decoder. ++ */ ++static void ++assert_qr_matches_reference (GBytes *qr_code, ++ const char *reference_file, ++ GnomeQrPixelFormat format) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autofree guint8 *data = NULL; ++ gsize size; ++ ++#ifdef UPDATE_REFERENCE_DATA ++ g_file_set_contents (reference_file, ++ (const gchar *)g_bytes_get_data (qr_code, NULL), ++ g_bytes_get_size (qr_code), &error); ++ g_assert_no_error (error); ++#endif ++ ++ g_file_get_contents (reference_file, ++ (gchar **) &data, ++ &size, ++ &error); ++ g_assert_no_error (error); ++ ++ g_assert_cmpmem (g_bytes_get_data (qr_code, NULL), ++ g_bytes_get_size (qr_code), ++ data, ++ size); ++} ++ ++static void ++test_gnome_qr_sync_generate_simple_rgb_888 (void) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GBytes) qr_code = NULL; ++ size_t pixel_size; ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("https://gnome.org", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ &pixel_size, ++ NULL, &error); ++ g_assert_nonnull (qr_code); ++ g_assert_no_error (error); ++ ++ assert_qr_matches_reference (qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-gnome.org-0-rgb.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGB_888); ++} ++ ++static void ++test_gnome_qr_sync_generate_simple_rgba_8888 (void) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GBytes) qr_code = NULL; ++ size_t pixel_size; ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("https://gnome.org", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888, ++ GNOME_QR_ECC_LEVEL_LOW, ++ &pixel_size, ++ NULL, &error); ++ g_assert_nonnull (qr_code); ++ g_assert_no_error (error); ++ ++ assert_qr_matches_reference (qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-gnome.org-0-rgba.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888); ++} ++ ++typedef struct { ++ GMainLoop *loop; ++ GBytes *qr_code; ++ size_t pixel_size; ++ GError *error; ++} AsyncData; ++ ++static void ++on_qr_code_generated (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ AsyncData *data = user_data; ++ ++ data->qr_code = gnome_qr_generate_qr_code_finish (result, ++ &data->pixel_size, ++ &data->error); ++ g_main_loop_quit (data->loop); ++} ++ ++static void ++test_gnome_qr_async_generate_simple_rgb_888 (void) ++{ ++ AsyncData data = { 0 }; ++ data.loop = g_main_loop_new (NULL, FALSE); ++ ++ gnome_qr_generate_qr_code_async ("https://gnome.org", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ NULL, ++ on_qr_code_generated, ++ &data); ++ ++ g_main_loop_run (data.loop); ++ g_main_loop_unref (data.loop); ++ ++ g_assert_nonnull (data.qr_code); ++ g_assert_no_error (data.error); ++ ++ assert_qr_matches_reference (data.qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-gnome.org-0-rgb.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGB_888); ++ ++ g_bytes_unref (data.qr_code); ++} ++ ++static void ++test_gnome_qr_async_generate_simple_rgba_8888 (void) ++{ ++ AsyncData data = { 0 }; ++ data.loop = g_main_loop_new (NULL, FALSE); ++ ++ gnome_qr_generate_qr_code_async ("https://gnome.org", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888, ++ GNOME_QR_ECC_LEVEL_LOW, ++ NULL, ++ on_qr_code_generated, ++ &data); ++ ++ g_main_loop_run (data.loop); ++ g_main_loop_unref (data.loop); ++ ++ g_assert_nonnull (data.qr_code); ++ g_assert_no_error (data.error); ++ ++ assert_qr_matches_reference (data.qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-gnome.org-0-rgba.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888); ++ ++ g_bytes_unref (data.qr_code); ++} ++ ++static void ++test_gnome_qr_sync_generate_custom_colors_fedora (void) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GBytes) qr_code = NULL; ++ GnomeQrColor bg_color = { 60, 110, 180, 255 }; /* Fedora blue */ ++ GnomeQrColor fg_color = { 255, 255, 255, 125 }; /* White with transparency */ ++ size_t pixel_size; ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("https://fedoraproject.org", ++ 256, &bg_color, &fg_color, ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888, ++ GNOME_QR_ECC_LEVEL_MEDIUM, ++ &pixel_size, ++ NULL, &error); ++ g_assert_nonnull (qr_code); ++ g_assert_no_error (error); ++ ++ assert_qr_matches_reference (qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-fedoraproject.org-256-rgba.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888); ++} ++ ++static void ++test_gnome_qr_sync_generate_custom_colors_ubuntu (void) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GBytes) qr_code = NULL; ++ GnomeQrColor bg_color = { 0, 0, 0, 200 }; /* Black with transparency */ ++ GnomeQrColor fg_color = { 233, 84, 32, 255 }; /* Ubuntu orange */ ++ size_t pixel_size; ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("https://ubuntu.com", ++ 256, &bg_color, &fg_color, ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888, ++ GNOME_QR_ECC_LEVEL_QUARTILE, ++ &pixel_size, ++ NULL, &error); ++ g_assert_nonnull (qr_code); ++ g_assert_no_error (error); ++ ++ assert_qr_matches_reference (qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-ubuntu.com-256-rgba.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888); ++} ++ ++static void ++test_gnome_qr_async_generate_custom_colors_fedora (void) ++{ ++ AsyncData data = { 0 }; ++ GnomeQrColor bg_color = { 60, 110, 180, 255 }; /* Fedora blue */ ++ GnomeQrColor fg_color = { 255, 255, 255, 125 }; /* White with transparency */ ++ ++ data.loop = g_main_loop_new (NULL, FALSE); ++ ++ gnome_qr_generate_qr_code_async ("https://fedoraproject.org", ++ 256, &bg_color, &fg_color, ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888, ++ GNOME_QR_ECC_LEVEL_MEDIUM, ++ NULL, ++ on_qr_code_generated, ++ &data); ++ ++ g_main_loop_run (data.loop); ++ g_main_loop_unref (data.loop); ++ ++ g_assert_nonnull (data.qr_code); ++ g_assert_no_error (data.error); ++ ++ assert_qr_matches_reference (data.qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-fedoraproject.org-256-rgba.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888); ++ ++ g_bytes_unref (data.qr_code); ++} ++ ++static void ++test_gnome_qr_async_generate_custom_colors_ubuntu (void) ++{ ++ AsyncData data = { 0 }; ++ GnomeQrColor bg_color = { 0, 0, 0, 200 }; /* Black with transparency */ ++ GnomeQrColor fg_color = { 233, 84, 32, 255 }; /* Ubuntu orange */ ++ ++ data.loop = g_main_loop_new (NULL, FALSE); ++ ++ gnome_qr_generate_qr_code_async ("https://ubuntu.com", ++ 256, &bg_color, &fg_color, ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888, ++ GNOME_QR_ECC_LEVEL_QUARTILE, ++ NULL, ++ on_qr_code_generated, ++ &data); ++ ++ g_main_loop_run (data.loop); ++ g_main_loop_unref (data.loop); ++ ++ g_assert_nonnull (data.qr_code); ++ g_assert_no_error (data.error); ++ ++ assert_qr_matches_reference (data.qr_code, ++ g_test_get_filename (G_TEST_DIST, ++ "data", ++ "qr-ubuntu.com-256-rgba.data", ++ NULL), ++ GNOME_QR_PIXEL_FORMAT_RGBA_8888); ++ ++ g_bytes_unref (data.qr_code); ++} ++ ++static void ++test_gnome_qr_sync_generate_cancelled (void) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GCancellable) cancellable = NULL; ++ g_autoptr (GBytes) qr_code = NULL; ++ size_t pixel_size; ++ ++ cancellable = g_cancellable_new (); ++ g_cancellable_cancel (cancellable); ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("https://gnome.org", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ &pixel_size, ++ cancellable, &error); ++ ++ g_assert_null (qr_code); ++ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); ++} ++ ++static void ++test_gnome_qr_async_generate_cancelled (void) ++{ ++ AsyncData data = { 0 }; ++ g_autoptr (GCancellable) cancellable = NULL; ++ ++ data.loop = g_main_loop_new (NULL, FALSE); ++ cancellable = g_cancellable_new (); ++ ++ gnome_qr_generate_qr_code_async ("https://gnome.org", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ cancellable, ++ on_qr_code_generated, ++ &data); ++ ++ g_cancellable_cancel (cancellable); ++ ++ g_main_loop_run (data.loop); ++ g_main_loop_unref (data.loop); ++ ++ g_assert_null (data.qr_code); ++ g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); ++ ++ g_error_free (data.error); ++} ++ ++static void ++test_gnome_qr_sync_generate_null_text (void) ++{ ++ g_autoptr (GBytes) qr_code = NULL; ++ size_t pixel_size; ++ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "*text != NULL*"); ++ ++ qr_code = gnome_qr_generate_qr_code_sync (NULL, ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ &pixel_size, ++ NULL, NULL); ++ ++ g_test_assert_expected_messages (); ++ g_assert_null (qr_code); ++} ++ ++static void ++test_gnome_qr_sync_generate_empty_text (void) ++{ ++ g_autoptr (GBytes) qr_code = NULL; ++ size_t pixel_size; ++ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "**text != '\\0'*"); ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ &pixel_size, ++ NULL, NULL); ++ ++ g_test_assert_expected_messages (); ++ g_assert_null (qr_code); ++} ++ ++static void ++test_gnome_qr_sync_generate_rgb_with_transparent_bg (void) ++{ ++ g_autoptr (GBytes) qr_code = NULL; ++ GnomeQrColor bg_color = { 255, 255, 255, 200 }; /* transparent white */ ++ size_t pixel_size; ++ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "*!bg_color || bg_color->alpha == 255*"); ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("https://gnome.org", ++ 0, &bg_color, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ &pixel_size, ++ NULL, NULL); ++ ++ g_test_assert_expected_messages (); ++ g_assert_null (qr_code); ++} ++ ++static void ++test_gnome_qr_sync_generate_rgb_with_transparent_fg (void) ++{ ++ g_autoptr (GBytes) qr_code = NULL; ++ GnomeQrColor fg_color = { 0, 0, 0, 200 }; /* transparent black */ ++ size_t pixel_size; ++ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "*!fg_color || fg_color->alpha == 255*"); ++ ++ qr_code = gnome_qr_generate_qr_code_sync ("https://gnome.org", ++ 0, NULL, &fg_color, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ &pixel_size, ++ NULL, NULL); ++ ++ g_test_assert_expected_messages (); ++ g_assert_null (qr_code); ++} ++ ++static void ++test_gnome_qr_async_generate_null_text (void) ++{ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "*text != NULL*"); ++ ++ gnome_qr_generate_qr_code_async (NULL, ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ NULL, ++ NULL, ++ NULL); ++ ++ g_test_assert_expected_messages (); ++} ++ ++static void ++test_gnome_qr_async_generate_empty_text (void) ++{ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "**text != '\\0'*"); ++ ++ gnome_qr_generate_qr_code_async ("", ++ 0, NULL, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ NULL, ++ NULL, ++ NULL); ++ ++ g_test_assert_expected_messages (); ++} ++ ++static void ++test_gnome_qr_async_generate_rgb_with_transparent_bg (void) ++{ ++ GnomeQrColor bg_color = { 255, 255, 255, 200 }; /* transparent white */ ++ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "*!bg_color || bg_color->alpha == 255*"); ++ ++ gnome_qr_generate_qr_code_async ("https://gnome.org", ++ 0, &bg_color, NULL, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ NULL, ++ NULL, ++ NULL); ++ ++ g_test_assert_expected_messages (); ++} ++ ++static void ++test_gnome_qr_async_generate_rgb_with_transparent_fg (void) ++{ ++ GnomeQrColor fg_color = { 0, 0, 0, 200 }; /* transparent black */ ++ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, ++ "*!fg_color || fg_color->alpha == 255*"); ++ ++ gnome_qr_generate_qr_code_async ("https://gnome.org", ++ 0, NULL, &fg_color, ++ GNOME_QR_PIXEL_FORMAT_RGB_888, ++ GNOME_QR_ECC_LEVEL_HIGH, ++ NULL, ++ NULL, ++ NULL); ++ ++ g_test_assert_expected_messages (); ++} ++ ++int ++main (int argc, ++ char *argv[]) ++{ ++ g_setenv ("LC_ALL", "C", TRUE); ++ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); ++ ++ g_test_add_func ("/gnome-qr/sync/generate-simple-rgb-888", test_gnome_qr_sync_generate_simple_rgb_888); ++ g_test_add_func ("/gnome-qr/sync/generate-simple-rgba-8888", test_gnome_qr_sync_generate_simple_rgba_8888); ++ g_test_add_func ("/gnome-qr/sync/generate-custom-colors-fedora", test_gnome_qr_sync_generate_custom_colors_fedora); ++ g_test_add_func ("/gnome-qr/sync/generate-custom-colors-ubuntu", test_gnome_qr_sync_generate_custom_colors_ubuntu); ++ g_test_add_func ("/gnome-qr/sync/generate-cancelled", test_gnome_qr_sync_generate_cancelled); ++ g_test_add_func ("/gnome-qr/sync/generate-null-text", test_gnome_qr_sync_generate_null_text); ++ g_test_add_func ("/gnome-qr/sync/generate-empty-text", test_gnome_qr_sync_generate_empty_text); ++ g_test_add_func ("/gnome-qr/sync/generate-rgb-with-transparent-bg", test_gnome_qr_sync_generate_rgb_with_transparent_bg); ++ g_test_add_func ("/gnome-qr/sync/generate-rgb-with-transparent-fg", test_gnome_qr_sync_generate_rgb_with_transparent_fg); ++ g_test_add_func ("/gnome-qr/async/generate-simple-rgb-888", test_gnome_qr_async_generate_simple_rgb_888); ++ g_test_add_func ("/gnome-qr/async/generate-simple-rgba-8888", test_gnome_qr_async_generate_simple_rgba_8888); ++ g_test_add_func ("/gnome-qr/async/generate-custom-colors-fedora", test_gnome_qr_async_generate_custom_colors_fedora); ++ g_test_add_func ("/gnome-qr/async/generate-custom-colors-ubuntu", test_gnome_qr_async_generate_custom_colors_ubuntu); ++ g_test_add_func ("/gnome-qr/async/generate-cancelled", test_gnome_qr_async_generate_cancelled); ++ g_test_add_func ("/gnome-qr/async/generate-null-text", test_gnome_qr_async_generate_null_text); ++ g_test_add_func ("/gnome-qr/async/generate-empty-text", test_gnome_qr_async_generate_empty_text); ++ g_test_add_func ("/gnome-qr/async/generate-rgb-with-transparent-bg", test_gnome_qr_async_generate_rgb_with_transparent_bg); ++ g_test_add_func ("/gnome-qr/async/generate-rgb-with-transparent-fg", test_gnome_qr_async_generate_rgb_with_transparent_fg); ++ ++ return g_test_run (); ++} +-- +2.52.0 + + +From 30e213d6c2fe867a35e8d2ede4954e97c9ae8f3a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 06:11:42 +0100 +Subject: [PATCH 18/20] gnome-qr: Ensure that the final size is at least the + requested one + +Each block can be wider if the space needed does not fit the requested +space +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 3 ++- + libgnome-desktop/gnome-qr/meson.build | 1 + + meson.build | 2 +- + tests/qr-code.c | 6 ++++++ + 4 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index 936b43a7..e18c04a9 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -27,6 +27,7 @@ + + #include "gnome-qr.h" + ++#include + #include + + typedef struct +@@ -162,7 +163,7 @@ gnome_qr_generate_qr_code_sync (const char *text, + qr_size = qrcodegen_getSize (qr_code); + g_assert (qr_size > 0); + +- block_size = MAX (1, requested_size / qr_size); ++ block_size = MAX (1, ceil ((double) requested_size / qr_size)); + total_size = qr_size * block_size; + + qr_matrix = g_byte_array_sized_new (total_size * total_size * +diff --git a/libgnome-desktop/gnome-qr/meson.build b/libgnome-desktop/gnome-qr/meson.build +index 63b5de03..c0679b31 100644 +--- a/libgnome-desktop/gnome-qr/meson.build ++++ b/libgnome-desktop/gnome-qr/meson.build +@@ -2,6 +2,7 @@ libgnome_qr_deps = [ + glib_dep, + gio_dep, + qrcodegen_dep, ++ m_dep, + ] + + libgnome_qr_sources = files([ +diff --git a/meson.build b/meson.build +index 4f0e11a2..0fb515d7 100644 +--- a/meson.build ++++ b/meson.build +@@ -84,7 +84,7 @@ iso_codes_prefix = iso_codes_dep.get_variable(pkgconfig: 'prefix') + + cc = meson.get_compiler('c') + +-m_dep = cc.find_library('m', required: false) ++m_dep = cc.find_library('m', required: true) + rt_dep = cc.find_library('rt', required: false) + + if not cc.has_function('clock_gettime', dependencies: rt_dep) +diff --git a/tests/qr-code.c b/tests/qr-code.c +index 80c121ad..b3659f7b 100644 +--- a/tests/qr-code.c ++++ b/tests/qr-code.c +@@ -142,6 +142,7 @@ test_gnome_qr_async_generate_simple_rgb_888 (void) + + g_assert_nonnull (data.qr_code); + g_assert_no_error (data.error); ++ g_assert_cmpuint (data.pixel_size, >, 0); + + assert_qr_matches_reference (data.qr_code, + g_test_get_filename (G_TEST_DIST, +@@ -172,6 +173,7 @@ test_gnome_qr_async_generate_simple_rgba_8888 (void) + + g_assert_nonnull (data.qr_code); + g_assert_no_error (data.error); ++ g_assert_cmpuint (data.pixel_size, >, 0); + + assert_qr_matches_reference (data.qr_code, + g_test_get_filename (G_TEST_DIST, +@@ -200,6 +202,7 @@ test_gnome_qr_sync_generate_custom_colors_fedora (void) + NULL, &error); + g_assert_nonnull (qr_code); + g_assert_no_error (error); ++ g_assert_cmpuint (pixel_size, >=, 256); + + assert_qr_matches_reference (qr_code, + g_test_get_filename (G_TEST_DIST, +@@ -226,6 +229,7 @@ test_gnome_qr_sync_generate_custom_colors_ubuntu (void) + NULL, &error); + g_assert_nonnull (qr_code); + g_assert_no_error (error); ++ g_assert_cmpuint (pixel_size, >=, 256); + + assert_qr_matches_reference (qr_code, + g_test_get_filename (G_TEST_DIST, +@@ -257,6 +261,7 @@ test_gnome_qr_async_generate_custom_colors_fedora (void) + + g_assert_nonnull (data.qr_code); + g_assert_no_error (data.error); ++ g_assert_cmpuint (data.pixel_size, >=, 256); + + assert_qr_matches_reference (data.qr_code, + g_test_get_filename (G_TEST_DIST, +@@ -290,6 +295,7 @@ test_gnome_qr_async_generate_custom_colors_ubuntu (void) + + g_assert_nonnull (data.qr_code); + g_assert_no_error (data.error); ++ g_assert_cmpuint (data.pixel_size, >=, 256); + + assert_qr_matches_reference (data.qr_code, + g_test_get_filename (G_TEST_DIST, +-- +2.52.0 + + +From a54256a35f841118bcc67e4b21107642361533ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 06:41:02 +0100 +Subject: [PATCH 19/20] gnome-qr: Add introspection support + +We definitely need introspection to be able to use this in the shell and +anywhere else +--- + libgnome-desktop/gnome-qr/meson.build | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/libgnome-desktop/gnome-qr/meson.build b/libgnome-desktop/gnome-qr/meson.build +index c0679b31..3f4c461f 100644 +--- a/libgnome-desktop/gnome-qr/meson.build ++++ b/libgnome-desktop/gnome-qr/meson.build +@@ -31,6 +31,23 @@ libgnome_qr = library('gnome-qr', + ], + ) + ++if get_option('introspection') ++ libgnome_qr_gir = gnome.generate_gir(libgnome_qr, ++ sources: [libgnome_qr_headers, libgnome_qr_sources], ++ export_packages: 'gnome-qr-4', ++ namespace: 'GnomeQR', ++ nsversion: '4.0', ++ identifier_prefix: 'GnomeQr', ++ symbol_prefix: 'gnome_qr', ++ includes: [libgnome_desktop_base_gir[0], 'Gio-2.0', 'GLib-2.0'], ++ extra_args: ['--quiet', '--warn-all'], ++ fatal_warnings: true, ++ install: true, ++ ) ++else ++ libgnome_qr_gir = '' ++endif ++ + pkg.generate( + libgnome_qr, + requires: ['gsettings-desktop-schemas'], +@@ -42,6 +59,9 @@ pkg.generate( + ) + + gnome_qr_dep = declare_dependency( ++ sources: [ ++ libgnome_qr_gir, ++ ], + include_directories: [ + include_directories('.'), + ], +-- +2.52.0 + + +From 1a9e34f2b13bbb51541e137a4091f22299555c11 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 5 Dec 2025 08:03:39 +0100 +Subject: [PATCH 20/20] gnome-qr: Update license to GPL-3+ + +Part of this code was already licensed as GPL3+, so let's just use this +version and adjust the copyright holders +--- + libgnome-desktop/gnome-qr/gnome-qr.c | 24 +++++++++++++----------- + libgnome-desktop/gnome-qr/gnome-qr.h | 24 +++++++++++++----------- + 2 files changed, 26 insertions(+), 22 deletions(-) + +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.c b/libgnome-desktop/gnome-qr/gnome-qr.c +index e18c04a9..b2a34b13 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.c ++++ b/libgnome-desktop/gnome-qr/gnome-qr.c +@@ -1,24 +1,26 @@ + /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + /* ++ * Copyright 2019 Purism SPC + * Copyright 2024-2025 Red Hat, Inc + * Copyright 2024-2025 Canonical Ltd + * +- * SPDX-License-Identifier: LGPL-2.1-or-later ++ * SPDX-License-Identifier: GPL-3.0-or-later + * +- * 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.1 of the License, or (at your option) any later version. ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. + * +- * This library is distributed in the hope that it will be useful, ++ * This program 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. ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. + * +- * You should have received a copy of the GNU Lesser General +- * Public License along with this library; if not, see . ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . + * +- * Author: Ray Strode ++ * Author: Mohammed Sadiq ++ * Ray Strode + * Marco Trevisan + * Joan Torres Lopez + */ +diff --git a/libgnome-desktop/gnome-qr/gnome-qr.h b/libgnome-desktop/gnome-qr/gnome-qr.h +index d7dbf52a..955ebf3b 100644 +--- a/libgnome-desktop/gnome-qr/gnome-qr.h ++++ b/libgnome-desktop/gnome-qr/gnome-qr.h +@@ -1,24 +1,26 @@ + /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + /* ++ * Copyright 2019 Purism SPC + * Copyright 2024-2025 Red Hat, Inc + * Copyright 2024-2025 Canonical Ltd + * +- * SPDX-License-Identifier: LGPL-2.1-or-later ++ * SPDX-License-Identifier: GPL-3.0-or-later + * +- * 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.1 of the License, or (at your option) any later version. ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. + * +- * This library is distributed in the hope that it will be useful, ++ * This program 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. ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. + * +- * You should have received a copy of the GNU Lesser General +- * Public License along with this library; if not, see . ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . + * +- * Author: Ray Strode ++ * Author: Mohammed Sadiq ++ * Ray Strode + * Marco Trevisan + * Joan Torres Lopez + */ +-- +2.52.0 + diff --git a/QR-Code-generator.tar.gz b/QR-Code-generator.tar.gz new file mode 100644 index 0000000..c58783a Binary files /dev/null and b/QR-Code-generator.tar.gz differ diff --git a/gnome-desktop3.spec b/gnome-desktop3.spec index a49aace..71557cf 100644 --- a/gnome-desktop3.spec +++ b/gnome-desktop3.spec @@ -29,6 +29,9 @@ Source4: loupe-defaults.list # Generated with: # for i in `grep MimeType= /usr/share/applications/org.gnome.Evince.desktop | sed 's/MimeType=//' | sed 's/;/ /g'` ; do echo $i=org.gnome.Evince.desktop\; >> evince-defaults.list ; done Source5: evince-defaults.list +Source6: QR-Code-generator.tar.gz + +Patch: 0001-Add-gnome-qr-library.patch BuildRequires: gcc BuildRequires: gettext @@ -116,6 +119,12 @@ the functionality of the installed %{name} package. %prep %autosetup -p1 -n gnome-desktop-%{tarball_version} +# Extract QR Code generator subproject manually (wrap-based subproject sources +# are not included in git patches, only in release tarballs) +mkdir -p subprojects/qrcodegen +tar -xzf %{SOURCE6} -C subprojects/qrcodegen --strip-components=1 +cp -r subprojects/packagefiles/qrcodegen/* subprojects/qrcodegen/ + %build %meson -Dgtk_doc=true -Dinstalled_tests=true %meson_build @@ -156,6 +165,7 @@ cat %SOURCE5 >> $RPM_BUILD_ROOT/%{_datadir}/applications/gnome-mimeapps.list # LGPL %{_libdir}/libgnome-bg-4.so.2{,.*} %{_libdir}/libgnome-desktop-4.so.2{,.*} +%{_libdir}/libgnome-qr-4.so.2{,.*} %{_libdir}/libgnome-rr-4.so.2{,.*} %{_libdir}/girepository-1.0/Gnome*-4.0.typelib