From cb6d6e8835ccdac1af9c4b7b3bd66ede8b855794 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Mon, 30 Oct 2023 14:32:28 -0700 Subject: [PATCH] Backport MRs #3311 and #3326 to fix screencast issues (#2247033) --- 3311.patch | 460 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3326.patch | 78 +++++++++ mutter.spec | 9 + 3 files changed, 547 insertions(+) create mode 100644 3311.patch create mode 100644 3326.patch diff --git a/3311.patch b/3311.patch new file mode 100644 index 0000000..8b2ede3 --- /dev/null +++ b/3311.patch @@ -0,0 +1,460 @@ +From b258e1f7eefa67006da72172d2a61389a58b7630 Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Mon, 9 Oct 2023 22:47:38 +0200 +Subject: [PATCH 1/2] screen-cast/stream-src: Various code cleanups + +No functional changes. + +Part-of: +--- + src/backends/meta-screen-cast-stream-src.c | 117 ++++++++++----------- + 1 file changed, 57 insertions(+), 60 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 8d4e32ef60e..acb5e9a1da1 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -555,17 +555,16 @@ maybe_record_cursor (MetaScreenCastStreamSrc *src, + } + + static gboolean +-do_record_frame (MetaScreenCastStreamSrc *src, +- MetaScreenCastRecordFlag flags, +- struct spa_buffer *spa_buffer, +- uint8_t *data, +- GError **error) ++do_record_frame (MetaScreenCastStreamSrc *src, ++ MetaScreenCastRecordFlag flags, ++ struct spa_buffer *spa_buffer, ++ GError **error) + { + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); ++ struct spa_data *spa_data = &spa_buffer->datas[0]; + +- if (spa_buffer->datas[0].data || +- spa_buffer->datas[0].type == SPA_DATA_MemFd) ++ if (spa_data->data || spa_data->type == SPA_DATA_MemFd) + { + int width = priv->video_format.size.width; + int height = priv->video_format.size.height; +@@ -575,14 +574,14 @@ do_record_frame (MetaScreenCastStreamSrc *src, + width, + height, + stride, +- data, ++ spa_data->data, + error); + } +- else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) ++ else if (spa_data->type == SPA_DATA_DmaBuf) + { + CoglDmaBufHandle *dmabuf_handle = + g_hash_table_lookup (priv->dmabuf_handles, +- GINT_TO_POINTER (spa_buffer->datas[0].fd)); ++ GINT_TO_POINTER (spa_data->fd)); + CoglFramebuffer *dmabuf_fbo = + cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); + +@@ -592,7 +591,7 @@ do_record_frame (MetaScreenCastStreamSrc *src, + } + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +- "Unknown SPA buffer type %u", spa_buffer->datas[0].type); ++ "Unknown SPA buffer type %u", spa_data->type); + return FALSE; + } + +@@ -745,12 +744,12 @@ meta_screen_cast_stream_src_maybe_record_frame_with_timestamp (MetaScreenCastStr + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); + MetaScreenCastRecordResult record_result = +- META_SCREEN_CAST_RECORD_RESULT_RECORDED_NOTHING; ++ META_SCREEN_CAST_RECORD_RESULT_RECORDED_NOTHING; + MtkRectangle crop_rect; + struct pw_buffer *buffer; + struct spa_buffer *spa_buffer; + struct spa_meta_header *header; +- uint8_t *data = NULL; ++ struct spa_data *spa_data; + + /* Accumulate the damaged region since we might not schedule a frame capture + * eventually but once we do, we should report all the previous damaged areas. +@@ -817,13 +816,13 @@ meta_screen_cast_stream_src_maybe_record_frame_with_timestamp (MetaScreenCastStr + } + + spa_buffer = buffer->buffer; +- data = spa_buffer->datas[0].data; ++ spa_data = &spa_buffer->datas[0]; + + header = spa_buffer_find_meta_data (spa_buffer, + SPA_META_Header, + sizeof (*header)); + +- if (spa_buffer->datas[0].type != SPA_DATA_DmaBuf && !data) ++ if (spa_data->type != SPA_DATA_DmaBuf && !spa_data->data) + { + g_critical ("Invalid buffer data"); + if (header) +@@ -838,10 +837,9 @@ meta_screen_cast_stream_src_maybe_record_frame_with_timestamp (MetaScreenCastStr + g_autoptr (GError) error = NULL; + + g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove); +- if (do_record_frame (src, flags, spa_buffer, data, &error)) ++ if (do_record_frame (src, flags, spa_buffer, &error)) + { + maybe_add_damaged_regions_metadata (src, spa_buffer); +- struct spa_data *spa_data = &spa_buffer->datas[0]; + struct spa_meta_region *spa_meta_video_crop; + + spa_data->chunk->size = spa_data->maxsize; +@@ -879,14 +877,14 @@ meta_screen_cast_stream_src_maybe_record_frame_with_timestamp (MetaScreenCastStr + { + if (error) + g_warning ("Failed to record screen cast frame: %s", error->message); +- spa_buffer->datas[0].chunk->size = 0; +- spa_buffer->datas[0].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; ++ spa_data->chunk->size = 0; ++ spa_data->chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; + } + } + else + { +- spa_buffer->datas[0].chunk->size = 0; +- spa_buffer->datas[0].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; ++ spa_data->chunk->size = 0; ++ spa_data->chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; + } + + record_result |= maybe_record_cursor (src, spa_buffer); +@@ -1091,7 +1089,7 @@ on_stream_add_buffer (void *data, + meta_screen_cast_session_get_screen_cast (session); + CoglDmaBufHandle *dmabuf_handle; + struct spa_buffer *spa_buffer = buffer->buffer; +- struct spa_data *spa_data = spa_buffer->datas; ++ struct spa_data *spa_data = &spa_buffer->datas[0]; + const int bpp = 4; + int stride; + +@@ -1099,11 +1097,11 @@ on_stream_add_buffer (void *data, + + stride = SPA_ROUND_UP_N (priv->video_format.size.width * bpp, 4); + +- spa_data[0].mapoffset = 0; +- spa_data[0].maxsize = stride * priv->video_format.size.height; +- spa_data[0].data = NULL; ++ spa_data->mapoffset = 0; ++ spa_data->maxsize = stride * priv->video_format.size.height; ++ spa_data->data = NULL; + +- if (spa_data[0].type & (1 << SPA_DATA_DmaBuf)) ++ if (spa_data->type & (1 << SPA_DATA_DmaBuf)) + { + CoglPixelFormat cogl_format; + +@@ -1141,19 +1139,19 @@ on_stream_add_buffer (void *data, + "Allocating DMA buffer for pw_stream %u", + pw_stream_get_node_id (priv->pipewire_stream)); + +- spa_data[0].type = SPA_DATA_DmaBuf; +- spa_data[0].flags = SPA_DATA_FLAG_READWRITE; +- spa_data[0].fd = cogl_dma_buf_handle_get_fd (dmabuf_handle); ++ spa_data->type = SPA_DATA_DmaBuf; ++ spa_data->flags = SPA_DATA_FLAG_READWRITE; ++ spa_data->fd = cogl_dma_buf_handle_get_fd (dmabuf_handle); + + g_hash_table_insert (priv->dmabuf_handles, +- GINT_TO_POINTER (spa_data[0].fd), ++ GINT_TO_POINTER (spa_data->fd), + dmabuf_handle); + } + else + { + unsigned int seals; + +- if (!(spa_data[0].type & (1 << SPA_DATA_MemFd))) ++ if (!(spa_data->type & (1 << SPA_DATA_MemFd))) + { + g_critical ("No supported PipeWire stream buffer data type could " + "be negotiated"); +@@ -1165,40 +1163,39 @@ on_stream_add_buffer (void *data, + pw_stream_get_node_id (priv->pipewire_stream)); + + /* Fallback to a memfd buffer */ +- spa_data[0].type = SPA_DATA_MemFd; +- spa_data[0].flags = SPA_DATA_FLAG_READWRITE; +- spa_data[0].fd = memfd_create ("mutter-screen-cast-memfd", +- MFD_CLOEXEC | MFD_ALLOW_SEALING); +- if (spa_data[0].fd == -1) ++ spa_data->type = SPA_DATA_MemFd; ++ spa_data->flags = SPA_DATA_FLAG_READWRITE; ++ spa_data->fd = memfd_create ("mutter-screen-cast-memfd", ++ MFD_CLOEXEC | MFD_ALLOW_SEALING); ++ if (spa_data->fd == -1) + { + g_critical ("Can't create memfd: %m"); + return; + } +- spa_data[0].mapoffset = 0; +- spa_data[0].maxsize = stride * priv->video_format.size.height; ++ spa_data->maxsize = stride * priv->video_format.size.height; + +- if (ftruncate (spa_data[0].fd, spa_data[0].maxsize) < 0) ++ if (ftruncate (spa_data->fd, spa_data->maxsize) < 0) + { +- close (spa_data[0].fd); +- spa_data[0].fd = -1; +- g_critical ("Can't truncate to %d: %m", spa_data[0].maxsize); ++ close (spa_data->fd); ++ spa_data->fd = -1; ++ g_critical ("Can't truncate to %d: %m", spa_data->maxsize); + return; + } + + seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; +- if (fcntl (spa_data[0].fd, F_ADD_SEALS, seals) == -1) ++ if (fcntl (spa_data->fd, F_ADD_SEALS, seals) == -1) + g_warning ("Failed to add seals: %m"); + +- spa_data[0].data = mmap (NULL, +- spa_data[0].maxsize, +- PROT_READ | PROT_WRITE, +- MAP_SHARED, +- spa_data[0].fd, +- spa_data[0].mapoffset); +- if (spa_data[0].data == MAP_FAILED) ++ spa_data->data = mmap (NULL, ++ spa_data->maxsize, ++ PROT_READ | PROT_WRITE, ++ MAP_SHARED, ++ spa_data->fd, ++ spa_data->mapoffset); ++ if (spa_data->data == MAP_FAILED) + { +- close (spa_data[0].fd); +- spa_data[0].fd = -1; ++ close (spa_data->fd); ++ spa_data->fd = -1; + g_critical ("Failed to mmap memory: %m"); + return; + } +@@ -1219,23 +1216,23 @@ on_stream_remove_buffer (void *data, + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); + struct spa_buffer *spa_buffer = buffer->buffer; +- struct spa_data *spa_data = spa_buffer->datas; ++ struct spa_data *spa_data = &spa_buffer->datas[0]; + + priv->buffer_count--; + +- if (spa_data[0].type == SPA_DATA_DmaBuf) ++ if (spa_data->type == SPA_DATA_DmaBuf) + { +- if (!g_hash_table_remove (priv->dmabuf_handles, GINT_TO_POINTER (spa_data[0].fd))) ++ if (!g_hash_table_remove (priv->dmabuf_handles, GINT_TO_POINTER (spa_data->fd))) + g_critical ("Failed to remove non-exported DMA buffer"); + } +- else if (spa_data[0].type == SPA_DATA_MemFd) ++ else if (spa_data->type == SPA_DATA_MemFd) + { +- g_warn_if_fail (spa_data[0].fd > 0 || !spa_data[0].data); ++ g_warn_if_fail (spa_data->fd > 0 || !spa_data->data); + +- if (spa_data[0].fd > 0) ++ if (spa_data->fd > 0) + { +- munmap (spa_data[0].data, spa_data[0].maxsize); +- close (spa_data[0].fd); ++ munmap (spa_data->data, spa_data->maxsize); ++ close (spa_data->fd); + } + } + } +-- +GitLab + + +From 9b663f44e6044ece52c38b3ee23bbc2b55328b47 Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Thu, 5 Oct 2023 08:36:31 +0200 +Subject: [PATCH 2/2] screen-cast/strieam-src: Fix stride and max buffer size + calculation + +1. Centralize stride calculation in one function. +2. For dmabufs query the stride instead of assuming a certain value. +3. For system memory buffers use the pixel format to calculate the + stride. +4. Stop negotiating `SPA_PARAM_BUFFERS_size` and + `SPA_PARAM_BUFFERS_stride`. + +2. fixes an actual bug where we reported wrong max buffer sizes, +resulting in crashes in Gstreamer when doing area screencasts on AMD +GPUs. + +The reasoning for 4. is that the values were possibly wrong for +dmabufs as the negotiation happens before we create any buffers. +Further more neither Mutter nor the common consumers required it. +The later either ignore the values (OBS), always accept (gstpipewiresrc) +them or calculate the exact same possibly wrong values (libwebrtc). + +Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6747 +Part-of: +--- + src/backends/meta-screen-cast-stream-src.c | 70 ++++++++++------------ + 1 file changed, 33 insertions(+), 37 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index acb5e9a1da1..cf198cf6472 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -102,7 +102,6 @@ typedef struct _MetaScreenCastStreamSrcPrivate + uint32_t node_id; + + struct spa_video_info_raw video_format; +- int video_stride; + + int64_t last_frame_timestamp_us; + guint follow_up_frame_source_id; +@@ -554,6 +553,33 @@ maybe_record_cursor (MetaScreenCastStreamSrc *src, + g_assert_not_reached (); + } + ++static int32_t ++meta_screen_cast_stream_src_calculate_stride (MetaScreenCastStreamSrc *src, ++ struct spa_data *spa_data) ++{ ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ CoglPixelFormat cogl_format; ++ int bpp; ++ ++ if (spa_data->type == SPA_DATA_DmaBuf) ++ { ++ CoglDmaBufHandle *dmabuf_handle = NULL; ++ ++ dmabuf_handle = g_hash_table_lookup (priv->dmabuf_handles, ++ GINT_TO_POINTER (spa_data->fd)); ++ if (dmabuf_handle) ++ return cogl_dma_buf_handle_get_stride (dmabuf_handle); ++ } ++ ++ if (!cogl_pixel_format_from_spa_video_format (priv->video_format.format, ++ &cogl_format)) ++ g_assert_not_reached (); ++ ++ bpp = cogl_pixel_format_get_bytes_per_pixel (cogl_format, 0); ++ return SPA_ROUND_UP_N (priv->video_format.size.width * bpp, 4); ++} ++ + static gboolean + do_record_frame (MetaScreenCastStreamSrc *src, + MetaScreenCastRecordFlag flags, +@@ -568,7 +594,7 @@ do_record_frame (MetaScreenCastStreamSrc *src, + { + int width = priv->video_format.size.width; + int height = priv->video_format.size.height; +- int stride = priv->video_stride; ++ int stride = meta_screen_cast_stream_src_calculate_stride (src, spa_data); + + return meta_screen_cast_stream_src_record_to_buffer (src, + width, +@@ -632,26 +658,6 @@ maybe_schedule_follow_up_frame (MetaScreenCastStreamSrc *src, + src); + } + +-static int32_t +-meta_screen_cast_stream_src_calculate_stride (MetaScreenCastStreamSrc *src, +- struct spa_data *spa_data) +-{ +- MetaScreenCastStreamSrcPrivate *priv = +- meta_screen_cast_stream_src_get_instance_private (src); +- CoglDmaBufHandle *dmabuf_handle = NULL; +- +- if (spa_data->type == SPA_DATA_DmaBuf) +- { +- dmabuf_handle = g_hash_table_lookup (priv->dmabuf_handles, +- GINT_TO_POINTER (spa_data->fd)); +- } +- +- if (dmabuf_handle) +- return cogl_dma_buf_handle_get_stride (dmabuf_handle); +- else +- return priv->video_stride; +-} +- + static void + maybe_add_damaged_regions_metadata (MetaScreenCastStreamSrc *src, + struct spa_buffer *spa_buffer) +@@ -1014,11 +1020,9 @@ on_stream_param_changed (void *data, + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + uint8_t params_buffer[1024]; +- int32_t width, height, stride, size; + struct spa_pod_builder pod_builder; + const struct spa_pod *params[5]; + int n_params = 0; +- const int bpp = 4; + int buffer_types; + + if (!format || id != SPA_PARAM_Format) +@@ -1027,13 +1031,6 @@ on_stream_param_changed (void *data, + spa_format_video_raw_parse (format, + &priv->video_format); + +- width = priv->video_format.size.width; +- height = priv->video_format.size.height; +- stride = SPA_ROUND_UP_N (width * bpp, 4); +- size = height * stride; +- +- priv->video_stride = stride; +- + pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer)); + + buffer_types = 1 << SPA_DATA_MemFd; +@@ -1045,8 +1042,6 @@ on_stream_param_changed (void *data, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (16, 2, 16), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int (1), +- SPA_PARAM_BUFFERS_size, SPA_POD_Int (size), +- SPA_PARAM_BUFFERS_stride, SPA_POD_Int (stride), + SPA_PARAM_BUFFERS_align, SPA_POD_Int (16), + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int (buffer_types)); + +@@ -1090,15 +1085,11 @@ on_stream_add_buffer (void *data, + CoglDmaBufHandle *dmabuf_handle; + struct spa_buffer *spa_buffer = buffer->buffer; + struct spa_data *spa_data = &spa_buffer->datas[0]; +- const int bpp = 4; + int stride; + + priv->buffer_count++; + +- stride = SPA_ROUND_UP_N (priv->video_format.size.width * bpp, 4); +- + spa_data->mapoffset = 0; +- spa_data->maxsize = stride * priv->video_format.size.height; + spa_data->data = NULL; + + if (spa_data->type & (1 << SPA_DATA_DmaBuf)) +@@ -1143,6 +1134,9 @@ on_stream_add_buffer (void *data, + spa_data->flags = SPA_DATA_FLAG_READWRITE; + spa_data->fd = cogl_dma_buf_handle_get_fd (dmabuf_handle); + ++ stride = meta_screen_cast_stream_src_calculate_stride (src, spa_data); ++ spa_data->maxsize = stride * priv->video_format.size.height; ++ + g_hash_table_insert (priv->dmabuf_handles, + GINT_TO_POINTER (spa_data->fd), + dmabuf_handle); +@@ -1172,6 +1166,8 @@ on_stream_add_buffer (void *data, + g_critical ("Can't create memfd: %m"); + return; + } ++ ++ stride = meta_screen_cast_stream_src_calculate_stride (src, spa_data); + spa_data->maxsize = stride * priv->video_format.size.height; + + if (ftruncate (spa_data->fd, spa_data->maxsize) < 0) +-- +GitLab + diff --git a/3326.patch b/3326.patch new file mode 100644 index 0000000..37f3b61 --- /dev/null +++ b/3326.patch @@ -0,0 +1,78 @@ +From 5809ef62f5e8b12a9cbaf8ec8efac814563bf13e Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Fri, 13 Oct 2023 15:37:27 +0200 +Subject: [PATCH 1/2] screen-cast/stream-src: Calculate stride after adding + handle to hash table + +`calculate_stride()` looks up the dmabuf handle from the hash table so +we need to add it first. + +Fixes 9b663f44e6044ece52c38b3ee23bbc2b55328b47 + +Part-of: +--- + src/backends/meta-screen-cast-stream-src.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index cf198cf6472..fd49c533930 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -1134,12 +1134,12 @@ on_stream_add_buffer (void *data, + spa_data->flags = SPA_DATA_FLAG_READWRITE; + spa_data->fd = cogl_dma_buf_handle_get_fd (dmabuf_handle); + +- stride = meta_screen_cast_stream_src_calculate_stride (src, spa_data); +- spa_data->maxsize = stride * priv->video_format.size.height; +- + g_hash_table_insert (priv->dmabuf_handles, + GINT_TO_POINTER (spa_data->fd), + dmabuf_handle); ++ ++ stride = meta_screen_cast_stream_src_calculate_stride (src, spa_data); ++ spa_data->maxsize = stride * priv->video_format.size.height; + } + else + { +-- +GitLab + + +From 8d3d8b86e517c97accf11eb243078faf31dd72bb Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Fri, 13 Oct 2023 18:29:19 +0200 +Subject: [PATCH 2/2] screen-cast/stream-src: Assert that dmabuf handle lookup + succeeds + +To prevent issues like the one fixed in the previous commit. + +Also remove a redundant variable assignment. + +Part-of: +--- + src/backends/meta-screen-cast-stream-src.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index fd49c533930..273c085d5b8 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -564,12 +564,12 @@ meta_screen_cast_stream_src_calculate_stride (MetaScreenCastStreamSrc *src, + + if (spa_data->type == SPA_DATA_DmaBuf) + { +- CoglDmaBufHandle *dmabuf_handle = NULL; ++ CoglDmaBufHandle *dmabuf_handle; + + dmabuf_handle = g_hash_table_lookup (priv->dmabuf_handles, + GINT_TO_POINTER (spa_data->fd)); +- if (dmabuf_handle) +- return cogl_dma_buf_handle_get_stride (dmabuf_handle); ++ g_assert (dmabuf_handle != NULL); ++ return cogl_dma_buf_handle_get_stride (dmabuf_handle); + } + + if (!cogl_pixel_format_from_spa_video_format (priv->video_format.format, +-- +GitLab + diff --git a/mutter.spec b/mutter.spec index 829c342..755307d 100644 --- a/mutter.spec +++ b/mutter.spec @@ -56,6 +56,15 @@ Patch: 0004-kms-impl-device-Inhibit-real-time-scheduling-when-mo.patch # which solves the problems reported with #3329 alone Patch: 0001-modified-3329.patch +# https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7148 +# https://bugzilla.redhat.com/show_bug.cgi?id=2247033 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3311 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3326 +# Fix problems with screen captures being corrupted or not working on +# certain hardware +Patch: 3311.patch +Patch: 3326.patch + BuildRequires: pkgconfig(gobject-introspection-1.0) >= 1.41.0 BuildRequires: pkgconfig(sm) BuildRequires: pkgconfig(libwacom)