aboutsummaryrefslogtreecommitdiff
path: root/gnu/packages/patches
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/packages/patches')
-rw-r--r--gnu/packages/patches/ffmpeg-jami-pipewiregrab-source-filter.patch1451
-rw-r--r--gnu/packages/patches/gwenview-kimageannotator.patch29
-rw-r--r--gnu/packages/patches/jami-qml-tests-discovery.patch15
-rw-r--r--gnu/packages/patches/jami-qwindowkit.patch37
-rw-r--r--gnu/packages/patches/jami-tests-qtwebengine-ifdef-to-if.patch26
-rw-r--r--gnu/packages/patches/jami-unbundle-dependencies.patch6
-rw-r--r--gnu/packages/patches/libarchive-remove-potential-backdoor.patch47
-rw-r--r--gnu/packages/patches/opencolorio-fix-build-with-gcc11.patch37
-rw-r--r--gnu/packages/patches/openssh-gcc-13-ppc64le-fzero-call-used-regs.patch61
-rw-r--r--gnu/packages/patches/qtbase-find-tools-in-PATH.patch49
-rw-r--r--gnu/packages/patches/qtbase-qmake-fix-includedir.patch29
-rw-r--r--gnu/packages/patches/qtbase-qmlimportscanner-qml-import-path.patch33
-rw-r--r--gnu/packages/patches/qtbase-use-TZDIR.patch141
-rw-r--r--gnu/packages/patches/qtdeclarative-disable-qmlcache.patch27
-rw-r--r--gnu/packages/patches/xgboost-use-system-dmlc-core.patch14
15 files changed, 1733 insertions, 269 deletions
diff --git a/gnu/packages/patches/ffmpeg-jami-pipewiregrab-source-filter.patch b/gnu/packages/patches/ffmpeg-jami-pipewiregrab-source-filter.patch
new file mode 100644
index 0000000000..f2da4f478f
--- /dev/null
+++ b/gnu/packages/patches/ffmpeg-jami-pipewiregrab-source-filter.patch
@@ -0,0 +1,1451 @@
+diff --git a/configure b/configure
+index 3cd3bdfb44..0756ad95ea 100755
+--- a/configure
++++ b/configure
+@@ -297,6 +297,7 @@ External library support:
+ --enable-libxcb-shm enable X11 grabbing shm communication [autodetect]
+ --enable-libxcb-xfixes enable X11 grabbing mouse rendering [autodetect]
+ --enable-libxcb-shape enable X11 grabbing shape rendering [autodetect]
++ --enable-libpipewire enable screen grabbing using pipewire [autodetect]
+ --enable-libxvid enable Xvid encoding via xvidcore,
+ native MPEG-4/Xvid encoder exists [no]
+ --enable-libxml2 enable XML parsing using the C library libxml2, needed
+@@ -1747,6 +1748,8 @@ EXTERNAL_AUTODETECT_LIBRARY_LIST="
+ libxcb_shm
+ libxcb_shape
+ libxcb_xfixes
++ libpipewire
++ libgio_unix
+ lzma
+ mediafoundation
+ metal
+@@ -3709,6 +3712,7 @@ pad_opencl_filter_deps="opencl"
+ pan_filter_deps="swresample"
+ perspective_filter_deps="gpl"
+ phase_filter_deps="gpl"
++pipewiregrab_filter_deps="libpipewire libgio_unix pthreads"
+ pp7_filter_deps="gpl"
+ pp_filter_deps="gpl postproc"
+ prewitt_opencl_filter_deps="opencl"
+@@ -6928,6 +6932,18 @@ if enabled libxcb; then
+ enabled libxcb_xfixes && check_pkg_config libxcb_xfixes xcb-xfixes xcb/xfixes.h xcb_xfixes_get_cursor_image
+ fi
+
++# Starting with version 0.3.52, PipeWire's spa library uses the __LOCALE_C_ONLY macro to determine
++# whether the locale_t type (introduced in POSIX.1-2008) and some related functions are available (see
++# https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/2390 for more information).
++# Unfortunately, this macro is specific to uclibc, which can cause build issues on systems that use a
++# different implementation of libc if POSIX 2008 support isn't enabled (which is the case for FFmpeg currently).
++# As a workaround for this problem, we add a compilation flag to ensure that __LOCALE_C_ONLY is always defined.
++add_cppflags -D__LOCALE_C_ONLY
++enabled libpipewire && check_pkg_config libpipewire "libpipewire-0.3 >= 0.3.40" pipewire/pipewire.h pw_init
++if enabled libpipewire; then
++ enabled libgio_unix && check_pkg_config libgio_unix gio-unix-2.0 gio/gio.h g_main_loop_new
++fi
++
+ check_func_headers "windows.h" CreateDIBSection "$gdigrab_indev_extralibs"
+
+ # d3d11va requires linking directly to dxgi and d3d11 if not building for
+diff --git a/libavfilter/Makefile b/libavfilter/Makefile
+index b3d3d981dd..abe7c3cd0d 100644
+--- a/libavfilter/Makefile
++++ b/libavfilter/Makefile
+@@ -583,6 +583,7 @@ OBJS-$(CONFIG_NULLSRC_FILTER) += vsrc_testsrc.o
+ OBJS-$(CONFIG_OPENCLSRC_FILTER) += vf_program_opencl.o opencl.o
+ OBJS-$(CONFIG_PAL75BARS_FILTER) += vsrc_testsrc.o
+ OBJS-$(CONFIG_PAL100BARS_FILTER) += vsrc_testsrc.o
++OBJS-$(CONFIG_PIPEWIREGRAB_FILTER) += vsrc_pipewiregrab.o
+ OBJS-$(CONFIG_RGBTESTSRC_FILTER) += vsrc_testsrc.o
+ OBJS-$(CONFIG_SIERPINSKI_FILTER) += vsrc_sierpinski.o
+ OBJS-$(CONFIG_SMPTEBARS_FILTER) += vsrc_testsrc.o
+diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
+index d7db46c2af..87204fec71 100644
+--- a/libavfilter/allfilters.c
++++ b/libavfilter/allfilters.c
+@@ -548,6 +548,7 @@ extern const AVFilter ff_vsrc_nullsrc;
+ extern const AVFilter ff_vsrc_openclsrc;
+ extern const AVFilter ff_vsrc_pal75bars;
+ extern const AVFilter ff_vsrc_pal100bars;
++extern const AVFilter ff_vsrc_pipewiregrab;
+ extern const AVFilter ff_vsrc_rgbtestsrc;
+ extern const AVFilter ff_vsrc_sierpinski;
+ extern const AVFilter ff_vsrc_smptebars;
+diff --git a/libavfilter/vsrc_pipewiregrab.c b/libavfilter/vsrc_pipewiregrab.c
+new file mode 100644
+index 0000000000..ff9c3468ab
+--- /dev/null
++++ b/libavfilter/vsrc_pipewiregrab.c
+@@ -0,0 +1,1373 @@
++/*
++ * PipeWire input grabber (ScreenCast)
++ * Copyright (C) 2024 Savoir-faire Linux, Inc.
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg 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.
++ *
++ * FFmpeg 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 FFmpeg; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/**
++ * @file
++ * PipeWireGrab video source
++ * @author Firas Ashkar <firas.ashkar at savoirfairelinux.com>
++ * @author Abhishek Ojha <abhishek.ojha at savoirfairelinux.com>
++ * @author François-Simon Fauteux-Chapleau <francois-simon.fauteux-chapleau at savoirfairelinux.com>
++ */
++
++#include "config.h"
++
++#include <fcntl.h>
++#include <linux/dma-buf.h>
++#include <math.h>
++#include <pthread.h>
++#include <stdatomic.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/mman.h>
++#include <sys/queue.h>
++
++#include "libavutil/internal.h"
++#include "libavutil/mathematics.h"
++#include "libavutil/opt.h"
++#include "libavutil/parseutils.h"
++#include "libavutil/time.h"
++#include "libavutil/avstring.h"
++#include "libavformat/avformat.h"
++#include "libavformat/internal.h"
++#include "libavutil/avassert.h"
++#include "avfilter.h"
++#include "internal.h"
++
++#include <pipewire/pipewire.h>
++#include <pipewire/thread-loop.h>
++#include <spa/debug/types.h>
++#include <spa/param/video/format-utils.h>
++#include <spa/param/video/raw.h>
++#include <spa/param/video/type-info.h>
++
++#include <gio/gio.h>
++#include <gio/gunixfdlist.h>
++
++#ifndef __USE_XOPEN2K8
++#define F_DUPFD_CLOEXEC \
++ 1030 /* Duplicate file descriptor with close-on-exit set. */
++#endif
++
++#define REQUEST_PATH "/org/freedesktop/portal/desktop/request/%s/%s"
++#define BYTES_PER_PIXEL 4 /* currently all formats assume 4 bytes per pixel */
++#define MAX_SPA_PARAM 4 /* max number of params for spa pod */
++
++#define CURSOR_META_SIZE(width, height) \
++ (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + \
++ width * height * 4)
++
++/**
++ * PipeWire capture types
++ */
++typedef enum {
++ DESKTOP_CAPTURE = 1,
++ WINDOW_CAPTURE = 2,
++} pw_capture_type;
++
++/**
++ * XDG Desktop Portal supported cursor modes
++ */
++enum PortalCursorMode {
++ PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
++ PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
++};
++
++typedef struct PipewireGrabContext {
++ const AVClass *class;
++
++ pthread_cond_t pipewire_initialization_cond_var;
++ pthread_mutex_t pipewire_initialization_mutex;
++ atomic_int pipewire_initialization_over;
++
++ int pw_init_called;
++
++ pthread_mutex_t current_frame_mutex;
++ AVFrame *current_frame;
++
++ GDBusConnection *connection;
++ GDBusProxy *proxy;
++ GCancellable *cancellable;
++
++ char *sender_name;
++ char *session_handle;
++
++ uint64_t pipewire_external_node; // only needed because FFmpeg doesn't have a uint32 AVOption type
++ uint32_t pipewire_node;
++ int pipewire_fd;
++
++ uint32_t available_cursor_modes;
++
++ GMainLoop *glib_main_loop;
++ struct pw_thread_loop *thread_loop;
++ struct pw_context *context;
++
++ struct pw_core *core;
++ struct spa_hook core_listener;
++
++ struct pw_stream *stream;
++ struct spa_hook stream_listener;
++ struct spa_video_info format;
++
++ pw_capture_type capture_type;
++
++ int draw_mouse;
++
++ uint32_t width, height;
++
++ size_t frame_size;
++ uint8_t Bpp;
++ enum AVPixelFormat av_pxl_format;
++
++ int64_t time_frame;
++ int64_t frame_duration;
++
++ AVRational framerate;
++
++ int portal_error;
++ int pipewire_error;
++} PipewireGrabContext;
++
++/**
++ * DBus method/event marshalling structure
++ */
++struct DbusCallData {
++ AVFilterContext *ctx;
++ char *request_path;
++ guint signal_id;
++ gulong cancelled_id;
++};
++
++
++#define OFFSET(x) offsetof(PipewireGrabContext, x)
++#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
++static const AVOption pipewiregrab_options[] = {
++ { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, { .str = "ntsc" }, 0, INT_MAX, FLAGS },
++ { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS },
++ { "capture_type", "set the capture type (1 for screen, 2 for window)", OFFSET(capture_type), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 2, FLAGS },
++ { "fd", "set file descriptor to be used by PipeWire", OFFSET(pipewire_fd), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
++ { "node", "set PipeWire node", OFFSET(pipewire_external_node), AV_OPT_TYPE_UINT64, { .i64 = 0 }, 0, 0xffffffff, FLAGS },
++ { NULL },
++};
++
++AVFILTER_DEFINE_CLASS(pipewiregrab);
++
++/**
++ * Helper function to allow portal_init_screencast to stop and return an error
++ * code if a DBus operation/callback fails.
++ *
++ * @param ctx
++ * @param error error code
++ * @param message error message
++ */
++static void portal_abort(AVFilterContext *ctx, int error, const char *message)
++{
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ pw_ctx->portal_error = error;
++ av_log(ctx, AV_LOG_ERROR, "Aborting: %s\n", message);
++
++ if (pw_ctx->glib_main_loop &&
++ g_main_loop_is_running(pw_ctx->glib_main_loop))
++ g_main_loop_quit(pw_ctx->glib_main_loop);
++}
++
++/**
++ * Callback to handle PipeWire core info events
++ *
++ * @param user_data pointer to the filter's AVFilterContext
++ * @param info pw_core_info
++ */
++static void on_core_info_callback(void *user_data, const struct pw_core_info *info)
++{
++ AVFilterContext *ctx = user_data;
++ av_log(ctx, AV_LOG_DEBUG, "Server version: %s\n", info->version);
++ av_log(ctx, AV_LOG_INFO, "Library version: %s\n", pw_get_library_version());
++ av_log(ctx, AV_LOG_DEBUG, "Header version: %s\n", pw_get_headers_version());
++}
++
++/**
++ * Callback to handle PipeWire core done events
++ *
++ * @param user_data pointer to the filter's AVFilterContext
++ * @param id PipeWire object id of calling
++ * @param seq PipeWire object sequence
++ */
++static void on_core_done_callback(void *user_data, uint32_t id, int seq)
++{
++ AVFilterContext *ctx = user_data;
++ PipewireGrabContext *pw_ctx;
++
++ if (!ctx || !ctx->priv)
++ return;
++
++ pw_ctx = ctx->priv;
++
++ if (id == PW_ID_CORE)
++ pw_thread_loop_signal(pw_ctx->thread_loop, false);
++}
++
++/**
++ * Callback to handle Pipewire core error events
++ *
++ * @param user_data pointer to the filter's AVFilterContext
++ * @param id PipeWire object id of calling
++ * @param seq PipeWire object sequence
++ * @param res error number
++ * @param message error message
++ */
++static void on_core_error_callback(void *user_data, uint32_t id, int seq,
++ int res, const char *message)
++{
++ AVFilterContext *ctx = user_data;
++ PipewireGrabContext *pw_ctx;
++
++ if (!ctx)
++ return;
++
++ av_log(ctx, AV_LOG_ERROR,
++ "PipeWire core error: %s (id=%u, seq=%d, res=%d: %s)\n",
++ message, id, seq, res, g_strerror(-res));
++
++ pw_ctx = ctx->priv;
++ if (!pw_ctx) {
++ av_log(ctx, AV_LOG_ERROR,
++ "Invalid private context data\n");
++ return;
++ }
++
++ pw_thread_loop_signal(pw_ctx->thread_loop, false);
++
++ pw_ctx->pipewire_error = res;
++ atomic_store(&pw_ctx->pipewire_initialization_over, 1);
++ pthread_cond_signal(&pw_ctx->pipewire_initialization_cond_var);
++}
++
++/**
++ * PipeWire core events callbacks
++ */
++static const struct pw_core_events core_events = {
++ PW_VERSION_CORE_EVENTS,
++ .info = on_core_info_callback,
++ .done = on_core_done_callback,
++ .error = on_core_error_callback,
++};
++
++/**
++ * Helper function: convert spa video format to AVPixelFormat
++ *
++ * @param video_format spa video format to convert
++ * @return the corresponding AVPixelFormat
++ */
++static enum AVPixelFormat
++spa_video_format_to_av_pixel_format(enum spa_video_format video_format)
++{
++ switch (video_format) {
++ case SPA_VIDEO_FORMAT_RGBA:
++ case SPA_VIDEO_FORMAT_RGBx:
++ return AV_PIX_FMT_RGBA;
++
++ case SPA_VIDEO_FORMAT_BGRA:
++ case SPA_VIDEO_FORMAT_BGRx:
++ return AV_PIX_FMT_BGRA;
++
++ default:
++ return AV_PIX_FMT_NONE;
++ }
++}
++
++/**
++ * Callback to free a DbusCallData object's memory and unsubscribe from the
++ * associated dbus signal.
++ *
++ * @param ptr_dbus_call_data DBus marshalling structure
++ */
++static void dbus_call_data_free(struct DbusCallData *ptr_dbus_call_data)
++{
++ AVFilterContext *ctx;
++ PipewireGrabContext *pw_ctx;
++
++ if (!ptr_dbus_call_data)
++ return;
++
++ ctx = ptr_dbus_call_data->ctx;
++ if (!ctx || !ctx->priv)
++ return;
++
++ pw_ctx = ctx->priv;
++
++ if (ptr_dbus_call_data->signal_id)
++ g_dbus_connection_signal_unsubscribe(pw_ctx->connection,
++ ptr_dbus_call_data->signal_id);
++
++ if (ptr_dbus_call_data->cancelled_id > 0)
++ g_signal_handler_disconnect(pw_ctx->cancellable,
++ ptr_dbus_call_data->cancelled_id);
++
++ g_clear_pointer(&ptr_dbus_call_data->request_path, g_free);
++ av_free(ptr_dbus_call_data);
++}
++
++/**
++ * DBus callback of cancelled events
++ *
++ * @param cancellable (not used)
++ * @param user_data DBus marshalling structure
++ */
++static void on_cancelled_callback(GCancellable *cancellable, gpointer user_data)
++{
++ struct DbusCallData *ptr_dbus_call_data = user_data;
++ AVFilterContext *ctx = ptr_dbus_call_data->ctx;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ if (!pw_ctx)
++ return;
++
++ g_dbus_connection_call(pw_ctx->connection, "org.freedesktop.portal.Desktop",
++ ptr_dbus_call_data->request_path,
++ "org.freedesktop.portal.Request", "Close", NULL,
++ NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
++
++ av_log(ctx, AV_LOG_WARNING, "Portal request cancelled\n");
++
++ pw_ctx->portal_error = ECANCELED;
++ g_main_loop_quit(pw_ctx->glib_main_loop);
++}
++
++/**
++ * PipeWire callback of parameters changed events
++ *
++ * @param user_data DBus marshalling structure
++ * @param id contains chan param type
++ * @param param pointer to changed param structure
++ */
++static void on_stream_param_changed_callback(void *user_data, uint32_t id,
++ const struct spa_pod *param)
++{
++ struct spa_pod_builder pod_builder;
++ const struct spa_pod *params[MAX_SPA_PARAM];
++ uint32_t n_params = 0;
++ uint8_t params_buffer[4096];
++ int result;
++ PipewireGrabContext *pw_ctx;
++ AVFilterContext *ctx = user_data;
++
++ if (!ctx || !ctx->priv || !param)
++ return;
++
++ if (id != SPA_PARAM_Format) {
++ av_log(ctx, AV_LOG_WARNING,
++ "Ignoring non-Format param change\n");
++ return;
++ }
++
++ pw_ctx = ctx->priv;
++
++ result = spa_format_parse(param, &pw_ctx->format.media_type,
++ &pw_ctx->format.media_subtype);
++ if (result < 0) {
++ av_log(ctx, AV_LOG_ERROR, "Unable to parse media type\n");
++ pw_ctx->pipewire_error = AVERROR(EINVAL);
++ goto end;
++ }
++
++ if (pw_ctx->format.media_type != SPA_MEDIA_TYPE_video ||
++ pw_ctx->format.media_subtype != SPA_MEDIA_SUBTYPE_raw) {
++ av_log(ctx, AV_LOG_ERROR, "Unexpected media type\n");
++ pw_ctx->pipewire_error = AVERROR(EINVAL);
++ goto end;
++ }
++
++ spa_format_video_raw_parse(param, &pw_ctx->format.info.raw);
++
++ av_log(ctx, AV_LOG_INFO, "Negotiated format:\n");
++
++ av_log(ctx, AV_LOG_INFO, "Format: %d (%s)\n",
++ pw_ctx->format.info.raw.format,
++ spa_debug_type_find_name(spa_type_video_format,
++ pw_ctx->format.info.raw.format));
++ av_log(ctx, AV_LOG_INFO, "Size: %dx%d\n",
++ pw_ctx->format.info.raw.size.width,
++ pw_ctx->format.info.raw.size.height);
++ av_log(ctx, AV_LOG_INFO, "Framerate: %d/%d\n",
++ pw_ctx->format.info.raw.framerate.num,
++ pw_ctx->format.info.raw.framerate.denom);
++
++ pw_ctx->width = pw_ctx->format.info.raw.size.width;
++ pw_ctx->height = pw_ctx->format.info.raw.size.height;
++ pw_ctx->Bpp = BYTES_PER_PIXEL;
++ pw_ctx->frame_size = pw_ctx->width * pw_ctx->height * pw_ctx->Bpp;
++ if (pw_ctx->frame_size + AV_INPUT_BUFFER_PADDING_SIZE > INT_MAX) {
++ av_log(ctx, AV_LOG_ERROR, "Captured area is too large\n");
++ pw_ctx->pipewire_error = AVERROR(EINVAL);
++ goto end;
++ }
++
++ pw_ctx->av_pxl_format =
++ spa_video_format_to_av_pixel_format(pw_ctx->format.info.raw.format);
++ if (pw_ctx->av_pxl_format == AV_PIX_FMT_NONE) {
++ av_log(ctx, AV_LOG_ERROR,
++ "Unsupported buffer format: %d\n", pw_ctx->format.info.raw.format);
++ pw_ctx->pipewire_error = AVERROR(EINVAL);
++ goto end;
++ }
++
++ /* Video crop */
++ pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
++ params[n_params++] = spa_pod_builder_add_object(
++ &pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
++ SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoCrop),
++ SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_region)));
++
++ /* Cursor */
++ params[n_params++] = spa_pod_builder_add_object(
++ &pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
++ SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Cursor), SPA_PARAM_META_size,
++ SPA_POD_CHOICE_RANGE_Int(CURSOR_META_SIZE(64, 64),
++ CURSOR_META_SIZE(1, 1),
++ CURSOR_META_SIZE(1024, 1024)));
++
++ /* Buffer options */
++ params[n_params++] = spa_pod_builder_add_object(
++ &pod_builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
++ SPA_PARAM_BUFFERS_dataType,
++ SPA_POD_Int((1 << SPA_DATA_MemPtr) | (1 << SPA_DATA_MemFd)));
++
++ /* Meta header */
++ params[n_params++] = spa_pod_builder_add_object(
++ &pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
++ SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
++ SPA_PARAM_META_size,
++ SPA_POD_Int(sizeof(struct spa_meta_header)));
++
++ pw_stream_update_params(pw_ctx->stream, params, n_params);
++
++end:
++ // Signal pipewiregrab_init that PipeWire initialization is over (either
++ // because it was completed successfully or because there was an error, in
++ // which case pw_ctx->pipewire_error will have been set to a nonzero value).
++ atomic_store(&pw_ctx->pipewire_initialization_over, 1);
++ pthread_cond_signal(&pw_ctx->pipewire_initialization_cond_var);
++}
++
++/**
++ * PipeWire callback of state changed events
++ *
++ * @param user_data DBus marshalling structure
++ * @param old PipeWire stream old state
++ * @param state PipeWire stream current state
++ * @param error received error information
++ */
++static void on_stream_state_changed_callback(void *user_data,
++ enum pw_stream_state old,
++ enum pw_stream_state state,
++ const char *error)
++{
++ AVFilterContext *ctx = user_data;
++ if (!ctx)
++ return;
++
++ av_log(ctx, AV_LOG_INFO, "stream state: \"%s\"\n",
++ pw_stream_state_as_string(state));
++}
++
++/**
++ * Find most recent buffer received in a PipeWire stream
++ *
++ * @param stream stream to get buffer from
++ * @return most recent buffer in the stream
++ */
++static struct pw_buffer *find_most_recent_buffer_and_recycle_olders(struct pw_stream *stream)
++{
++ struct pw_buffer *pw_buf = NULL;
++ while (1) {
++ struct pw_buffer *aux = pw_stream_dequeue_buffer(stream);
++ if (!aux)
++ break;
++ if (pw_buf)
++ pw_stream_queue_buffer(stream, pw_buf);
++ pw_buf = aux;
++ }
++ return pw_buf;
++}
++
++/**
++ * Our data processing function
++ *
++ * @param user_data DBus marshalling structure
++ */
++static void on_stream_process_callback(void *user_data)
++{
++ struct spa_buffer *spa_buf = NULL;
++ struct pw_buffer *pw_buf = NULL;
++ uint8_t *map = NULL;
++ void *sdata = NULL;
++ struct spa_meta_header *header = NULL;
++ struct spa_meta_region *frame_region;
++ uint32_t frame_width;
++ uint32_t frame_height;
++ AVFilterContext *ctx = user_data;
++ PipewireGrabContext *pw_ctx = NULL;
++
++ if (!ctx || !ctx->priv)
++ return;
++
++ pw_ctx = ctx->priv;
++
++ // We need to wait for pw_ctx->current_frame to have been allocated before
++ // we can use it to get frames from the PipeWire thread to FFmpeg
++ pthread_mutex_lock(&pw_ctx->current_frame_mutex);
++ if (!pw_ctx->current_frame) {
++ pthread_mutex_unlock(&pw_ctx->current_frame_mutex);
++ return;
++ }
++ pthread_mutex_unlock(&pw_ctx->current_frame_mutex);
++
++ // Get a buffer from PipeWire
++ pw_buf = find_most_recent_buffer_and_recycle_olders(pw_ctx->stream);
++ if (!pw_buf) {
++ av_log(ctx, AV_LOG_ERROR, "Out of buffers\n");
++ return;
++ }
++
++ // Check whether the buffer is corrupted
++ spa_buf = pw_buf->buffer;
++ header = spa_buffer_find_meta_data(spa_buf,
++ SPA_META_Header, sizeof(*header));
++ if (header && (header->flags & SPA_META_HEADER_FLAG_CORRUPTED)) {
++ av_log(ctx, AV_LOG_ERROR, "Corrupted PipeWire buffer\n");
++ goto end;
++ }
++
++ // Get a pointer to the buffer's data
++ if (spa_buf->datas[0].type == SPA_DATA_MemFd ) {
++ map = mmap(NULL, spa_buf->datas[0].maxsize + spa_buf->datas[0].mapoffset,
++ PROT_READ, MAP_PRIVATE, spa_buf->datas[0].fd, 0);
++ if (map == MAP_FAILED) {
++ av_log(ctx, AV_LOG_ERROR, "mmap failed! error %s\n", g_strerror(errno));
++ goto end;
++ }
++ sdata = SPA_PTROFF(map, spa_buf->datas[0].mapoffset, uint8_t);
++ } else if (spa_buf->datas[0].type == SPA_DATA_MemPtr) {
++ if (spa_buf->datas[0].data == NULL) {
++ av_log(ctx, AV_LOG_ERROR, "No data\n");
++ goto end;
++ }
++ map = NULL;
++ sdata = spa_buf->datas[0].data;
++ } else {
++ av_log(ctx, AV_LOG_ERROR, "Buffer is not valid\n");
++ goto end;
++ }
++
++ if ((frame_region = spa_buffer_find_meta_data(spa_buf, SPA_META_VideoCrop,
++ sizeof(*frame_region)))
++ && spa_meta_region_is_valid(frame_region)) {
++ frame_width = frame_region->region.size.width;
++ frame_height = frame_region->region.size.height;
++ } else {
++ frame_width = pw_ctx->width;
++ frame_height = pw_ctx->height;
++ }
++
++ // Update current_frame with the new data
++ pthread_mutex_lock(&pw_ctx->current_frame_mutex);
++ memcpy(pw_ctx->current_frame->data[0], sdata, spa_buf->datas[0].chunk->size);
++ pw_ctx->current_frame->width = frame_width;
++ pw_ctx->current_frame->height = frame_height;
++ pthread_mutex_unlock(&pw_ctx->current_frame_mutex);
++
++ // Cleanup
++ if (spa_buf->datas[0].type == SPA_DATA_MemFd)
++ munmap(map, spa_buf->datas[0].maxsize + spa_buf->datas[0].mapoffset);
++end:
++ pw_stream_queue_buffer(pw_ctx->stream, pw_buf);
++ return;
++}
++
++static const struct pw_stream_events stream_events = {
++ PW_VERSION_STREAM_EVENTS,
++ .state_changed = on_stream_state_changed_callback,
++ .param_changed = on_stream_param_changed_callback,
++ .process = on_stream_process_callback,
++};
++
++static struct DbusCallData *subscribe_to_signal(AVFilterContext *ctx,
++ const char *path,
++ GDBusSignalCallback callback)
++{
++ struct DbusCallData *ptr_dbus_call_data;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ ptr_dbus_call_data = (struct DbusCallData *)av_mallocz(sizeof(struct DbusCallData));
++ if (!ptr_dbus_call_data)
++ return NULL;
++
++ ptr_dbus_call_data->ctx = ctx;
++ ptr_dbus_call_data->request_path = g_strdup(path);
++ ptr_dbus_call_data->cancelled_id =
++ g_signal_connect(pw_ctx->cancellable, "cancelled",
++ G_CALLBACK(on_cancelled_callback),
++ ptr_dbus_call_data /* user_data */);
++ ptr_dbus_call_data->signal_id = g_dbus_connection_signal_subscribe(
++ pw_ctx->connection, "org.freedesktop.portal.Desktop" /*sender*/,
++ "org.freedesktop.portal.Request" /*interface_name*/,
++ "Response" /*member: dbus signal name*/,
++ ptr_dbus_call_data->request_path /*object_path*/, NULL,
++ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, callback, ptr_dbus_call_data, NULL);
++
++ return ptr_dbus_call_data;
++}
++
++static int play_pipewire_stream(AVFilterContext *ctx)
++{
++ int ret;
++ const struct spa_pod *ptr_spa_pod;
++ uint8_t buffer[4096];
++ struct spa_pod_builder spa_pod_bldr = {
++ 0,
++ };
++
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ pw_init(NULL, NULL);
++ pw_ctx->pw_init_called = 1;
++
++ pw_ctx->thread_loop =
++ pw_thread_loop_new("thread loop", NULL);
++ if (!pw_ctx->thread_loop) {
++ av_log(ctx, AV_LOG_ERROR, "pw_thread_loop_new failed\n");
++ return AVERROR(ENOMEM);
++ }
++
++ pw_ctx->context =
++ pw_context_new(pw_thread_loop_get_loop(pw_ctx->thread_loop), NULL, 0);
++ if (!pw_ctx->context) {
++ av_log(ctx, AV_LOG_ERROR, "pw_context_new failed\n");
++ ret = AVERROR(ENOMEM);
++ goto fail;
++ }
++
++ if (pw_thread_loop_start(pw_ctx->thread_loop) < 0) {
++ av_log(ctx, AV_LOG_ERROR, "pw_thread_loop_start failed\n");
++ ret = AVERROR(EFAULT);
++ goto fail;
++ }
++
++ pw_thread_loop_lock(pw_ctx->thread_loop);
++
++ // Core
++ pw_ctx->core =
++ pw_context_connect_fd(pw_ctx->context,
++ fcntl(pw_ctx->pipewire_fd, F_DUPFD_CLOEXEC, 3),
++ NULL, 0);
++ if (!pw_ctx->core) {
++ ret = AVERROR(errno);
++ av_log(ctx, AV_LOG_ERROR, "pw_context_connect_fd failed\n");
++ pw_thread_loop_unlock(pw_ctx->thread_loop);
++ goto fail;
++ }
++
++ pw_core_add_listener(pw_ctx->core, &pw_ctx->core_listener, &core_events,
++ ctx /* user_data */);
++
++ // Stream
++ pw_ctx->stream = pw_stream_new(
++ pw_ctx->core, "wayland grab",
++ pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", PW_KEY_MEDIA_CATEGORY,
++ "Capture", PW_KEY_MEDIA_ROLE, "Screen", NULL));
++
++ if (!pw_ctx->stream) {
++ av_log(ctx, AV_LOG_ERROR, "pw_stream_new failed\n");
++ ret = AVERROR(ENOMEM);
++ pw_thread_loop_unlock(pw_ctx->thread_loop);
++ goto fail;
++ }
++
++ pw_stream_add_listener(pw_ctx->stream, &pw_ctx->stream_listener,
++ &stream_events, ctx /* user_data */);
++
++ // Stream parameters
++ spa_pod_bldr = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
++ ptr_spa_pod = spa_pod_builder_add_object(
++ &spa_pod_bldr, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
++ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
++ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
++ SPA_FORMAT_VIDEO_format,
++ SPA_POD_CHOICE_ENUM_Id(4, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_RGBx,
++ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA),
++ SPA_FORMAT_VIDEO_size,
++ SPA_POD_CHOICE_RANGE_Rectangle(&SPA_RECTANGLE(320, 240),
++ &SPA_RECTANGLE(1, 1),
++ &SPA_RECTANGLE(4096, 4096)),
++ SPA_FORMAT_VIDEO_framerate,
++ SPA_POD_CHOICE_RANGE_Fraction(
++ &SPA_FRACTION(pw_ctx->framerate.num,
++ pw_ctx->framerate.den),
++ &SPA_FRACTION(0, 1), &SPA_FRACTION(144, 1)));
++
++ ret = pw_stream_connect(
++ pw_ctx->stream, PW_DIRECTION_INPUT, pw_ctx->pipewire_node,
++ PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS, &ptr_spa_pod, 1);
++ if (ret != 0) {
++ av_log(ctx, AV_LOG_ERROR, "pw_stream_connect failed\n");
++ pw_thread_loop_unlock(pw_ctx->thread_loop);
++ goto fail;
++ }
++
++ av_log(ctx, AV_LOG_INFO, "Starting screen capture ...\n");
++ pw_thread_loop_unlock(pw_ctx->thread_loop);
++ return 0;
++
++fail:
++ if (pw_ctx->core) {
++ pw_core_disconnect(pw_ctx->core);
++ pw_ctx->core = NULL;
++ }
++ if (pw_ctx->context) {
++ pw_context_destroy(pw_ctx->context);
++ pw_ctx->context = NULL;
++ }
++ if (pw_ctx->thread_loop) {
++ pw_thread_loop_destroy(pw_ctx->thread_loop);
++ pw_ctx->thread_loop = NULL;
++ }
++
++ return ret;
++}
++
++static void portal_open_pipewire_remote(AVFilterContext *ctx)
++{
++ GUnixFDList* fd_list = NULL;
++ GVariant* result = NULL;
++ GError* error = NULL;
++ int fd_index;
++ GVariantBuilder builder;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
++
++ result = g_dbus_proxy_call_with_unix_fd_list_sync(
++ pw_ctx->proxy, "OpenPipeWireRemote",
++ g_variant_new("(oa{sv})", pw_ctx->session_handle, &builder),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &fd_list, pw_ctx->cancellable,
++ &error);
++ if (error)
++ goto fail;
++
++ g_variant_get(result, "(h)", &fd_index);
++ g_variant_unref(result);
++
++ pw_ctx->pipewire_fd = g_unix_fd_list_get(fd_list, fd_index, &error);
++ g_object_unref(fd_list);
++ if (error)
++ goto fail;
++
++ g_main_loop_quit(pw_ctx->glib_main_loop);
++ return;
++
++fail:
++ av_log(ctx, AV_LOG_ERROR,
++ "Error retrieving PipeWire fd: %s\n", error->message);
++ g_error_free(error);
++ portal_abort(ctx, EIO, "Failed to open PipeWire remote");
++}
++
++static void on_start_response_received_callback(
++ GDBusConnection *connection, const char *sender_name,
++ const char *object_path, const char *interface_name,
++ const char *signal_name, GVariant *parameters, gpointer user_data)
++{
++ GVariant* stream_properties = NULL;
++ GVariant* streams = NULL;
++ GVariant* result = NULL;
++ GVariantIter iter;
++ uint32_t response;
++
++ struct DbusCallData *ptr_dbus_call_data = user_data;
++ AVFilterContext *ctx = ptr_dbus_call_data->ctx;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ if (!pw_ctx) {
++ portal_abort(ctx, EINVAL, "Invalid private context data");
++ return;
++ }
++
++ g_clear_pointer(&ptr_dbus_call_data, dbus_call_data_free);
++
++ g_variant_get(parameters, "(u@a{sv})", &response, &result);
++
++ if (response) {
++ g_variant_unref(result);
++ portal_abort(
++ ctx, EACCES, "Failed to start screencast, denied or cancelled by user");
++ return;
++ }
++
++ streams = g_variant_lookup_value(result, "streams", G_VARIANT_TYPE_ARRAY);
++
++ g_variant_iter_init(&iter, streams);
++ av_assert0(g_variant_iter_n_children(&iter) == 1);
++
++ g_variant_iter_loop(&iter, "(u@a{sv})", &pw_ctx->pipewire_node,
++ &stream_properties);
++
++ av_log(ctx, AV_LOG_INFO, "Monitor selected, setting up screencast\n\n");
++
++ g_variant_unref(result);
++ g_variant_unref(streams);
++ g_variant_unref(stream_properties);
++
++ portal_open_pipewire_remote(ctx);
++}
++
++static int portal_call_dbus_method(AVFilterContext *ctx,
++ const gchar *method_name, GVariant *parameters)
++{
++ GVariant* result;
++ GError* error = NULL;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ result = g_dbus_proxy_call_sync(pw_ctx->proxy, method_name, parameters,
++ G_DBUS_CALL_FLAGS_NONE, -1,
++ pw_ctx->cancellable, &error);
++ if (error) {
++ av_log(ctx, AV_LOG_ERROR,
++ "Call to DBus method '%s' failed: %s\n",
++ method_name, error->message);
++ g_error_free(error);
++ return EIO;
++ }
++ g_variant_unref(result);
++ return 0;
++}
++
++static void portal_start(AVFilterContext *ctx)
++{
++ int ret;
++ const char *request_token;
++ g_autofree char *request_path;
++ GVariantBuilder builder;
++ GVariant *parameters;
++ struct DbusCallData *ptr_dbus_call_data;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ if (!pw_ctx) {
++ portal_abort(ctx, EINVAL, "Invalid private context data");
++ return;
++ }
++
++ request_token = "pipewiregrabStart";
++ request_path = g_strdup_printf(REQUEST_PATH, pw_ctx->sender_name, request_token);
++
++ av_log(ctx, AV_LOG_INFO, "Asking for monitor…\n");
++
++ ptr_dbus_call_data = subscribe_to_signal(ctx, request_path,
++ on_start_response_received_callback);
++ if (!ptr_dbus_call_data) {
++ portal_abort(ctx, ENOMEM, "Failed to allocate DBus call data");
++ return;
++ }
++
++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
++ g_variant_builder_add(&builder, "{sv}", "handle_token",
++ g_variant_new_string(request_token));
++ parameters = g_variant_new("(osa{sv})", pw_ctx->session_handle, "", &builder);
++
++ ret = portal_call_dbus_method(ctx, "Start", parameters);
++ if (ret != 0)
++ portal_abort(ctx, ret, "Failed to start screen cast session");
++}
++
++static void on_select_sources_response_received_callback(
++ GDBusConnection *connection, const char *sender_name,
++ const char *object_path, const char *interface_name,
++ const char *signal_name, GVariant *parameters, gpointer user_data)
++{
++ GVariant* ret = NULL;
++ uint32_t response;
++ struct DbusCallData *ptr_dbus_call_data = user_data;
++ AVFilterContext *ctx = ptr_dbus_call_data->ctx;
++
++ av_log(ctx, AV_LOG_INFO,
++ "Response to select source received\n");
++
++ g_clear_pointer(&ptr_dbus_call_data, dbus_call_data_free);
++
++ g_variant_get(parameters, "(u@a{sv})", &response, &ret);
++ g_variant_unref(ret);
++ if (response) {
++ portal_abort(
++ ctx, EACCES, "Failed to select screencast sources, denied or cancelled by user");
++ return;
++ }
++
++ portal_start(ctx);
++}
++
++static void portal_select_sources(AVFilterContext *ctx)
++{
++ int ret;
++ const char *request_token;
++ g_autofree char *request_path;
++ GVariantBuilder builder;
++ GVariant *parameters;
++ struct DbusCallData *ptr_dbus_call_data;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ request_token = "pipewiregrabSelectSources";
++ request_path = g_strdup_printf(REQUEST_PATH, pw_ctx->sender_name, request_token);
++
++ ptr_dbus_call_data = subscribe_to_signal(ctx, request_path,
++ on_select_sources_response_received_callback);
++ if (!ptr_dbus_call_data) {
++ portal_abort(ctx, ENOMEM, "Failed to allocate DBus call data");
++ return;
++ }
++
++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
++ g_variant_builder_add(&builder, "{sv}", "types",
++ g_variant_new_uint32(pw_ctx->capture_type));
++ g_variant_builder_add(&builder, "{sv}", "multiple",
++ g_variant_new_boolean(FALSE));
++ g_variant_builder_add(&builder, "{sv}", "handle_token",
++ g_variant_new_string(request_token));
++
++ if ((pw_ctx->available_cursor_modes & PORTAL_CURSOR_MODE_EMBEDDED)
++ && pw_ctx->draw_mouse)
++ g_variant_builder_add(&builder, "{sv}", "cursor_mode",
++ g_variant_new_uint32(PORTAL_CURSOR_MODE_EMBEDDED));
++ else
++ g_variant_builder_add(&builder, "{sv}", "cursor_mode",
++ g_variant_new_uint32(PORTAL_CURSOR_MODE_HIDDEN));
++ parameters = g_variant_new("(oa{sv})", pw_ctx->session_handle, &builder);
++
++ ret = portal_call_dbus_method(ctx, "SelectSources", parameters);
++ if (ret != 0)
++ portal_abort(ctx, ret, "Failed to select sources for screen cast session");
++}
++
++static void on_create_session_response_received_callback(
++ GDBusConnection *connection, const char *sender_name,
++ const char *object_path, const char *interface_name,
++ const char *signal_name, GVariant *parameters, gpointer user_data)
++{
++ uint32_t response;
++ GVariant* result = NULL;
++ struct DbusCallData *ptr_dbus_call_data = user_data;
++ AVFilterContext *ctx = ptr_dbus_call_data->ctx;
++
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ if (!pw_ctx) {
++ portal_abort(ctx, EINVAL, "Invalid private context data");
++ return;
++ }
++
++ g_clear_pointer(&ptr_dbus_call_data, dbus_call_data_free);
++
++ g_variant_get(parameters, "(u@a{sv})", &response, &result);
++
++ if (response != 0) {
++ g_variant_unref(result);
++ portal_abort(
++ ctx, EACCES, "Failed to create screencast session, denied or cancelled by user");
++ return;
++ }
++
++ av_log(ctx, AV_LOG_DEBUG, "Screencast session created\n");
++
++ g_variant_lookup(result, "session_handle", "s", &pw_ctx->session_handle);
++ g_variant_unref(result);
++
++ portal_select_sources(ctx);
++}
++
++/**
++ * Function to create a screen cast session
++ *
++ * @param ctx
++ */
++static void portal_create_session(AVFilterContext *ctx)
++{
++ int ret;
++ GVariantBuilder builder;
++ GVariant *parameters;
++ const char *request_token;
++ g_autofree char *request_path;
++ struct DbusCallData *ptr_dbus_call_data;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ request_token = "pipewiregrabCreateSession";
++ request_path = g_strdup_printf(REQUEST_PATH, pw_ctx->sender_name, request_token);
++
++ ptr_dbus_call_data = subscribe_to_signal(ctx, request_path,
++ on_create_session_response_received_callback);
++ if (!ptr_dbus_call_data) {
++ portal_abort(ctx, ENOMEM, "Failed to allocate DBus call data");
++ return;
++ }
++
++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
++ g_variant_builder_add(&builder, "{sv}", "handle_token",
++ g_variant_new_string(request_token));
++ g_variant_builder_add(&builder, "{sv}", "session_handle_token",
++ g_variant_new_string("pipewiregrab"));
++ parameters = g_variant_new("(a{sv})", &builder);
++
++ ret = portal_call_dbus_method(ctx, "CreateSession", parameters);
++ if (ret != 0)
++ portal_abort(ctx, ret, "Failed to create screen cast session");
++}
++
++/**
++ * Helper function: get available cursor modes and update the
++ * PipewireGrabContext accordingly
++ *
++ * @param ctx
++ */
++static void portal_update_available_cursor_modes(AVFilterContext *ctx)
++{
++ GVariant* cached_cursor_modes = NULL;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ cached_cursor_modes =
++ g_dbus_proxy_get_cached_property(pw_ctx->proxy, "AvailableCursorModes");
++
++ pw_ctx->available_cursor_modes =
++ cached_cursor_modes ? g_variant_get_uint32(cached_cursor_modes) : 0;
++
++ g_variant_unref(cached_cursor_modes);
++}
++
++static int create_dbus_proxy(AVFilterContext *ctx)
++{
++ GError* error = NULL;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ pw_ctx->proxy = g_dbus_proxy_new_sync(
++ pw_ctx->connection, G_DBUS_PROXY_FLAGS_NONE, NULL,
++ "org.freedesktop.portal.Desktop",
++ "/org/freedesktop/portal/desktop",
++ "org.freedesktop.portal.ScreenCast", NULL, &error);
++ if (error) {
++ av_log(ctx, AV_LOG_ERROR,
++ "Error creating proxy: %s\n", error->message);
++ g_error_free(error);
++ return EPERM;
++ }
++ return 0;
++}
++
++/**
++ * Create DBus connection and related objects
++ *
++ * @param ctx
++ */
++static int create_dbus_connection(AVFilterContext *ctx)
++{
++ char *aux;
++ GError* error = NULL;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++
++ pw_ctx->cancellable = g_cancellable_new();
++
++ pw_ctx->connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
++ if (error) {
++ av_log(ctx, AV_LOG_ERROR,
++ "Error getting session bus: %s\n", error->message);
++ g_error_free(error);
++ return EPERM;
++ }
++
++ aux = g_strdup(g_dbus_connection_get_unique_name(pw_ctx->connection) + 1);
++ pw_ctx->sender_name = av_strireplace(aux, ".", "_");
++ av_log(ctx, AV_LOG_DEBUG, "Initialized (sender name: %s)\n",
++ pw_ctx->sender_name);
++ return 0;
++}
++
++
++/**
++ * Use XDG Desktop Portal's ScreenCast interface to open a file descriptor that
++ * can be used by PipeWire to access the screen cast streams.
++ * (https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html)
++ *
++ * @param ctx
++ */
++static int portal_init_screencast(AVFilterContext *ctx)
++{
++ int ret = 0;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ GMainContext *glib_main_context;
++
++ // Create a new GLib context and set it as the default for the current thread.
++ // This ensures that the callbacks from DBus operations started in this thread are
++ // handled by the GLib main loop defined below, even if pipewiregrab_init was
++ // called by a program which also uses GLib and already had its own main loop running.
++ glib_main_context = g_main_context_new();
++ g_main_context_push_thread_default(glib_main_context);
++ pw_ctx->glib_main_loop = g_main_loop_new(glib_main_context, FALSE);
++ if (!pw_ctx->glib_main_loop) {
++ av_log(ctx, AV_LOG_ERROR, "g_main_loop_new failed\n");
++ ret = ENOMEM;
++ }
++
++ ret = create_dbus_connection(ctx);
++ if (ret != 0)
++ goto exit_glib_loop;
++
++ ret = create_dbus_proxy(ctx);
++ if (ret != 0)
++ goto exit_glib_loop;
++
++ portal_update_available_cursor_modes(ctx);
++ portal_create_session(ctx);
++ if (pw_ctx->portal_error) {
++ ret = pw_ctx->portal_error;
++ goto exit_glib_loop;
++ }
++
++ g_main_loop_run(pw_ctx->glib_main_loop);
++ // The main loop will run until it's stopped by portal_open_pipewire_remote (if
++ // all DBus method calls were successfully), portal_abort (in case of error) or
++ // on_cancelled_callback (if a DBus request is cancelled).
++ // In the latter two cases, pw_ctx->portal_error gets set to a nonzero value.
++ if (pw_ctx->portal_error)
++ ret = pw_ctx->portal_error;
++
++exit_glib_loop:
++ g_main_loop_unref(pw_ctx->glib_main_loop);
++ pw_ctx->glib_main_loop = NULL;
++ g_main_context_pop_thread_default(glib_main_context);
++ g_main_context_unref(glib_main_context);
++
++ return AVERROR(ret);
++}
++
++static av_cold int pipewiregrab_init(AVFilterContext *ctx)
++{
++ int ret;
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ if (!pw_ctx) {
++ av_log(ctx, AV_LOG_ERROR,
++ "Invalid private context data!\n");
++ return AVERROR(EINVAL);
++ }
++
++ atomic_init(&pw_ctx->pipewire_initialization_over, 0);
++ pthread_cond_init(&pw_ctx->pipewire_initialization_cond_var, NULL);
++ pthread_mutex_init(&pw_ctx->pipewire_initialization_mutex, NULL);
++ pthread_mutex_init(&pw_ctx->current_frame_mutex, NULL);
++
++ pw_ctx->pipewire_node = pw_ctx->pipewire_external_node;
++ if (pw_ctx->pipewire_fd == 0) {
++ ret = portal_init_screencast(ctx);
++ if (ret != 0) {
++ atomic_store(&pw_ctx->pipewire_initialization_over, 1);
++ pthread_cond_signal(&pw_ctx->pipewire_initialization_cond_var);
++ return ret;
++ }
++ }
++
++ ret = play_pipewire_stream(ctx);
++ if (ret != 0)
++ return ret;
++
++ // Wait until PipeWire initialization is over
++ pthread_mutex_lock(&pw_ctx->pipewire_initialization_mutex);
++ while (!atomic_load(&pw_ctx->pipewire_initialization_over)) {
++ pthread_cond_wait(&pw_ctx->pipewire_initialization_cond_var,
++ &pw_ctx->pipewire_initialization_mutex);
++ }
++ pthread_mutex_unlock(&pw_ctx->pipewire_initialization_mutex);
++
++ if (pw_ctx->pipewire_error)
++ return pw_ctx->pipewire_error;
++
++ return 0;
++}
++
++static void pipewiregrab_uninit(AVFilterContext *ctx)
++{
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ if (!pw_ctx)
++ return;
++
++ if (pw_ctx->glib_main_loop &&
++ g_main_loop_is_running(pw_ctx->glib_main_loop)) {
++ // Cancel ongoing DBus operation, if any
++ g_cancellable_cancel(pw_ctx->cancellable);
++ pthread_mutex_lock(&pw_ctx->pipewire_initialization_mutex);
++ while (!atomic_load(&pw_ctx->pipewire_initialization_over)) {
++ pthread_cond_wait(&pw_ctx->pipewire_initialization_cond_var,
++ &pw_ctx->pipewire_initialization_mutex);
++ }
++ pthread_mutex_unlock(&pw_ctx->pipewire_initialization_mutex);
++ }
++ g_clear_object(&pw_ctx->cancellable);
++
++ // PipeWire cleanup
++ if (pw_ctx->thread_loop) {
++ pw_thread_loop_signal(pw_ctx->thread_loop, false);
++ pw_thread_loop_unlock(pw_ctx->thread_loop);
++ pw_thread_loop_stop(pw_ctx->thread_loop);
++ }
++ if (pw_ctx->stream) {
++ pw_stream_disconnect(pw_ctx->stream);
++ g_clear_pointer(&pw_ctx->stream, pw_stream_destroy);
++ pw_ctx->stream = NULL;
++ }
++ if (pw_ctx->core){
++ pw_core_disconnect(pw_ctx->core);
++ pw_ctx->core = NULL;
++ }
++ if (pw_ctx->context) {
++ pw_context_destroy(pw_ctx->context);
++ pw_ctx->context = NULL;
++ }
++ if (pw_ctx->thread_loop) {
++ pw_thread_loop_destroy(pw_ctx->thread_loop);
++ pw_ctx->thread_loop = NULL;
++ }
++ if (pw_ctx->pw_init_called) {
++ pw_deinit();
++ pw_ctx->pw_init_called = 0;
++ }
++ if (pw_ctx->pipewire_fd > 0) {
++ close(pw_ctx->pipewire_fd);
++ pw_ctx->pipewire_fd = 0;
++ }
++ av_frame_free(&pw_ctx->current_frame);
++
++ // DBus cleanup
++ if (pw_ctx->session_handle) {
++ g_dbus_connection_call(
++ pw_ctx->connection, "org.freedesktop.portal.Desktop",
++ pw_ctx->session_handle, "org.freedesktop.portal.Session", "Close",
++ NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
++
++ g_clear_pointer(&pw_ctx->session_handle, g_free);
++ }
++ g_clear_object(&pw_ctx->connection);
++ g_clear_object(&pw_ctx->proxy);
++ g_clear_pointer(&pw_ctx->sender_name, g_free);
++}
++
++static int pipewiregrab_config_props(AVFilterLink *outlink)
++{
++ AVFrame *frame;
++ PipewireGrabContext *pw_ctx = outlink->src->priv;
++
++ AVRational time_base = av_inv_q(pw_ctx->framerate);
++ pw_ctx->frame_duration = av_rescale_q(1, time_base, AV_TIME_BASE_Q);
++ pw_ctx->time_frame = av_gettime_relative();
++
++ outlink->w = pw_ctx->width;
++ outlink->h = pw_ctx->height;
++ outlink->time_base = AV_TIME_BASE_Q;
++ outlink->frame_rate = pw_ctx->framerate;
++
++ frame = ff_get_video_buffer(outlink, pw_ctx->width, pw_ctx->height);
++ if (!frame)
++ return AVERROR(ENOMEM);
++ pthread_mutex_lock(&pw_ctx->current_frame_mutex);
++ pw_ctx->current_frame = frame;
++ pthread_mutex_unlock(&pw_ctx->current_frame_mutex);
++
++ return 0;
++}
++
++/**
++ * Helper function: calculate the wait time based
++ * on the frame duration
++ *
++ * @param pw_ctx
++ * @return current time
++ */
++static int64_t wait_frame(PipewireGrabContext *pw_ctx)
++{
++ int64_t curtime, delay;
++
++ /* Calculate the time of the next frame */
++ pw_ctx->time_frame += pw_ctx->frame_duration;
++
++ /* wait based on the frame rate */
++ while (1) {
++ curtime = av_gettime_relative();
++ delay = pw_ctx->time_frame - curtime;
++ if (delay <= 0)
++ break;
++ av_usleep(delay);
++ }
++
++ return curtime;
++}
++
++static int pipewiregrab_request_frame(AVFilterLink *outlink)
++{
++ int ret;
++ PipewireGrabContext *pw_ctx = outlink->src->priv;
++ AVFrame *frame = av_frame_alloc();
++ if (!frame)
++ return AVERROR(ENOMEM);
++
++ wait_frame(pw_ctx);
++
++ pthread_mutex_lock(&pw_ctx->current_frame_mutex);
++ ret = av_frame_ref(frame, pw_ctx->current_frame);
++ pthread_mutex_unlock(&pw_ctx->current_frame_mutex);
++ if (ret < 0) {
++ av_frame_free(&frame);
++ return ret;
++ }
++
++ frame->pts = av_gettime();
++ frame->duration = pw_ctx->frame_duration;
++ frame->sample_aspect_ratio = (AVRational) {1, 1};
++ frame->format = pw_ctx->av_pxl_format;
++
++ return ff_filter_frame(outlink, frame);
++}
++
++static int pipewiregrab_query_formats(AVFilterContext *ctx)
++{
++ PipewireGrabContext *pw_ctx = ctx->priv;
++ enum AVPixelFormat pix_fmts[] = {pw_ctx->av_pxl_format, AV_PIX_FMT_NONE};
++
++ return ff_set_common_formats_from_list(ctx, pix_fmts);
++}
++
++static const AVFilterPad pipewiregrab_outputs[] = {
++ {
++ .name = "default",
++ .type = AVMEDIA_TYPE_VIDEO,
++ .request_frame = pipewiregrab_request_frame,
++ .config_props = pipewiregrab_config_props,
++ },
++};
++
++const AVFilter ff_vsrc_pipewiregrab= {
++ .name = "pipewiregrab",
++ .description = NULL_IF_CONFIG_SMALL("Capture screen or window using PipeWire."),
++ .priv_size = sizeof(struct PipewireGrabContext),
++ .priv_class = &pipewiregrab_class,
++ .init = pipewiregrab_init,
++ .uninit = pipewiregrab_uninit,
++ .inputs = NULL,
++ FILTER_OUTPUTS(pipewiregrab_outputs),
++ FILTER_QUERY_FUNC(pipewiregrab_query_formats),
++};
diff --git a/gnu/packages/patches/gwenview-kimageannotator.patch b/gnu/packages/patches/gwenview-kimageannotator.patch
new file mode 100644
index 0000000000..92b994a0b5
--- /dev/null
+++ b/gnu/packages/patches/gwenview-kimageannotator.patch
@@ -0,0 +1,29 @@
+Submitted By: Douglas R. Reno <renodr at linuxfromscratch dot org>
+Date: 2024-02-26
+Initial Package Version: 23.08.5
+Upstream Status: Rejected (Qt6 port is primary now)
+Origin: Self
+Description: Fixes building Gwenview with recent kImageAnnotator
+ and kColorPicker versions by adjusting their paths
+ and FOUND variables so that the correct libraries are
+ selected.
+
+--- gwenview-23.08.5.orig/CMakeLists.txt 2024-02-26 15:02:29.702754535 -0600
++++ gwenview-23.08.5/CMakeLists.txt 2024-02-26 15:09:48.012866877 -0600
+@@ -166,11 +166,11 @@ if(NOT WITHOUT_X11)
+ endif()
+
+ if (QT_MAJOR_VERSION STREQUAL "5")
+- find_package(kImageAnnotator)
+- set_package_properties(kImageAnnotator PROPERTIES URL "https://github.com/ksnip/kImageAnnotator" DESCRIPTION "The kImageAnnotator library provides tools to annotate" TYPE REQUIRED)
+- if(kImageAnnotator_FOUND)
+- set(KIMAGEANNOTATOR_FOUND 1)
+- find_package(kColorPicker REQUIRED)
++ find_package(kImageAnnotator-Qt5)
++ set_package_properties(kImageAnnotator-Qt5 PROPERTIES URL "https://github.com/ksnip/kImageAnnotator" DESCRIPTION "The kImageAnnotator library provides tools to annotate" TYPE REQUIRED)
++ if(kImageAnnotator-Qt5_FOUND)
++ set(kImageAnnotator_FOUND 1)
++ find_package(kColorPicker-Qt5 REQUIRED)
+ if(NOT kImageAnnotator_VERSION VERSION_LESS 0.5.0)
+ set(KIMAGEANNOTATOR_CAN_LOAD_TRANSLATIONS 1)
+ endif()
diff --git a/gnu/packages/patches/jami-qml-tests-discovery.patch b/gnu/packages/patches/jami-qml-tests-discovery.patch
deleted file mode 100644
index 11fd69571c..0000000000
--- a/gnu/packages/patches/jami-qml-tests-discovery.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-Upstream status: https://review.jami.net/c/jami-client-qt/+/25640
-
-diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
-index d50908cf..587c9d15 100644
---- a/tests/CMakeLists.txt
-+++ b/tests/CMakeLists.txt
-@@ -73,6 +73,8 @@ endif()
-
- string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
-
-+set(QUICK_TEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}tests/qml/src")
-+
- set(QML_TESTS_SOURCE_FILES
- ${CMAKE_SOURCE_DIR}/tests/qml/main.cpp
- ${TEST_QML_RESOURCES}
diff --git a/gnu/packages/patches/jami-qwindowkit.patch b/gnu/packages/patches/jami-qwindowkit.patch
new file mode 100644
index 0000000000..65248a6940
--- /dev/null
+++ b/gnu/packages/patches/jami-qwindowkit.patch
@@ -0,0 +1,37 @@
+Upstream-status: https://lists.gnu.org/archive/html/jami/2024-03/msg00008.html
+
+This makes it possible to use the system-provided qwindowkit library.
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 6d2dccfb..8dedff50 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -100,13 +100,13 @@ endif()
+
+ # qwindowkit (frameless window)
+ add_fetch_content(
+- TARGET qwindowkit
++ TARGET QWindowKit
+ URL https://github.com/stdware/qwindowkit.git
+ BRANCH 79b1f3110754f9c21af2d7dacbd07b1a9dbaf6ef
+ PATCHES ${QWINDOWKIT_PATCHES}
+ OPTIONS ${QWINDOWKIT_OPTIONS}
+ )
+-list(APPEND CLIENT_INCLUDE_DIRS ${QWindowKit_BINARY_DIR}/include)
++
+ list(APPEND CLIENT_LIBS QWindowKit::Quick)
+
+ set(CMAKE_AUTOMOC ON)
+diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
+index b2730b71..4960899e 100644
+--- a/tests/CMakeLists.txt
++++ b/tests/CMakeLists.txt
+@@ -48,7 +48,7 @@ target_include_directories(test_common_obj PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/src)
+ target_link_directories(test_common_obj PRIVATE ${CLIENT_LINK_DIRS})
+-target_link_libraries(test_common_obj ${QML_TEST_LIBS})
++target_link_libraries(test_common_obj ${QML_TEST_LIBS} ${CLIENT_LIBS})
+ target_compile_definitions(test_common_obj PRIVATE BUILD_TESTING="ON")
+
+ set(COMMON_TESTS_SOURCES
diff --git a/gnu/packages/patches/jami-tests-qtwebengine-ifdef-to-if.patch b/gnu/packages/patches/jami-tests-qtwebengine-ifdef-to-if.patch
new file mode 100644
index 0000000000..63bfde6af0
--- /dev/null
+++ b/gnu/packages/patches/jami-tests-qtwebengine-ifdef-to-if.patch
@@ -0,0 +1,26 @@
+Upstream-status: https://lists.gnu.org/archive/html/jami/2024-03/msg00005.html
+
+Fix macro value checking.
+
+diff --git a/tests/qml/main.cpp b/tests/qml/main.cpp
+index 2fbecebe..8cb3de69 100644
+--- a/tests/qml/main.cpp
++++ b/tests/qml/main.cpp
+@@ -35,7 +35,7 @@
+ #include <QtQuickTest/quicktest.h>
+ #include <QSignalSpy>
+
+-#ifdef WITH_WEBENGINE
++#if WITH_WEBENGINE
+ #include <QtWebEngineCore>
+ #include <QtWebEngineQuick>
+ #endif
+@@ -192,7 +192,7 @@ main(int argc, char** argv)
+ // Allow the user to enable fatal warnings for certain tests.
+ Utils::remove_argument(argv, argc, "--failonwarn", [&]() { qputenv("QT_FATAL_WARNINGS", "1"); });
+
+-#ifdef WITH_WEBENGINE
++#if WITH_WEBENGINE
+ QtWebEngineQuick::initialize();
+ #endif
+ QTEST_SET_MAIN_SOURCE_PATH
diff --git a/gnu/packages/patches/jami-unbundle-dependencies.patch b/gnu/packages/patches/jami-unbundle-dependencies.patch
index dab82b26cb..2732087daf 100644
--- a/gnu/packages/patches/jami-unbundle-dependencies.patch
+++ b/gnu/packages/patches/jami-unbundle-dependencies.patch
@@ -16,13 +16,14 @@ Change-Id: I637959fefce6a21b0ee73a793acb6c3c42dcdce0
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
-index 38e7a4e2..3f1bd599 100644
+index e802357f..6d2dccfb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
-@@ -545,19 +545,33 @@ add_subdirectory(3rdparty/SortFilterProxyModel)
+@@ -600,20 +600,34 @@ add_subdirectory(3rdparty/SortFilterProxyModel)
set(SFPM_OBJECTS $<TARGET_OBJECTS:SortFilterProxyModel>)
# md4c
+-set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
-set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
-add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
-list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
@@ -34,6 +35,7 @@ index 38e7a4e2..3f1bd599 100644
+ list(APPEND CLIENT_LIBS md4c::md4c-html)
+else()
+ message("Using bundled md4c-html library")
++ set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
+ set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
+ add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
+ list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
diff --git a/gnu/packages/patches/libarchive-remove-potential-backdoor.patch b/gnu/packages/patches/libarchive-remove-potential-backdoor.patch
new file mode 100644
index 0000000000..2b9a9e2ffe
--- /dev/null
+++ b/gnu/packages/patches/libarchive-remove-potential-backdoor.patch
@@ -0,0 +1,47 @@
+Remove code added by 'JiaT75', the malicious actor that backdoored `xz`:
+
+https://github.com/libarchive/libarchive/pull/2101
+
+At libarchive, they are reviewing all code contributed by this actor:
+
+https://github.com/libarchive/libarchive/issues/2103
+
+See the original disclosure and subsequent discussion for more
+information about this incident:
+
+https://seclists.org/oss-sec/2024/q1/268
+
+Patch copied from upstream source repository:
+
+https://github.com/libarchive/libarchive/pull/2101/commits/e200fd8abfb4cf895a1cab4d89b67e6eefe83942
+
+From 6110e9c82d8ba830c3440f36b990483ceaaea52c Mon Sep 17 00:00:00 2001
+From: Ed Maste <emaste@freebsd.org>
+Date: Fri, 29 Mar 2024 18:02:06 -0400
+Subject: [PATCH] tar: make error reporting more robust and use correct errno
+ (#2101)
+
+As discussed in #1609.
+---
+ tar/read.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/tar/read.c b/tar/read.c
+index af3d3f42..a7f14a07 100644
+--- a/tar/read.c
++++ b/tar/read.c
+@@ -371,8 +371,9 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer)
+ if (r != ARCHIVE_OK) {
+ if (!bsdtar->verbose)
+ safe_fprintf(stderr, "%s", archive_entry_pathname(entry));
+- fprintf(stderr, ": %s: ", archive_error_string(a));
+- fprintf(stderr, "%s", strerror(errno));
++ safe_fprintf(stderr, ": %s: %s",
++ archive_error_string(a),
++ strerror(archive_errno(a)));
+ if (!bsdtar->verbose)
+ fprintf(stderr, "\n");
+ bsdtar->return_value = 1;
+--
+2.41.0
+
diff --git a/gnu/packages/patches/opencolorio-fix-build-with-gcc11.patch b/gnu/packages/patches/opencolorio-fix-build-with-gcc11.patch
deleted file mode 100644
index 06507db206..0000000000
--- a/gnu/packages/patches/opencolorio-fix-build-with-gcc11.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-Fix build failure with GCC 11:
-
-------
-[...]
-/tmp/guix-build-opencolorio-1.1.1.drv-0/source/src/core/ImageDesc.cpp:60:51: error: ‘this’ pointer is null [-Werror=nonnull]
- 60 | os << "width=" << packedImg->getWidth() << ", ";
- | ^
-/tmp/guix-build-opencolorio-1.1.1.drv-0/source/src/core/ImageDesc.cpp:274:10: note: in a call to non-static member function ‘long int OpenColorIO::v1::PackedImageDesc::getWidth() const’
- 274 | long PackedImageDesc::getWidth() const
- | ^~~~~~~~~~~~~~~
-/tmp/guix-build-opencolorio-1.1.1.drv-0/source/src/core/ImageDesc.cpp:61:53: error: ‘this’ pointer is null [-Werror=nonnull]
- 61 | os << "height=" << packedImg->getHeight() << ", ";
- | ^
-/tmp/guix-build-opencolorio-1.1.1.drv-0/source/src/core/ImageDesc.cpp:279:10: note: in a call to non-static member function ‘long int OpenColorIO::v1::PackedImageDesc::getHeight() const’
- 279 | long PackedImageDesc::getHeight() const
- | ^~~~~~~~~~~~~~~
-cc1plus: all warnings being treated as errors
-[...]
-------
-
-Patch copied from Gentoo:
-
-https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=7e726d850502018b6760da78dbd4a419603016b8
-
-diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
-index 1eb691b6..cff9bd83 100644
---- a/src/core/CMakeLists.txt
-+++ b/src/core/CMakeLists.txt
-@@ -23,8 +23,6 @@ if(WIN32)
- if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
- set(EXTERNAL_COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS} /WX")
- endif()
--else()
-- set(EXTERNAL_COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS} -Werror")
- endif()
-
- # SHARED
diff --git a/gnu/packages/patches/openssh-gcc-13-ppc64le-fzero-call-used-regs.patch b/gnu/packages/patches/openssh-gcc-13-ppc64le-fzero-call-used-regs.patch
deleted file mode 100644
index 1af9868b9a..0000000000
--- a/gnu/packages/patches/openssh-gcc-13-ppc64le-fzero-call-used-regs.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 1036d77b34a5fa15e56f516b81b9928006848cbd Mon Sep 17 00:00:00 2001
-From: Damien Miller <djm@mindrot.org>
-Date: Fri, 22 Dec 2023 17:56:26 +1100
-Subject: [PATCH] better detection of broken -fzero-call-used-regs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-gcc 13.2.0 on ppc64le refuses to compile some function, including
-cipher.c:compression_alg_list() with an error:
-
-> sorry, unimplemented: argument ‘used’ is not supportedcw
-> for ‘-fzero-call-used-regs’ on this target
-
-This extends the autoconf will-it-work test with a similarly-
-structured function that seems to catch this.
-
-Spotted/tested by Colin Watson; bz3645
----
-
-Taken from upsteam, and for Guix by jackhill@jackhill.us
-Thanks Marcel van der Boom for noticing: https://issues.guix.gnu.org/67948#2
-
-m4/openssh.m4 | 12 +++++++++---
- 1 file changed, 9 insertions(+), 3 deletions(-)
-
-diff --git a/m4/openssh.m4 b/m4/openssh.m4
-index 5d4c56280..033df501c 100644
---- a/m4/openssh.m4
-+++ b/m4/openssh.m4
-@@ -20,18 +20,24 @@ char *f2(char *s, ...) {
- va_end(args);
- return strdup(ret);
- }
-+const char *f3(int s) {
-+ return s ? "good" : "gooder";
-+}
- int main(int argc, char **argv) {
-- (void)argv;
- char b[256], *cp;
-+ const char *s;
- /* Some math to catch -ftrapv problems in the toolchain */
- int i = 123 * argc, j = 456 + argc, k = 789 - argc;
- float l = i * 2.1;
- double m = l / 0.5;
- long long int n = argc * 12345LL, o = 12345LL * (long long int)argc;
-+ (void)argv;
- f(1);
-- snprintf(b, sizeof b, "%d %d %d %f %f %lld %lld\n", i,j,k,l,m,n,o);
-+ s = f3(f(2));
-+ snprintf(b, sizeof b, "%d %d %d %f %f %lld %lld %s\n", i,j,k,l,m,n,o,s);
- if (write(1, b, 0) == -1) exit(0);
-- cp = f2("%d %d %d %f %f %lld %lld\n", i,j,k,l,m,n,o);
-+ cp = f2("%d %d %d %f %f %lld %lld %s\n", i,j,k,l,m,n,o,s);
-+ if (write(1, cp, 0) == -1) exit(0);
- free(cp);
- /*
- * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does
---
-2.41.0
-
diff --git a/gnu/packages/patches/qtbase-find-tools-in-PATH.patch b/gnu/packages/patches/qtbase-find-tools-in-PATH.patch
new file mode 100644
index 0000000000..d5e38b09be
--- /dev/null
+++ b/gnu/packages/patches/qtbase-find-tools-in-PATH.patch
@@ -0,0 +1,49 @@
+Patch retrieved from NixOS
+
+https://github.com/NixOS/nixpkgs/blob/93ecdaa1f34354c9476062dc4fe323b442c087d5/pkgs/development/libraries/qt-6/patches/0006-qtbase-find-tools-in-PATH.patch
+
+From a8b9fae710a2bd5e743f5e16364eaa8c38dbd784 Mon Sep 17 00:00:00 2001
+From: rewine <luhongxu@deepin.org>
+Date: Wed, 29 Mar 2023 11:51:33 +0800
+Subject: [PATCH 06/11] qtbase-find-tools-in-PATH
+
+1. find qt's tools in `QTTOOLSPATH` env
+ qt assumes that all components use the same install prefix
+ we can't get the real prefix for qttools when build qtbase
+ we will add /libexec to `QTTOOLSPATH` in qtToolsHook
+ find_path will also search in 'PATH' by default
+ see `CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`
+
+2. disable tool_dependencies_enabled
+ We can guarantee the build order of qt components in nixpkgs
+ tools in qttools always build before qtdoc
+ qdoc_bin is not a build target now, since we find it in `QTTOOLSPATH`
+---
+ cmake/QtDocsHelpers.cmake | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/cmake/QtDocsHelpers.cmake b/cmake/QtDocsHelpers.cmake
+index 48ed5a324bf..91d8d41fb1f 100644
+--- a/cmake/QtDocsHelpers.cmake
++++ b/cmake/QtDocsHelpers.cmake
+@@ -47,9 +47,14 @@ function(qt_internal_add_docs)
+ set(doc_tools_libexec "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_LIBEXECDIR}")
+ endif()
+
+- set(qdoc_bin "${doc_tools_bin}/qdoc${CMAKE_EXECUTABLE_SUFFIX}")
+- set(qtattributionsscanner_bin "${doc_tools_libexec}/qtattributionsscanner${CMAKE_EXECUTABLE_SUFFIX}")
+- set(qhelpgenerator_bin "${doc_tools_libexec}/qhelpgenerator${CMAKE_EXECUTABLE_SUFFIX}")
++ set(tool_dependencies_enabled FALSE)
++
++ find_path(qdoc_path name qdoc PATHS ENV QTTOOLSPATH)
++ find_path(qtattributionsscanner_path name qtattributionsscanner PATHS ENV QTTOOLSPATH)
++ find_path(qhelpgenerator_path name qhelpgenerator PATHS ENV QTTOOLSPATH)
++ set(qdoc_bin "${qdoc_path}/qdoc${CMAKE_EXECUTABLE_SUFFIX}")
++ set(qtattributionsscanner_bin "${qtattributionsscanner_path}/qtattributionsscanner${CMAKE_EXECUTABLE_SUFFIX}")
++ set(qhelpgenerator_bin "${qhelpgenerator_path}/qhelpgenerator${CMAKE_EXECUTABLE_SUFFIX}")
+
+ get_target_property(target_type ${target} TYPE)
+ if (NOT target_type STREQUAL "INTERFACE_LIBRARY")
+--
+2.42.0
+
diff --git a/gnu/packages/patches/qtbase-qmake-fix-includedir.patch b/gnu/packages/patches/qtbase-qmake-fix-includedir.patch
new file mode 100644
index 0000000000..766689c4fd
--- /dev/null
+++ b/gnu/packages/patches/qtbase-qmake-fix-includedir.patch
@@ -0,0 +1,29 @@
+Patch retrieved from NixOS
+https://github.com/NixOS/nixpkgs/blob/93ecdaa1f34354c9476062dc4fe323b442c087d5/pkgs/development/libraries/qt-6/patches/0003-qtbase-qmake-fix-includedir-in-generated-pkg-config.patch
+
+From 6088085d3074316dd74639fc6c1233e5862aff11 Mon Sep 17 00:00:00 2001
+From: Nick Cao <nickcao@nichi.co>
+Date: Fri, 14 Apr 2023 09:34:46 +0800
+Subject: [PATCH 03/11] qtbase: qmake: fix includedir in generated pkg-config
+
+---
+ qmake/generators/makefile.cpp | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp
+index 11d2f0ff7df..c78ed0d3485 100644
+--- a/qmake/generators/makefile.cpp
++++ b/qmake/generators/makefile.cpp
+@@ -3412,8 +3412,7 @@ MakefileGenerator::writePkgConfigFile()
+ << varGlue("QMAKE_PKGCONFIG_CFLAGS", "", " ", " ")
+ // << varGlue("DEFINES","-D"," -D"," ")
+ ;
+- if (!project->values("QMAKE_DEFAULT_INCDIRS").contains(includeDir))
+- t << "-I${includedir}";
++ t << "-I${includedir}";
+ if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")
+ && libDir != QLatin1String("/Library/Frameworks")) {
+ t << " -F${libdir}";
+--
+2.42.0
+
diff --git a/gnu/packages/patches/qtbase-qmlimportscanner-qml-import-path.patch b/gnu/packages/patches/qtbase-qmlimportscanner-qml-import-path.patch
new file mode 100644
index 0000000000..b73a1fba73
--- /dev/null
+++ b/gnu/packages/patches/qtbase-qmlimportscanner-qml-import-path.patch
@@ -0,0 +1,33 @@
+Retrieved from nixpkgs.
+Modified to use QML_IMPORT_PATH instead of QML2_IMPORT_PATH.
+
+From d7a9a3b0ecdbb1b5829f25954d763d767f1c8794 Mon Sep 17 00:00:00 2001
+From: Nick Cao <nickcao@nichi.co>
+Date: Tue, 10 Oct 2023 10:12:56 -0400
+Subject: [PATCH 07/11] qtbase: pass to qmlimportscanner the QML2_IMPORT_PATH
+
+---
+ src/tools/macdeployqt/shared/shared.cpp | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/tools/macdeployqt/shared/shared.cpp b/src/tools/macdeployqt/shared/shared.cpp
+index 2ae4f998944..ba10ae02bcd 100644
+--- a/src/tools/macdeployqt/shared/shared.cpp
++++ b/src/tools/macdeployqt/shared/shared.cpp
+@@ -1297,6 +1297,13 @@ bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInf
+ argumentList.append( "-importPath");
+ argumentList.append(qmlImportsPath);
+
++ // In a modularized installation of qt as we have in Nix, instead, we will
++ // read the paths from the environment, as they are spread in multiple
++ // locations and normally set in the environment like this
++ auto envQmlImportPaths = ::qgetenv("QML_IMPORT_PATH").split(':');
++ for (const QString &importPath : envQmlImportPaths)
++ argumentList << "-importPath" << importPath;
++
+ // run qmlimportscanner
+ QProcess qmlImportScanner;
+ qmlImportScanner.start(qmlImportScannerPath, argumentList);
+--
+2.42.0
+
diff --git a/gnu/packages/patches/qtbase-use-TZDIR.patch b/gnu/packages/patches/qtbase-use-TZDIR.patch
deleted file mode 100644
index 98bf7493e9..0000000000
--- a/gnu/packages/patches/qtbase-use-TZDIR.patch
+++ /dev/null
@@ -1,141 +0,0 @@
-From 1075606f8b2f9e153c82f8e50cbd69cea9c72e87 Mon Sep 17 00:00:00 2001
-From: Edward Welbourne <edward.welbourne@qt.io>
-Date: Mon, 11 Sep 2023 11:41:39 +0200
-Subject: [PATCH] Support the TZDIR environment variable
-
-On Linux / glibc, this overrides the default system location for the
-zone info. So check for files there first. Break out a function to
-manage the trying of (now three) zoneinfo directories when opening a
-file by name relative to there.
-
-Pick-to: 6.6 6.5
-Task-number: QTBUG-116017
-Change-Id: I1f97107aabd9015c0a5543639870f1d70654ca67
----
-* Rebased on top of v6.5.2.
-
- src/corelib/time/qtimezoneprivate_tz.cpp | 73 ++++++++++++++++--------
- 1 file changed, 49 insertions(+), 24 deletions(-)
-
-diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
-index 067191d816..a8b2fc894e 100644
---- a/src/corelib/time/qtimezoneprivate_tz.cpp
-+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
-@@ -51,17 +51,41 @@ typedef QHash<QByteArray, QTzTimeZone> QTzTimeZoneHash;
-
- static bool isTzFile(const QString &name);
-
-+// Open a named file under the zone info directory:
-+static bool openZoneInfo(QString name, QFile *file)
-+{
-+ // At least on Linux / glibc (see man 3 tzset), $TZDIR overrides the system
-+ // default location for zone info:
-+ const QString tzdir = qEnvironmentVariable("TZDIR");
-+ if (!tzdir.isEmpty()) {
-+ file->setFileName(QDir(tzdir).filePath(name));
-+ if (file->open(QIODevice::ReadOnly))
-+ return true;
-+ }
-+ // Try modern system path first:
-+ constexpr auto zoneShare = "/usr/share/zoneinfo/"_L1;
-+ if (tzdir != zoneShare && tzdir != zoneShare.chopped(1)) {
-+ file->setFileName(zoneShare + name);
-+ if (file->open(QIODevice::ReadOnly))
-+ return true;
-+ }
-+ // Fall back to legacy system path:
-+ constexpr auto zoneLib = "/usr/lib/zoneinfo/"_L1;
-+ if (tzdir != zoneLib && tzdir != zoneLib.chopped(1)) {
-+ file->setFileName(zoneShare + name);
-+ if (file->open(QIODevice::ReadOnly))
-+ return true;
-+ }
-+ return false;
-+}
-+
- // Parse zone.tab table for territory information, read directories to ensure we
- // find all installed zones (many are omitted from zone.tab; even more from
- // zone1970.tab).
- static QTzTimeZoneHash loadTzTimeZones()
- {
-- QString path = QStringLiteral("/usr/share/zoneinfo/zone.tab");
-- if (!QFile::exists(path))
-- path = QStringLiteral("/usr/lib/zoneinfo/zone.tab");
--
-- QFile tzif(path);
-- if (!tzif.open(QIODevice::ReadOnly))
-+ QFile tzif;
-+ if (!openZoneInfo("zone.tab"_L1, &tzif))
- return QTzTimeZoneHash();
-
- QTzTimeZoneHash zonesHash;
-@@ -91,6 +115,7 @@ static QTzTimeZoneHash loadTzTimeZones()
- }
- }
-
-+ const QString path = tzif.fileName();
- const qsizetype cut = path.lastIndexOf(u'/');
- Q_ASSERT(cut > 0);
- const QDir zoneDir = QDir(path.first(cut));
-@@ -761,20 +786,13 @@ QTzTimeZoneCacheEntry QTzTimeZoneCache::findEntry(const QByteArray &ianaId)
- tzif.setFileName(QStringLiteral("/etc/localtime"));
- if (!tzif.open(QIODevice::ReadOnly))
- return ret;
-- } else {
-- // Open named tz, try modern path first, if fails try legacy path
-- tzif.setFileName("/usr/share/zoneinfo/"_L1 + QString::fromLocal8Bit(ianaId));
-- if (!tzif.open(QIODevice::ReadOnly)) {
-- tzif.setFileName("/usr/lib/zoneinfo/"_L1 + QString::fromLocal8Bit(ianaId));
-- if (!tzif.open(QIODevice::ReadOnly)) {
-- // ianaId may be a POSIX rule, taken from $TZ or /etc/TZ
-- auto check = validatePosixRule(ianaId);
-- if (check.isValid) {
-- ret.m_hasDst = check.hasDst;
-- ret.m_posixRule = ianaId;
-- }
-- return ret;
-- }
-+ } else if (!openZoneInfo(QString::fromLocal8Bit(ianaId), &tzif)) {
-+ // ianaId may be a POSIX rule, taken from $TZ or /etc/TZ
-+ auto check = validatePosixRule(ianaId);
-+ if (check.isValid) {
-+ ret.m_hasDst = check.hasDst;
-+ ret.m_posixRule = ianaId;
-+ return ret;
- }
- }
-
-@@ -1317,7 +1335,8 @@ private:
- {
- // On most distros /etc/localtime is a symlink to a real file so extract
- // name from the path
-- const auto zoneinfo = "/zoneinfo/"_L1;
-+ const QString tzdir = qEnvironmentVariable("TZDIR");
-+ constexpr auto zoneinfo = "/zoneinfo/"_L1;
- QString path = QStringLiteral("/etc/localtime");
- long iteration = getSymloopMax();
- // Symlink may point to another symlink etc. before being under zoneinfo/
-@@ -1325,9 +1344,15 @@ private:
- // symlink, like America/Montreal pointing to America/Toronto
- do {
- path = QFile::symLinkTarget(path);
-- int index = path.indexOf(zoneinfo);
-- if (index >= 0) // Found zoneinfo file; extract zone name from path:
-- return QStringView{ path }.mid(index + zoneinfo.size()).toUtf8();
-+ // If it's a zoneinfo file, extract the zone name from its path:
-+ int index = tzdir.isEmpty() ? -1 : path.indexOf(tzdir);
-+ if (index >= 0) {
-+ const auto tail = QStringView{ path }.sliced(index + tzdir.size()).toUtf8();
-+ return tail.startsWith(u'/') ? tail.sliced(1) : tail;
-+ }
-+ index = path.indexOf(zoneinfo);
-+ if (index >= 0)
-+ return QStringView{ path }.sliced(index + zoneinfo.size()).toUtf8();
- } while (!path.isEmpty() && --iteration > 0);
-
- return QByteArray();
-
-base-commit: af457a9f0f7eb1a2a7d11f495da508faab91a442
---
-2.41.0
-
diff --git a/gnu/packages/patches/qtdeclarative-disable-qmlcache.patch b/gnu/packages/patches/qtdeclarative-disable-qmlcache.patch
index 5f06ec53b4..df76fab910 100644
--- a/gnu/packages/patches/qtdeclarative-disable-qmlcache.patch
+++ b/gnu/packages/patches/qtdeclarative-disable-qmlcache.patch
@@ -1,16 +1,31 @@
Retrieved from
https://raw.githubusercontent.com/NixOS/nixpkgs/master/pkgs/development/libraries/qt-6/patches/qtdeclarative-default-disable-qmlcache.patch
+From 2d561e0a80f2d123a7348187975ee845f9dcd9e0 Mon Sep 17 00:00:00 2001
+From: Nick Cao <nickcao@nichi.co>
+Date: Tue, 10 Oct 2023 11:12:27 -0400
+Subject: [PATCH] qtdeclarative: disable qml disk cache
+
+---
+ src/qml/jsruntime/qv4engine.cpp | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
-index 852cde9e..165f1b57 100644
+index d1b4c4fff6..50f8a07420 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
-@@ -2093,7 +2093,7 @@ void ExecutionEngine::registerModule(const QString &_name, const QJSValue &modul
-
- bool ExecutionEngine::diskCacheEnabled() const
+@@ -2232,11 +2232,7 @@ ExecutionEngine::DiskCacheOptions ExecutionEngine::diskCacheOptions() const
{
-- return (!disableDiskCache() && !debugger()) || forceDiskCache();
-+ return forceDiskCache();
+ if (forceDiskCache())
+ return DiskCache::Enabled;
+- if (disableDiskCache() || debugger())
+- return DiskCache::Disabled;
+- static const DiskCacheOptions options = qmlGetConfigOption<
+- DiskCacheOptions, transFormDiskCache>("QML_DISK_CACHE");
+- return options;
++ return DiskCache::Disabled;
}
void ExecutionEngine::callInContext(QV4::Function *function, QObject *self,
+--
+2.42.0
diff --git a/gnu/packages/patches/xgboost-use-system-dmlc-core.patch b/gnu/packages/patches/xgboost-use-system-dmlc-core.patch
index 6b2a1651a5..cbc0feed1c 100644
--- a/gnu/packages/patches/xgboost-use-system-dmlc-core.patch
+++ b/gnu/packages/patches/xgboost-use-system-dmlc-core.patch
@@ -1,13 +1,13 @@
-# This patch was imported from Debian: https://sources.debian.org/src/xgboost/1.5.1-1/debian/patches/cmake-dmlc-core.patch/
+# This patch was imported from Debian: https://sources.debian.org/patches/xgboost/1.7.4-1/cmake-dmlc-core.patch/
Index: xgboost/CMakeLists.txt
===================================================================
--- xgboost.orig/CMakeLists.txt
+++ xgboost/CMakeLists.txt
-@@ -164,7 +164,9 @@ endif (USE_NCCL)
-
- # dmlc-core
- msvc_use_static_runtime()
+@@ -205,7 +205,9 @@ msvc_use_static_runtime()
+ if (FORCE_SHARED_CRT)
+ set(DMLC_FORCE_SHARED_CRT ON)
+ endif ()
-add_subdirectory(${xgboost_SOURCE_DIR}/dmlc-core)
+add_library(dmlc SHARED IMPORTED)
+find_library(DMLC_LIBRARY dmlc)
@@ -15,7 +15,7 @@ Index: xgboost/CMakeLists.txt
if (MSVC)
if (TARGET dmlc_unit_tests)
-@@ -222,7 +224,7 @@ set_target_properties(runxgboost PROPERT
+@@ -267,7 +269,7 @@ set_target_properties(runxgboost PROPERT
#-- End CLI for xgboost
# Common setup for all targets
@@ -24,7 +24,7 @@ Index: xgboost/CMakeLists.txt
xgboost_target_properties(${target})
xgboost_target_link_libraries(${target})
xgboost_target_defs(${target})
-@@ -273,7 +275,7 @@ install(DIRECTORY ${xgboost_SOURCE_DIR}/
+@@ -318,7 +320,7 @@ install(DIRECTORY ${xgboost_SOURCE_DIR}/
#
# https://github.com/dmlc/xgboost/issues/6085
if (BUILD_STATIC_LIB)