Update to 45.1
This commit is contained in:
parent
cb6d6e8835
commit
efea9ab023
1
.gitignore
vendored
1
.gitignore
vendored
@ -213,3 +213,4 @@ mutter-2.31.5.tar.bz2
|
|||||||
/mutter-45.beta.1.tar.xz
|
/mutter-45.beta.1.tar.xz
|
||||||
/mutter-45.rc.tar.xz
|
/mutter-45.rc.tar.xz
|
||||||
/mutter-45.0.tar.xz
|
/mutter-45.0.tar.xz
|
||||||
|
/mutter-45.1.tar.xz
|
||||||
|
@ -1,149 +0,0 @@
|
|||||||
From 01c01a221a7dabb2382a44b71dcf06649ee193d6 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ray Strode <rstrode@redhat.com>
|
|
||||||
Date: Tue, 10 Oct 2023 14:35:46 -0400
|
|
||||||
Subject: [PATCH 1/4] thread: For consistency, s/real_time/realtime/
|
|
||||||
|
|
||||||
Most of the code writes "real-time" as "realtime" not "real_time".
|
|
||||||
|
|
||||||
The only exception is one function `request_real_time_scheduling`.
|
|
||||||
|
|
||||||
This commit changes that function for consistency.
|
|
||||||
---
|
|
||||||
src/backends/native/meta-thread.c | 6 +++---
|
|
||||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/backends/native/meta-thread.c b/src/backends/native/meta-thread.c
|
|
||||||
index 08d01144d..93a84a8a5 100644
|
|
||||||
--- a/src/backends/native/meta-thread.c
|
|
||||||
+++ b/src/backends/native/meta-thread.c
|
|
||||||
@@ -175,62 +175,62 @@ get_rtkit_property (MetaDBusRealtimeKit1 *rtkit_proxy,
|
|
||||||
{
|
|
||||||
GDBusConnection *connection;
|
|
||||||
g_autoptr (GVariant) prop_value = NULL;
|
|
||||||
g_autoptr (GVariant) property_variant = NULL;
|
|
||||||
|
|
||||||
/* The following is a fall back path for a RTKit daemon that doesn't support
|
|
||||||
* org.freedesktop.DBus.Properties.GetAll. See
|
|
||||||
* <https://github.com/heftig/rtkit/pull/30>.
|
|
||||||
*/
|
|
||||||
connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (rtkit_proxy));
|
|
||||||
prop_value =
|
|
||||||
g_dbus_connection_call_sync (connection,
|
|
||||||
"org.freedesktop.RealtimeKit1",
|
|
||||||
"/org/freedesktop/RealtimeKit1",
|
|
||||||
"org.freedesktop.DBus.Properties",
|
|
||||||
"Get",
|
|
||||||
g_variant_new ("(ss)",
|
|
||||||
"org.freedesktop.RealtimeKit1",
|
|
||||||
property_name),
|
|
||||||
G_VARIANT_TYPE ("(v)"),
|
|
||||||
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
||||||
-1, NULL, error);
|
|
||||||
if (!prop_value)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
g_variant_get (prop_value, "(v)", &property_variant);
|
|
||||||
return g_steal_pointer (&property_variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
-request_real_time_scheduling (MetaThread *thread,
|
|
||||||
- GError **error)
|
|
||||||
+request_realtime_scheduling (MetaThread *thread,
|
|
||||||
+ GError **error)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
g_autoptr (MetaDBusRealtimeKit1) rtkit_proxy = NULL;
|
|
||||||
g_autoptr (GError) local_error = NULL;
|
|
||||||
int64_t rttime;
|
|
||||||
struct rlimit rl;
|
|
||||||
uint32_t priority;
|
|
||||||
|
|
||||||
rtkit_proxy =
|
|
||||||
meta_dbus_realtime_kit1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
||||||
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
|
|
||||||
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
|
||||||
"org.freedesktop.RealtimeKit1",
|
|
||||||
"/org/freedesktop/RealtimeKit1",
|
|
||||||
NULL,
|
|
||||||
&local_error);
|
|
||||||
if (!rtkit_proxy)
|
|
||||||
{
|
|
||||||
g_dbus_error_strip_remote_error (local_error);
|
|
||||||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
|
||||||
"Failed to acquire RTKit D-Bus proxy: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
priority = meta_dbus_realtime_kit1_get_max_realtime_priority (rtkit_proxy);
|
|
||||||
if (priority == 0)
|
|
||||||
{
|
|
||||||
g_autoptr (GVariant) priority_variant = NULL;
|
|
||||||
|
|
||||||
priority_variant = get_rtkit_property (rtkit_proxy,
|
|
||||||
@@ -286,61 +286,61 @@ request_real_time_scheduling (MetaThread *thread,
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gpointer
|
|
||||||
thread_impl_func (gpointer user_data)
|
|
||||||
{
|
|
||||||
MetaThread *thread = META_THREAD (user_data);
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
MetaThreadImpl *impl = priv->impl;
|
|
||||||
MetaThreadImplRunFlags run_flags = META_THREAD_IMPL_RUN_FLAG_NONE;
|
|
||||||
GMainContext *thread_context = meta_thread_impl_get_main_context (impl);
|
|
||||||
#ifdef HAVE_PROFILER
|
|
||||||
MetaContext *context = meta_backend_get_context (priv->backend);
|
|
||||||
MetaProfiler *profiler = meta_context_get_profiler (context);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_mutex_lock (&priv->kernel.init_mutex);
|
|
||||||
g_mutex_unlock (&priv->kernel.init_mutex);
|
|
||||||
|
|
||||||
g_main_context_push_thread_default (thread_context);
|
|
||||||
|
|
||||||
#ifdef HAVE_PROFILER
|
|
||||||
meta_profiler_register_thread (profiler, thread_context, priv->name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (priv->wants_realtime)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
- if (!request_real_time_scheduling (thread, &error))
|
|
||||||
+ if (!request_realtime_scheduling (thread, &error))
|
|
||||||
{
|
|
||||||
g_warning ("Failed to make thread '%s' realtime scheduled: %s",
|
|
||||||
priv->name, error->message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_message ("Made thread '%s' realtime scheduled", priv->name);
|
|
||||||
run_flags |= META_THREAD_IMPL_RUN_FLAG_REALTIME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_thread_impl_run (impl, run_flags);
|
|
||||||
|
|
||||||
#ifdef HAVE_PROFILER
|
|
||||||
meta_profiler_unregister_thread (profiler, thread_context);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_main_context_pop_thread_default (thread_context);
|
|
||||||
|
|
||||||
return GINT_TO_POINTER (TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _WrapperSource
|
|
||||||
{
|
|
||||||
GSource base;
|
|
||||||
|
|
||||||
GMainContext *thread_main_context;
|
|
||||||
|
|
||||||
GPollFD fds[256];
|
|
||||||
gpointer fd_tags[256];
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
|||||||
From 742316746d50084dda4ebda5ad081e59451e3256 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ray Strode <rstrode@redhat.com>
|
|
||||||
Date: Wed, 11 Oct 2023 09:37:54 -0400
|
|
||||||
Subject: [PATCH 2/4] tests/dbusmock-templates/rtkit: Add
|
|
||||||
MakeThreadHighPriority
|
|
||||||
|
|
||||||
mutter will soon need to call an additional method in RTKit,
|
|
||||||
MakeThreadHighPriority.
|
|
||||||
|
|
||||||
In preparation for that, this commit stubs it out in the
|
|
||||||
dbusmock template.
|
|
||||||
---
|
|
||||||
src/tests/dbusmock-templates/rtkit.py | 4 ++++
|
|
||||||
1 file changed, 4 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/src/tests/dbusmock-templates/rtkit.py b/src/tests/dbusmock-templates/rtkit.py
|
|
||||||
index 168582de8..766bd681e 100644
|
|
||||||
--- a/src/tests/dbusmock-templates/rtkit.py
|
|
||||||
+++ b/src/tests/dbusmock-templates/rtkit.py
|
|
||||||
@@ -1,38 +1,42 @@
|
|
||||||
# This program 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 3 of the License, or (at your option) any
|
|
||||||
# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text
|
|
||||||
# of the license.
|
|
||||||
|
|
||||||
__author__ = 'Jonas Ådahl'
|
|
||||||
__copyright__ = '(c) 2022 Red Hat Inc.'
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
from dbusmock import MOCK_IFACE, mockobject
|
|
||||||
|
|
||||||
BUS_NAME = 'org.freedesktop.RealtimeKit1'
|
|
||||||
MAIN_OBJ = '/org/freedesktop/RealtimeKit1'
|
|
||||||
MAIN_IFACE = 'org.freedesktop.RealtimeKit1'
|
|
||||||
SYSTEM_BUS = True
|
|
||||||
|
|
||||||
|
|
||||||
def load(mock, parameters):
|
|
||||||
mock.AddProperty(MAIN_IFACE, 'RTTimeUSecMax', dbus.Int64(200000))
|
|
||||||
mock.AddProperty(MAIN_IFACE, 'MaxRealtimePriority', dbus.Int32(20))
|
|
||||||
mock.AddProperty(MAIN_IFACE, 'MinNiceLevel', dbus.Int32(-15))
|
|
||||||
mock.priorities = dict()
|
|
||||||
|
|
||||||
@dbus.service.method(MAIN_IFACE, in_signature='tu')
|
|
||||||
def MakeThreadRealtime(self, thread, priority):
|
|
||||||
self.priorities[thread] = priority
|
|
||||||
|
|
||||||
+@dbus.service.method(MAIN_IFACE, in_signature='tu')
|
|
||||||
+def MakeThreadHighPriority(self, thread, priority):
|
|
||||||
+ self.priorities[thread] = priority
|
|
||||||
+
|
|
||||||
@dbus.service.method(MOCK_IFACE)
|
|
||||||
def Reset(self):
|
|
||||||
self.priorities = dict()
|
|
||||||
|
|
||||||
@dbus.service.method(MOCK_IFACE, in_signature='t', out_signature='u')
|
|
||||||
def GetThreadPriority(self, thread):
|
|
||||||
if thread in self.priorities:
|
|
||||||
return self.priorities[thread]
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -1,580 +0,0 @@
|
|||||||
From a7c0f106b30137e1fc6159eaecee8e61b8dc48a9 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ray Strode <rstrode@redhat.com>
|
|
||||||
Date: Tue, 10 Oct 2023 14:39:13 -0400
|
|
||||||
Subject: [PATCH 3/4] thread: Allow turning off rt scheduling for running
|
|
||||||
thread
|
|
||||||
|
|
||||||
At the moment if a thread is made real-time there's no going back,
|
|
||||||
it stays real-time for the duration of its life.
|
|
||||||
|
|
||||||
That's suboptimal because real-time threads are expected by RTKit to
|
|
||||||
have an rlimit on their CPU time and certain GPU drivers in the kernel
|
|
||||||
can exceed that CPU time during certain operations like DPMS off.
|
|
||||||
|
|
||||||
This commit adds two new ref counted functions:
|
|
||||||
|
|
||||||
meta_thread_{un,}inhibit_realtime_in_impl
|
|
||||||
|
|
||||||
that allow turning a thread real-time or normally scheduled. At the same
|
|
||||||
time, this commit stores the RTKit proxy as private data on the thread
|
|
||||||
so that it can be reused by the above apis.
|
|
||||||
|
|
||||||
A subsequent commit will use the new APIs.
|
|
||||||
---
|
|
||||||
src/backends/native/meta-thread.c | 189 ++++++++++++++++++++++++++----
|
|
||||||
src/backends/native/meta-thread.h | 3 +
|
|
||||||
2 files changed, 168 insertions(+), 24 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/backends/native/meta-thread.c b/src/backends/native/meta-thread.c
|
|
||||||
index 93a84a8a5..1ed30441d 100644
|
|
||||||
--- a/src/backends/native/meta-thread.c
|
|
||||||
+++ b/src/backends/native/meta-thread.c
|
|
||||||
@@ -56,62 +56,66 @@ typedef struct _MetaThreadCallbackSource
|
|
||||||
|
|
||||||
GMutex mutex;
|
|
||||||
GCond cond;
|
|
||||||
|
|
||||||
MetaThread *thread;
|
|
||||||
GMainContext *main_context;
|
|
||||||
GList *callbacks;
|
|
||||||
gboolean needs_flush;
|
|
||||||
} MetaThreadCallbackSource;
|
|
||||||
|
|
||||||
typedef struct _MetaThreadPrivate
|
|
||||||
{
|
|
||||||
MetaBackend *backend;
|
|
||||||
char *name;
|
|
||||||
|
|
||||||
GMainContext *main_context;
|
|
||||||
|
|
||||||
MetaThreadImpl *impl;
|
|
||||||
gboolean wants_realtime;
|
|
||||||
gboolean waiting_for_impl_task;
|
|
||||||
GSource *wrapper_source;
|
|
||||||
|
|
||||||
GMutex callbacks_mutex;
|
|
||||||
GHashTable *callback_sources;
|
|
||||||
|
|
||||||
MetaThreadType thread_type;
|
|
||||||
|
|
||||||
GThread *main_thread;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
+ MetaDBusRealtimeKit1 *rtkit_proxy;
|
|
||||||
GThread *thread;
|
|
||||||
+ pid_t thread_id;
|
|
||||||
GMutex init_mutex;
|
|
||||||
+ int realtime_inhibit_count;
|
|
||||||
+ gboolean is_realtime;
|
|
||||||
} kernel;
|
|
||||||
} MetaThreadPrivate;
|
|
||||||
|
|
||||||
typedef struct _MetaThreadClassPrivate
|
|
||||||
{
|
|
||||||
GType impl_type;
|
|
||||||
} MetaThreadClassPrivate;
|
|
||||||
|
|
||||||
static void initable_iface_init (GInitableIface *initable_iface);
|
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (MetaThread, meta_thread, G_TYPE_OBJECT,
|
|
||||||
G_ADD_PRIVATE (MetaThread)
|
|
||||||
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
|
||||||
initable_iface_init)
|
|
||||||
g_type_add_class_private (g_define_type_id,
|
|
||||||
sizeof (MetaThreadClassPrivate)))
|
|
||||||
|
|
||||||
static void
|
|
||||||
meta_thread_callback_data_free (MetaThreadCallbackData *callback_data)
|
|
||||||
{
|
|
||||||
if (callback_data->user_data_destroy)
|
|
||||||
callback_data->user_data_destroy (callback_data->user_data);
|
|
||||||
g_free (callback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
meta_thread_get_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
@@ -175,181 +179,278 @@ get_rtkit_property (MetaDBusRealtimeKit1 *rtkit_proxy,
|
|
||||||
{
|
|
||||||
GDBusConnection *connection;
|
|
||||||
g_autoptr (GVariant) prop_value = NULL;
|
|
||||||
g_autoptr (GVariant) property_variant = NULL;
|
|
||||||
|
|
||||||
/* The following is a fall back path for a RTKit daemon that doesn't support
|
|
||||||
* org.freedesktop.DBus.Properties.GetAll. See
|
|
||||||
* <https://github.com/heftig/rtkit/pull/30>.
|
|
||||||
*/
|
|
||||||
connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (rtkit_proxy));
|
|
||||||
prop_value =
|
|
||||||
g_dbus_connection_call_sync (connection,
|
|
||||||
"org.freedesktop.RealtimeKit1",
|
|
||||||
"/org/freedesktop/RealtimeKit1",
|
|
||||||
"org.freedesktop.DBus.Properties",
|
|
||||||
"Get",
|
|
||||||
g_variant_new ("(ss)",
|
|
||||||
"org.freedesktop.RealtimeKit1",
|
|
||||||
property_name),
|
|
||||||
G_VARIANT_TYPE ("(v)"),
|
|
||||||
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
||||||
-1, NULL, error);
|
|
||||||
if (!prop_value)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
g_variant_get (prop_value, "(v)", &property_variant);
|
|
||||||
return g_steal_pointer (&property_variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
-request_realtime_scheduling (MetaThread *thread,
|
|
||||||
- GError **error)
|
|
||||||
+ensure_realtime_kit_proxy (MetaThread *thread,
|
|
||||||
+ GError **error)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
g_autoptr (MetaDBusRealtimeKit1) rtkit_proxy = NULL;
|
|
||||||
g_autoptr (GError) local_error = NULL;
|
|
||||||
- int64_t rttime;
|
|
||||||
- struct rlimit rl;
|
|
||||||
- uint32_t priority;
|
|
||||||
+
|
|
||||||
+ if (priv->kernel.rtkit_proxy)
|
|
||||||
+ return TRUE;
|
|
||||||
|
|
||||||
rtkit_proxy =
|
|
||||||
meta_dbus_realtime_kit1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
||||||
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
|
|
||||||
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
|
||||||
"org.freedesktop.RealtimeKit1",
|
|
||||||
"/org/freedesktop/RealtimeKit1",
|
|
||||||
NULL,
|
|
||||||
&local_error);
|
|
||||||
if (!rtkit_proxy)
|
|
||||||
{
|
|
||||||
g_dbus_error_strip_remote_error (local_error);
|
|
||||||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
|
||||||
"Failed to acquire RTKit D-Bus proxy: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
- priority = meta_dbus_realtime_kit1_get_max_realtime_priority (rtkit_proxy);
|
|
||||||
+ priv->kernel.rtkit_proxy = g_steal_pointer (&rtkit_proxy);
|
|
||||||
+ return TRUE;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static gboolean
|
|
||||||
+request_realtime_scheduling (MetaThread *thread,
|
|
||||||
+ GError **error)
|
|
||||||
+{
|
|
||||||
+ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
+ g_autoptr (GError) local_error = NULL;
|
|
||||||
+ int64_t rttime;
|
|
||||||
+ struct rlimit rl;
|
|
||||||
+ uint32_t priority;
|
|
||||||
+
|
|
||||||
+ if (!ensure_realtime_kit_proxy (thread, error))
|
|
||||||
+ return FALSE;
|
|
||||||
+
|
|
||||||
+ priority = meta_dbus_realtime_kit1_get_max_realtime_priority (priv->kernel.rtkit_proxy);
|
|
||||||
if (priority == 0)
|
|
||||||
{
|
|
||||||
g_autoptr (GVariant) priority_variant = NULL;
|
|
||||||
|
|
||||||
- priority_variant = get_rtkit_property (rtkit_proxy,
|
|
||||||
+ priority_variant = get_rtkit_property (priv->kernel.rtkit_proxy,
|
|
||||||
"MaxRealtimePriority",
|
|
||||||
error);
|
|
||||||
if (!priority_variant)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
priority = g_variant_get_int32 (priority_variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priority == 0)
|
|
||||||
g_warning ("Maximum real time scheduling priority is 0");
|
|
||||||
|
|
||||||
- rttime = meta_dbus_realtime_kit1_get_rttime_usec_max (rtkit_proxy);
|
|
||||||
+ rttime = meta_dbus_realtime_kit1_get_rttime_usec_max (priv->kernel.rtkit_proxy);
|
|
||||||
if (rttime == 0)
|
|
||||||
{
|
|
||||||
g_autoptr (GVariant) rttime_variant = NULL;
|
|
||||||
|
|
||||||
- rttime_variant = get_rtkit_property (rtkit_proxy,
|
|
||||||
+ rttime_variant = get_rtkit_property (priv->kernel.rtkit_proxy,
|
|
||||||
"RTTimeUSecMax",
|
|
||||||
error);
|
|
||||||
if (!rttime_variant)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
rttime = g_variant_get_int64 (rttime_variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_topic (META_DEBUG_BACKEND,
|
|
||||||
"Setting soft and hard RLIMIT_RTTIME limit to %lu", rttime);
|
|
||||||
rl.rlim_cur = rttime;
|
|
||||||
rl.rlim_max = rttime;
|
|
||||||
|
|
||||||
if (setrlimit (RLIMIT_RTTIME, &rl) != 0)
|
|
||||||
{
|
|
||||||
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
|
||||||
"Failed to set RLIMIT_RTTIME: %s", g_strerror (errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_topic (META_DEBUG_BACKEND, "Setting '%s' thread real time priority to %d",
|
|
||||||
priv->name, priority);
|
|
||||||
- if (!meta_dbus_realtime_kit1_call_make_thread_realtime_sync (rtkit_proxy,
|
|
||||||
- gettid (),
|
|
||||||
+ if (!meta_dbus_realtime_kit1_call_make_thread_realtime_sync (priv->kernel.rtkit_proxy,
|
|
||||||
+ priv->kernel.thread_id,
|
|
||||||
priority,
|
|
||||||
NULL,
|
|
||||||
&local_error))
|
|
||||||
{
|
|
||||||
g_dbus_error_strip_remote_error (local_error);
|
|
||||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static gboolean
|
|
||||||
+request_normal_scheduling (MetaThread *thread,
|
|
||||||
+ GError **error)
|
|
||||||
+{
|
|
||||||
+ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
+ g_autoptr (GError) local_error = NULL;
|
|
||||||
+
|
|
||||||
+ if (!ensure_realtime_kit_proxy (thread, error))
|
|
||||||
+ return FALSE;
|
|
||||||
+
|
|
||||||
+ meta_topic (META_DEBUG_BACKEND, "Setting '%s' thread to normal priority", priv->name);
|
|
||||||
+ if (!meta_dbus_realtime_kit1_call_make_thread_high_priority_sync (priv->kernel.rtkit_proxy,
|
|
||||||
+ priv->kernel.thread_id,
|
|
||||||
+ 0 /* "normal" nice value */,
|
|
||||||
+ NULL,
|
|
||||||
+ &local_error))
|
|
||||||
+ {
|
|
||||||
+ g_dbus_error_strip_remote_error (local_error);
|
|
||||||
+ g_propagate_error (error, g_steal_pointer (&local_error));
|
|
||||||
+ return FALSE;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return TRUE;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static gboolean
|
|
||||||
+should_use_realtime_scheduling_in_impl (MetaThread *thread)
|
|
||||||
+{
|
|
||||||
+ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
+ gboolean should_use_realtime_scheduling = FALSE;
|
|
||||||
+
|
|
||||||
+ switch (priv->thread_type)
|
|
||||||
+ {
|
|
||||||
+ case META_THREAD_TYPE_USER:
|
|
||||||
+ break;
|
|
||||||
+ case META_THREAD_TYPE_KERNEL:
|
|
||||||
+ if (priv->wants_realtime && priv->kernel.realtime_inhibit_count == 0)
|
|
||||||
+ should_use_realtime_scheduling = TRUE;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return should_use_realtime_scheduling;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void
|
|
||||||
+sync_realtime_scheduling_in_impl (MetaThread *thread)
|
|
||||||
+{
|
|
||||||
+ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
+ g_autoptr (GError) error = NULL;
|
|
||||||
+ gboolean should_be_realtime;
|
|
||||||
+
|
|
||||||
+ should_be_realtime = should_use_realtime_scheduling_in_impl (thread);
|
|
||||||
+
|
|
||||||
+ if (should_be_realtime == priv->kernel.is_realtime)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (should_be_realtime)
|
|
||||||
+ {
|
|
||||||
+ if (!request_realtime_scheduling (thread, &error))
|
|
||||||
+ {
|
|
||||||
+ g_warning ("Failed to make thread '%s' realtime scheduled: %s",
|
|
||||||
+ priv->name, error->message);
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ {
|
|
||||||
+ meta_topic (META_DEBUG_BACKEND, "Made thread '%s' real-time scheduled", priv->name);
|
|
||||||
+ priv->kernel.is_realtime = TRUE;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ {
|
|
||||||
+ if (!request_normal_scheduling (thread, &error))
|
|
||||||
+ {
|
|
||||||
+ g_warning ("Failed to make thread '%s' normally scheduled: %s",
|
|
||||||
+ priv->name, error->message);
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ {
|
|
||||||
+ meta_topic (META_DEBUG_BACKEND, "Made thread '%s' normally scheduled", priv->name);
|
|
||||||
+ priv->kernel.is_realtime = FALSE;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static gpointer
|
|
||||||
thread_impl_func (gpointer user_data)
|
|
||||||
{
|
|
||||||
MetaThread *thread = META_THREAD (user_data);
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
MetaThreadImpl *impl = priv->impl;
|
|
||||||
MetaThreadImplRunFlags run_flags = META_THREAD_IMPL_RUN_FLAG_NONE;
|
|
||||||
GMainContext *thread_context = meta_thread_impl_get_main_context (impl);
|
|
||||||
#ifdef HAVE_PROFILER
|
|
||||||
MetaContext *context = meta_backend_get_context (priv->backend);
|
|
||||||
MetaProfiler *profiler = meta_context_get_profiler (context);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_mutex_lock (&priv->kernel.init_mutex);
|
|
||||||
g_mutex_unlock (&priv->kernel.init_mutex);
|
|
||||||
|
|
||||||
g_main_context_push_thread_default (thread_context);
|
|
||||||
|
|
||||||
#ifdef HAVE_PROFILER
|
|
||||||
meta_profiler_register_thread (profiler, thread_context, priv->name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
- if (priv->wants_realtime)
|
|
||||||
- {
|
|
||||||
- g_autoptr (GError) error = NULL;
|
|
||||||
+ priv->kernel.thread_id = gettid ();
|
|
||||||
+ priv->kernel.realtime_inhibit_count = 0;
|
|
||||||
+ priv->kernel.is_realtime = FALSE;
|
|
||||||
|
|
||||||
- if (!request_realtime_scheduling (thread, &error))
|
|
||||||
- {
|
|
||||||
- g_warning ("Failed to make thread '%s' realtime scheduled: %s",
|
|
||||||
- priv->name, error->message);
|
|
||||||
- }
|
|
||||||
- else
|
|
||||||
- {
|
|
||||||
- g_message ("Made thread '%s' realtime scheduled", priv->name);
|
|
||||||
- run_flags |= META_THREAD_IMPL_RUN_FLAG_REALTIME;
|
|
||||||
- }
|
|
||||||
+ sync_realtime_scheduling_in_impl (thread);
|
|
||||||
+
|
|
||||||
+ if (priv->kernel.is_realtime)
|
|
||||||
+ {
|
|
||||||
+ g_message ("Made thread '%s' realtime scheduled", priv->name);
|
|
||||||
+ run_flags |= META_THREAD_IMPL_RUN_FLAG_REALTIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_thread_impl_run (impl, run_flags);
|
|
||||||
|
|
||||||
#ifdef HAVE_PROFILER
|
|
||||||
meta_profiler_unregister_thread (profiler, thread_context);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_main_context_pop_thread_default (thread_context);
|
|
||||||
|
|
||||||
return GINT_TO_POINTER (TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _WrapperSource
|
|
||||||
{
|
|
||||||
GSource base;
|
|
||||||
|
|
||||||
GMainContext *thread_main_context;
|
|
||||||
|
|
||||||
GPollFD fds[256];
|
|
||||||
gpointer fd_tags[256];
|
|
||||||
int n_fds;
|
|
||||||
int priority;
|
|
||||||
} WrapperSource;
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
wrapper_source_prepare (GSource *source,
|
|
||||||
int *timeout)
|
|
||||||
{
|
|
||||||
WrapperSource *wrapper_source = (WrapperSource *) source;
|
|
||||||
@@ -522,60 +623,64 @@ meta_thread_initable_init (GInitable *initable,
|
|
||||||
|
|
||||||
start_thread (thread);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
initable_iface_init (GInitableIface *initable_iface)
|
|
||||||
{
|
|
||||||
initable_iface->init = meta_thread_initable_init;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
finalize_thread_user (MetaThread *thread)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
meta_thread_impl_terminate (priv->impl);
|
|
||||||
while (meta_thread_impl_dispatch (priv->impl) > 0);
|
|
||||||
unwrap_main_context (thread, meta_thread_impl_get_main_context (priv->impl));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
finalize_thread_kernel (MetaThread *thread)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
meta_thread_impl_terminate (priv->impl);
|
|
||||||
g_thread_join (priv->kernel.thread);
|
|
||||||
priv->kernel.thread = NULL;
|
|
||||||
+ priv->kernel.thread_id = 0;
|
|
||||||
+
|
|
||||||
+ g_clear_object (&priv->kernel.rtkit_proxy);
|
|
||||||
+
|
|
||||||
g_mutex_clear (&priv->kernel.init_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tear_down_thread (MetaThread *thread)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
switch (priv->thread_type)
|
|
||||||
{
|
|
||||||
case META_THREAD_TYPE_USER:
|
|
||||||
finalize_thread_user (thread);
|
|
||||||
break;
|
|
||||||
case META_THREAD_TYPE_KERNEL:
|
|
||||||
finalize_thread_kernel (thread);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_thread_flush_callbacks (thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
meta_thread_finalize (GObject *object)
|
|
||||||
{
|
|
||||||
MetaThread *thread = META_THREAD (object);
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
tear_down_thread (thread);
|
|
||||||
|
|
||||||
meta_thread_unregister_callback_context (thread, priv->main_context);
|
|
||||||
@@ -1134,30 +1239,66 @@ meta_thread_get_thread_type (MetaThread *thread)
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
return priv->thread_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
GThread *
|
|
||||||
meta_thread_get_thread (MetaThread *thread)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
g_assert (priv->thread_type == META_THREAD_TYPE_KERNEL);
|
|
||||||
|
|
||||||
return priv->kernel.thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
meta_thread_is_in_impl_task (MetaThread *thread)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
return meta_thread_impl_is_in_impl (priv->impl);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
meta_thread_is_waiting_for_impl_task (MetaThread *thread)
|
|
||||||
{
|
|
||||||
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
|
|
||||||
return priv->waiting_for_impl_task;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+void
|
|
||||||
+meta_thread_inhibit_realtime_in_impl (MetaThread *thread)
|
|
||||||
+{
|
|
||||||
+ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
+
|
|
||||||
+ switch (priv->thread_type)
|
|
||||||
+ {
|
|
||||||
+ case META_THREAD_TYPE_KERNEL:
|
|
||||||
+ priv->kernel.realtime_inhibit_count++;
|
|
||||||
+
|
|
||||||
+ if (priv->kernel.realtime_inhibit_count == 1)
|
|
||||||
+ sync_realtime_scheduling_in_impl (thread);
|
|
||||||
+ break;
|
|
||||||
+ case META_THREAD_TYPE_USER:
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void
|
|
||||||
+meta_thread_uninhibit_realtime_in_impl (MetaThread *thread)
|
|
||||||
+{
|
|
||||||
+ MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
|
|
||||||
+
|
|
||||||
+ switch (priv->thread_type)
|
|
||||||
+ {
|
|
||||||
+ case META_THREAD_TYPE_KERNEL:
|
|
||||||
+ priv->kernel.realtime_inhibit_count--;
|
|
||||||
+
|
|
||||||
+ if (priv->kernel.realtime_inhibit_count == 0)
|
|
||||||
+ sync_realtime_scheduling_in_impl (thread);
|
|
||||||
+ break;
|
|
||||||
+ case META_THREAD_TYPE_USER:
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
diff --git a/src/backends/native/meta-thread.h b/src/backends/native/meta-thread.h
|
|
||||||
index f6c5c94f5..4765719ec 100644
|
|
||||||
--- a/src/backends/native/meta-thread.h
|
|
||||||
+++ b/src/backends/native/meta-thread.h
|
|
||||||
@@ -71,36 +71,39 @@ void meta_thread_queue_callback (MetaThread *thread,
|
|
||||||
GDestroyNotify user_data_destroy);
|
|
||||||
|
|
||||||
META_EXPORT_TEST
|
|
||||||
void meta_thread_flush_callbacks (MetaThread *thread);
|
|
||||||
|
|
||||||
META_EXPORT_TEST
|
|
||||||
gpointer meta_thread_run_impl_task_sync (MetaThread *thread,
|
|
||||||
MetaThreadTaskFunc func,
|
|
||||||
gpointer user_data,
|
|
||||||
GError **error);
|
|
||||||
|
|
||||||
META_EXPORT_TEST
|
|
||||||
void meta_thread_post_impl_task (MetaThread *thread,
|
|
||||||
MetaThreadTaskFunc func,
|
|
||||||
gpointer user_data,
|
|
||||||
GDestroyNotify user_data_destroy,
|
|
||||||
MetaThreadTaskFeedbackFunc feedback_func,
|
|
||||||
gpointer feedback_user_data);
|
|
||||||
|
|
||||||
META_EXPORT_TEST
|
|
||||||
MetaBackend * meta_thread_get_backend (MetaThread *thread);
|
|
||||||
|
|
||||||
META_EXPORT_TEST
|
|
||||||
const char * meta_thread_get_name (MetaThread *thread);
|
|
||||||
|
|
||||||
META_EXPORT_TEST
|
|
||||||
gboolean meta_thread_is_in_impl_task (MetaThread *thread);
|
|
||||||
|
|
||||||
gboolean meta_thread_is_waiting_for_impl_task (MetaThread *thread);
|
|
||||||
|
|
||||||
+void meta_thread_inhibit_realtime_in_impl (MetaThread *thread);
|
|
||||||
+void meta_thread_uninhibit_realtime_in_impl (MetaThread *thread);
|
|
||||||
+
|
|
||||||
#define meta_assert_in_thread_impl(thread) \
|
|
||||||
g_assert (meta_thread_is_in_impl_task (thread))
|
|
||||||
#define meta_assert_not_in_thread_impl(thread) \
|
|
||||||
g_assert (!meta_thread_is_in_impl_task (thread))
|
|
||||||
#define meta_assert_is_waiting_for_thread_impl_task(thread) \
|
|
||||||
g_assert (meta_thread_is_waiting_for_impl_task (thread))
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -1,183 +0,0 @@
|
|||||||
From 105a3d60df5df74fd7ea78cacc0d9ca30607b45a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ray Strode <rstrode@redhat.com>
|
|
||||||
Date: Tue, 10 Oct 2023 14:48:55 -0400
|
|
||||||
Subject: [PATCH 4/4] kms/impl-device: Inhibit real-time scheduling when mode
|
|
||||||
setting
|
|
||||||
|
|
||||||
Certain kernel drivers can take an unreasonably long time to
|
|
||||||
complete mode setting operations. That excessive CPU time is charged
|
|
||||||
to the process's rlimits which can lead to the process getting killed
|
|
||||||
if the thread is a real-time thread.
|
|
||||||
|
|
||||||
This commit inhibits real-time scheduling around mode setting
|
|
||||||
commits, since those commits are the ones currently presenting as
|
|
||||||
excessively slow.
|
|
||||||
|
|
||||||
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3037
|
|
||||||
---
|
|
||||||
src/backends/native/meta-kms-impl-device.c | 15 ++++++++++++++-
|
|
||||||
1 file changed, 14 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c
|
|
||||||
index da372383d..d53552704 100644
|
|
||||||
--- a/src/backends/native/meta-kms-impl-device.c
|
|
||||||
+++ b/src/backends/native/meta-kms-impl-device.c
|
|
||||||
@@ -1554,141 +1554,154 @@ meta_kms_impl_device_schedule_process (MetaKmsImplDevice *impl_device,
|
|
||||||
|
|
||||||
if (crtc_frame->await_flush)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!is_using_deadline_timer (impl_device))
|
|
||||||
goto needs_flush;
|
|
||||||
|
|
||||||
if (crtc_frame->pending_page_flip)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ensure_deadline_timer_armed (impl_device, crtc_frame, &error))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
|
||||||
g_warning ("Failed to determine deadline: %s", error->message);
|
|
||||||
|
|
||||||
priv = meta_kms_impl_device_get_instance_private (impl_device);
|
|
||||||
priv->deadline_timer_inhibited = TRUE;
|
|
||||||
|
|
||||||
needs_flush:
|
|
||||||
meta_kms_device_set_needs_flush (meta_kms_crtc_get_device (crtc), crtc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static MetaKmsFeedback *
|
|
||||||
process_mode_set_update (MetaKmsImplDevice *impl_device,
|
|
||||||
MetaKmsUpdate *update,
|
|
||||||
MetaKmsUpdateFlag flags)
|
|
||||||
{
|
|
||||||
MetaKmsImplDevicePrivate *priv =
|
|
||||||
meta_kms_impl_device_get_instance_private (impl_device);
|
|
||||||
+ MetaKmsImpl *kms_impl = meta_kms_impl_device_get_impl (impl_device);
|
|
||||||
+ MetaThreadImpl *thread_impl = META_THREAD_IMPL (kms_impl);
|
|
||||||
+ MetaThread *thread = meta_thread_impl_get_thread (thread_impl);
|
|
||||||
+ MetaKmsFeedback *feedback;
|
|
||||||
CrtcFrame *crtc_frame;
|
|
||||||
GList *l;
|
|
||||||
GHashTableIter iter;
|
|
||||||
|
|
||||||
for (l = meta_kms_update_get_mode_sets (update); l; l = l->next)
|
|
||||||
{
|
|
||||||
MetaKmsModeSet *mode_set = l->data;
|
|
||||||
MetaKmsCrtc *crtc = mode_set->crtc;
|
|
||||||
|
|
||||||
crtc_frame = get_crtc_frame (impl_device, crtc);
|
|
||||||
if (!crtc_frame)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!crtc_frame->pending_update)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
meta_kms_update_merge_from (update, crtc_frame->pending_update);
|
|
||||||
g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_hash_table_iter_init (&iter, priv->crtc_frames);
|
|
||||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &crtc_frame))
|
|
||||||
{
|
|
||||||
crtc_frame->deadline.is_deadline_page_flip = FALSE;
|
|
||||||
crtc_frame->await_flush = FALSE;
|
|
||||||
crtc_frame->pending_page_flip = FALSE;
|
|
||||||
g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free);
|
|
||||||
disarm_crtc_frame_deadline_timer (crtc_frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- return do_process (impl_device, NULL, update, flags);
|
|
||||||
+ meta_thread_inhibit_realtime_in_impl (thread);
|
|
||||||
+ feedback = do_process (impl_device, NULL, update, flags);
|
|
||||||
+ meta_thread_uninhibit_realtime_in_impl (thread);
|
|
||||||
+
|
|
||||||
+ return feedback;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetaKmsFeedback *
|
|
||||||
meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device,
|
|
||||||
MetaKmsUpdate *update,
|
|
||||||
MetaKmsUpdateFlag flags)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) error = NULL;
|
|
||||||
|
|
||||||
if (!ensure_device_file (impl_device, &error))
|
|
||||||
{
|
|
||||||
MetaKmsFeedback *feedback = NULL;
|
|
||||||
|
|
||||||
feedback = meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error));
|
|
||||||
queue_result_feedback (impl_device, update, feedback);
|
|
||||||
|
|
||||||
meta_kms_update_free (update);
|
|
||||||
return feedback;
|
|
||||||
}
|
|
||||||
|
|
||||||
meta_kms_update_realize (update, impl_device);
|
|
||||||
|
|
||||||
if (flags & META_KMS_UPDATE_FLAG_TEST_ONLY)
|
|
||||||
{
|
|
||||||
return do_process (impl_device,
|
|
||||||
meta_kms_update_get_latch_crtc (update),
|
|
||||||
update, flags);
|
|
||||||
}
|
|
||||||
else if (flags & META_KMS_UPDATE_FLAG_MODE_SET)
|
|
||||||
{
|
|
||||||
return process_mode_set_update (impl_device, update, flags);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_assert_not_reached ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
meta_kms_impl_device_disable (MetaKmsImplDevice *impl_device)
|
|
||||||
{
|
|
||||||
MetaKmsImplDevicePrivate *priv =
|
|
||||||
meta_kms_impl_device_get_instance_private (impl_device);
|
|
||||||
+ MetaKmsImpl *kms_impl = meta_kms_impl_device_get_impl (impl_device);
|
|
||||||
+ MetaThreadImpl *thread_impl = META_THREAD_IMPL (kms_impl);
|
|
||||||
+ MetaThread *thread = meta_thread_impl_get_thread (thread_impl);
|
|
||||||
MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
|
|
||||||
|
|
||||||
if (!priv->device_file)
|
|
||||||
return;
|
|
||||||
|
|
||||||
meta_kms_impl_device_hold_fd (impl_device);
|
|
||||||
+ meta_thread_inhibit_realtime_in_impl (thread);
|
|
||||||
klass->disable (impl_device);
|
|
||||||
+ meta_thread_uninhibit_realtime_in_impl (thread);
|
|
||||||
g_list_foreach (priv->crtcs,
|
|
||||||
(GFunc) meta_kms_crtc_disable_in_impl, NULL);
|
|
||||||
g_list_foreach (priv->connectors,
|
|
||||||
(GFunc) meta_kms_connector_disable_in_impl, NULL);
|
|
||||||
meta_kms_impl_device_unhold_fd (impl_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
meta_kms_impl_device_handle_page_flip_callback (MetaKmsImplDevice *impl_device,
|
|
||||||
MetaKmsPageFlipData *page_flip_data)
|
|
||||||
{
|
|
||||||
MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
|
|
||||||
|
|
||||||
klass->handle_page_flip_callback (impl_device, page_flip_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
meta_kms_impl_device_discard_pending_page_flips (MetaKmsImplDevice *impl_device)
|
|
||||||
{
|
|
||||||
MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
|
|
||||||
|
|
||||||
klass->discard_pending_page_flips (impl_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
meta_kms_impl_device_hold_fd (MetaKmsImplDevice *impl_device)
|
|
||||||
{
|
|
||||||
MetaKmsImplDevicePrivate *priv =
|
|
||||||
meta_kms_impl_device_get_instance_private (impl_device);
|
|
||||||
MetaKms *kms = meta_kms_device_get_kms (priv->device);
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
58
3299.patch
58
3299.patch
@ -1,58 +0,0 @@
|
|||||||
From 4008ed2f55d4621a4ed2230799c1ab2d012b3545 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
|
||||||
Date: Wed, 27 Sep 2023 22:59:13 +0800
|
|
||||||
Subject: [PATCH] monitor-config-store: Discard config with fractional scale
|
|
||||||
when unusable
|
|
||||||
|
|
||||||
When a configuration has a fractional scale, but we're using a physical
|
|
||||||
monitor layout, we can't use the scale, but if we do, we end up with
|
|
||||||
wierd issues down the line. Just discard the config if we run into this.
|
|
||||||
|
|
||||||
Eventually we probably want to store the layout mode in the
|
|
||||||
configuration so we can handle more seamless switching between physical
|
|
||||||
and logical layout mode, but first do this.
|
|
||||||
|
|
||||||
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3057
|
|
||||||
---
|
|
||||||
src/backends/meta-monitor-config-store.c | 13 +++++++++++--
|
|
||||||
1 file changed, 11 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c
|
|
||||||
index fe9406fd3f7..53a32cd2aaf 100644
|
|
||||||
--- a/src/backends/meta-monitor-config-store.c
|
|
||||||
+++ b/src/backends/meta-monitor-config-store.c
|
|
||||||
@@ -653,6 +653,7 @@ derive_logical_monitor_layout (MetaLogicalMonitorConfig *logical_monitor_conf
|
|
||||||
MetaMonitorConfig *monitor_config;
|
|
||||||
int mode_width, mode_height;
|
|
||||||
int width = 0, height = 0;
|
|
||||||
+ float scale;
|
|
||||||
GList *l;
|
|
||||||
|
|
||||||
monitor_config = logical_monitor_config->monitor_configs->data;
|
|
||||||
@@ -683,13 +684,21 @@ derive_logical_monitor_layout (MetaLogicalMonitorConfig *logical_monitor_conf
|
|
||||||
height = mode_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ scale = logical_monitor_config->scale;
|
|
||||||
+
|
|
||||||
switch (layout_mode)
|
|
||||||
{
|
|
||||||
case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
|
|
||||||
- width = roundf (width / logical_monitor_config->scale);
|
|
||||||
- height = roundf (height / logical_monitor_config->scale);
|
|
||||||
+ width = roundf (width / scale);
|
|
||||||
+ height = roundf (height / scale);
|
|
||||||
break;
|
|
||||||
case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
|
|
||||||
+ if (!G_APPROX_VALUE (scale, roundf (scale), FLT_EPSILON))
|
|
||||||
+ {
|
|
||||||
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
||||||
+ "A fractional scale with physical layout mode not allowed");
|
|
||||||
+ return FALSE;
|
|
||||||
+ }
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
65
3306.patch
65
3306.patch
@ -1,65 +0,0 @@
|
|||||||
From 6fe1b3145f840d39240046c0812b1406d23fe569 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Carlos Garnacho <carlosg@gnome.org>
|
|
||||||
Date: Fri, 29 Sep 2023 13:14:14 +0200
|
|
||||||
Subject: [PATCH] backends/native: Translate keycodes with
|
|
||||||
xkb_key_state_get_one_sym()
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=UTF-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
There's two aspects from its documentation
|
|
||||||
(https://xkbcommon.org/doc/current/group__state.html#gae56031a8c1d48e7802da32f5f39f5738)
|
|
||||||
affecting us here:
|
|
||||||
|
|
||||||
1. "This function is similar to xkb_state_key_get_syms(), but intended for
|
|
||||||
users which cannot or do not want to handle the case where multiple
|
|
||||||
keysyms are returned (in which case this function is preferred)."
|
|
||||||
|
|
||||||
We are indeed in that field, and have been for a long time.
|
|
||||||
|
|
||||||
2. "This function performs Capitalization Keysym Transformations."
|
|
||||||
|
|
||||||
This is unlike the xkb_key_get_syms() function that we use, and
|
|
||||||
convenient here for parity with X11 since it behaves exactly that
|
|
||||||
way.
|
|
||||||
|
|
||||||
Fixes cases where the keysym for some keys is not properly capitalized
|
|
||||||
when caps lock is toggled, due to the output of capslock+key being
|
|
||||||
different from shift+key. An example of this is 'é' in french(azerty)
|
|
||||||
layout (bound to the '2' key). Even though shift+2 outputs '2',
|
|
||||||
capslock+é should output 'É'.
|
|
||||||
|
|
||||||
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3058
|
|
||||||
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3306>
|
|
||||||
---
|
|
||||||
src/backends/native/meta-xkb-utils.c | 7 +------
|
|
||||||
1 file changed, 1 insertion(+), 6 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/backends/native/meta-xkb-utils.c b/src/backends/native/meta-xkb-utils.c
|
|
||||||
index 1ec12a37bea..7f80cca2e2a 100644
|
|
||||||
--- a/src/backends/native/meta-xkb-utils.c
|
|
||||||
+++ b/src/backends/native/meta-xkb-utils.c
|
|
||||||
@@ -50,7 +50,6 @@ meta_key_event_new_from_evdev (ClutterInputDevice *device,
|
|
||||||
{
|
|
||||||
ClutterEvent *event;
|
|
||||||
xkb_keysym_t sym;
|
|
||||||
- const xkb_keysym_t *syms;
|
|
||||||
char buffer[8];
|
|
||||||
gunichar unicode_value;
|
|
||||||
ClutterModifierType modifiers;
|
|
||||||
@@ -62,11 +61,7 @@ meta_key_event_new_from_evdev (ClutterInputDevice *device,
|
|
||||||
* upwards by 8. */
|
|
||||||
key = meta_xkb_evdev_to_keycode (key);
|
|
||||||
|
|
||||||
- n = xkb_key_get_syms (xkb_state, key, &syms);
|
|
||||||
- if (n == 1)
|
|
||||||
- sym = syms[0];
|
|
||||||
- else
|
|
||||||
- sym = XKB_KEY_NoSymbol;
|
|
||||||
+ sym = xkb_state_key_get_one_sym (xkb_state, key);
|
|
||||||
|
|
||||||
modifiers = xkb_state_serialize_mods (xkb_state, XKB_STATE_MODS_EFFECTIVE) |
|
|
||||||
button_state;
|
|
||||||
--
|
|
||||||
GitLab
|
|
||||||
|
|
460
3311.patch
460
3311.patch
@ -1,460 +0,0 @@
|
|||||||
From b258e1f7eefa67006da72172d2a61389a58b7630 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Robert Mader <robert.mader@collabora.com>
|
|
||||||
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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3311>
|
|
||||||
---
|
|
||||||
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 <robert.mader@collabora.com>
|
|
||||||
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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3311>
|
|
||||||
---
|
|
||||||
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
|
|
||||||
|
|
78
3326.patch
78
3326.patch
@ -1,78 +0,0 @@
|
|||||||
From 5809ef62f5e8b12a9cbaf8ec8efac814563bf13e Mon Sep 17 00:00:00 2001
|
|
||||||
From: Robert Mader <robert.mader@collabora.com>
|
|
||||||
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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3326>
|
|
||||||
---
|
|
||||||
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 <robert.mader@collabora.com>
|
|
||||||
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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3326>
|
|
||||||
---
|
|
||||||
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
|
|
||||||
|
|
23
mutter.spec
23
mutter.spec
@ -13,7 +13,7 @@
|
|||||||
%global tarball_version %%(echo %{version} | tr '~' '.')
|
%global tarball_version %%(echo %{version} | tr '~' '.')
|
||||||
|
|
||||||
Name: mutter
|
Name: mutter
|
||||||
Version: 45.0
|
Version: 45.1
|
||||||
Release: %autorelease
|
Release: %autorelease
|
||||||
Summary: Window and compositing manager based on Clutter
|
Summary: Window and compositing manager based on Clutter
|
||||||
|
|
||||||
@ -32,39 +32,18 @@ Patch: 0001-place-Always-center-initial-setup-fedora-welcome.patch
|
|||||||
|
|
||||||
Patch: 0001-gschema-Enable-scale-monitor-framebuffer-experimenta.patch
|
Patch: 0001-gschema-Enable-scale-monitor-framebuffer-experimenta.patch
|
||||||
|
|
||||||
# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3299
|
|
||||||
Patch: 3299.patch
|
|
||||||
|
|
||||||
# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3306
|
|
||||||
Patch: 3306.patch
|
|
||||||
|
|
||||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2239128
|
# https://bugzilla.redhat.com/show_bug.cgi?id=2239128
|
||||||
# https://gitlab.gnome.org/GNOME/mutter/-/issues/3068
|
# https://gitlab.gnome.org/GNOME/mutter/-/issues/3068
|
||||||
# not upstreamed because for upstream we'd really want to find a way
|
# not upstreamed because for upstream we'd really want to find a way
|
||||||
# to fix *both* problems
|
# to fix *both* problems
|
||||||
Patch: 0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch
|
Patch: 0001-Revert-x11-Use-input-region-from-frame-window-for-de.patch
|
||||||
|
|
||||||
# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3324
|
|
||||||
Patch: 0001-thread-For-consistency-s-real_time-realtime.patch
|
|
||||||
Patch: 0002-tests-dbusmock-templates-rtkit-Add-MakeThreadHighPri.patch
|
|
||||||
Patch: 0003-thread-Allow-turning-off-rt-scheduling-for-running-t.patch
|
|
||||||
Patch: 0004-kms-impl-device-Inhibit-real-time-scheduling-when-mo.patch
|
|
||||||
|
|
||||||
# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329
|
# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329
|
||||||
# Modified to add the change from
|
# Modified to add the change from
|
||||||
# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329#note_1874837
|
# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3329#note_1874837
|
||||||
# which solves the problems reported with #3329 alone
|
# which solves the problems reported with #3329 alone
|
||||||
Patch: 0001-modified-3329.patch
|
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(gobject-introspection-1.0) >= 1.41.0
|
||||||
BuildRequires: pkgconfig(sm)
|
BuildRequires: pkgconfig(sm)
|
||||||
BuildRequires: pkgconfig(libwacom)
|
BuildRequires: pkgconfig(libwacom)
|
||||||
|
2
sources
2
sources
@ -1 +1 @@
|
|||||||
SHA512 (mutter-45.0.tar.xz) = bbf813a720a3d8545969d089790f6e4c19cd5c8eb3079ba028945e4728446c78aa7540061ea87e43151c2ef359ea986b0e7a45f56f0eb6a33ca2c3979955bb43
|
SHA512 (mutter-45.1.tar.xz) = 87a629e941eb231d05fc24dbb905acb12236c971a02dd000fdd6cc6e7686544b5796ce57d10b34e096dc956578a373d5a73e89760bcc6beb292e9a25ce2faaf4
|
||||||
|
Loading…
Reference in New Issue
Block a user