mirror of
https://github.com/brl/mutter.git
synced 2024-12-01 04:10:43 -05:00
92375c75f8
This is inspired by 98892391d7
where the usage of
`g_signal_handler_disconnect()` without resetting the corresponding
handler id later resulted in a bug. Using `g_clear_signal_handler()`
makes sure we avoid similar bugs and is almost always the better
alternative. We use it for new code, let's clean up the old code to
also use it.
A further benefit is that it can get called even if the passed id is
0, allowing us to remove a lot of now unnessecary checks, and the fact
that `g_clear_signal_handler()` checks for the right type size, forcing us
to clean up all places where we used `guint` instead of `gulong`.
No functional changes intended here and all changes should be trivial,
thus bundled in one big commit.
https://gitlab.gnome.org/GNOME/mutter/merge_requests/940
617 lines
16 KiB
C
617 lines
16 KiB
C
/*
|
|
* Copyright (C) 2018 Red Hat
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/native/meta-kms-private.h"
|
|
|
|
#include "backends/native/meta-backend-native.h"
|
|
#include "backends/native/meta-kms-device-private.h"
|
|
#include "backends/native/meta-kms-impl.h"
|
|
#include "backends/native/meta-kms-impl-simple.h"
|
|
#include "backends/native/meta-kms-update-private.h"
|
|
#include "backends/native/meta-udev.h"
|
|
#include "cogl/cogl-trace.h"
|
|
|
|
/**
|
|
* SECTION:kms
|
|
* @short description: KMS abstraction
|
|
* @title: KMS abstraction
|
|
*
|
|
* The KMS abstraction consists of various building blocks for helping out with
|
|
* interacting with the various drm API's, enabling users to use a
|
|
* transactional API, aiming to hide all interaction with the underlying APIs.
|
|
*
|
|
* The subsystem defines two separate contexts, the "main" context, and the
|
|
* "impl" context. The main context is the context of which mutter as a whole
|
|
* runs in. It uses the main GLib main loop and main context and always runs in
|
|
* the main thread.
|
|
*
|
|
* The impl context is where all underlying API is being executed. While in the
|
|
* current state, it always runs in the main thread, the aim is to be able to
|
|
* execute the impl context in a dedicated thread.
|
|
*
|
|
* The public facing MetaKms API is always assumed to be executed from the main
|
|
* context.
|
|
*
|
|
* The KMS abstraction consists of the following public components:
|
|
*
|
|
* #MetaKms:
|
|
*
|
|
* Main entry point; used by the native backend to create devices, post updates
|
|
* etc.
|
|
*
|
|
* #MetaKmsDevice:
|
|
*
|
|
* A device (usually /dev/dri/cardN, where N being a number). Used to get KMS
|
|
* objects, such as connectors, CRTCs, planes, as well as basic meta data such
|
|
* as device path etc.
|
|
*
|
|
* #MetaKmsCrtc:
|
|
*
|
|
* Represents a CRTC. It manages a representation of the current CRTC state,
|
|
* including current mode, coordinates, possible clones.
|
|
*
|
|
* #MetaKmsConnector:
|
|
*
|
|
* Represents a connector, e.g. a display port connection. It also manages a
|
|
* representation of the current state, including meta data such as physical
|
|
* dimension of the connected, available modes, EDID, tile info etc. It also
|
|
* contains helper functions for configuration, as well as methods for adding
|
|
* configuration to a transaction (See #MetaKmsUpdate).
|
|
*
|
|
* #MetaKmsPlane:
|
|
*
|
|
* Represents a hardware plane. A plane is used to define the content of what
|
|
* should be presented on a CRTC. Planes can either be primary planes, used as
|
|
* a backdrop for CRTCs, overlay planes, and cursor planes.
|
|
*
|
|
* #MetaKmsUpdate:
|
|
*
|
|
* A KMS transaction object, meant to be processed potentially atomically when
|
|
* posted. An update consists of plane assignments, mode sets and KMS object
|
|
* property entries. The user adds updates to the object, and then posts it via
|
|
* MetaKms. It will then be processed by the MetaKms backend (See
|
|
* #MetaKmsImpl), potentially atomically.
|
|
*
|
|
*
|
|
* There are also these private objects, without public facing API:
|
|
*
|
|
* #MetaKmsImpl:
|
|
*
|
|
* The KMS backend implementation, running in the impl context. #MetaKmsImpl
|
|
* itself is an abstract object, with potentially multiple implementations.
|
|
* Currently only #MetaKmsImplSimple exists.
|
|
*
|
|
* #MetaKmsImplSimple:
|
|
*
|
|
* A KMS backend implementation using the non-atomic drmMode* API. While it's
|
|
* interacted with using the transactional API, the #MetaKmsUpdate is processed
|
|
* non-atomically.
|
|
*
|
|
* #MetaKmsImplDevice:
|
|
*
|
|
* An object linked to a #MetaKmsDevice, but where it is executed in the impl
|
|
* context. It takes care of the updating of the various KMS object (CRTC,
|
|
* connector, ..) states.
|
|
*
|
|
* #MetaKmsPageFlip:
|
|
*
|
|
* A object representing a page flip. It's created when a page flip is queued,
|
|
* and contains information necessary to provide feedback to the one requesting
|
|
* the page flip.
|
|
*
|
|
*/
|
|
|
|
enum
|
|
{
|
|
RESOURCES_CHANGED,
|
|
|
|
N_SIGNALS
|
|
};
|
|
|
|
static int signals[N_SIGNALS];
|
|
|
|
typedef struct _MetaKmsCallbackData
|
|
{
|
|
MetaKmsCallback callback;
|
|
gpointer user_data;
|
|
GDestroyNotify user_data_destroy;
|
|
} MetaKmsCallbackData;
|
|
|
|
typedef struct _MetaKmsSimpleImplSource
|
|
{
|
|
GSource source;
|
|
MetaKms *kms;
|
|
} MetaKmsSimpleImplSource;
|
|
|
|
typedef struct _MetaKmsFdImplSource
|
|
{
|
|
GSource source;
|
|
|
|
gpointer fd_tag;
|
|
MetaKms *kms;
|
|
|
|
MetaKmsImplTaskFunc dispatch;
|
|
gpointer user_data;
|
|
} MetaKmsFdImplSource;
|
|
|
|
struct _MetaKms
|
|
{
|
|
GObject parent;
|
|
|
|
MetaBackend *backend;
|
|
|
|
gulong hotplug_handler_id;
|
|
gulong removed_handler_id;
|
|
|
|
MetaKmsImpl *impl;
|
|
gboolean in_impl_task;
|
|
gboolean waiting_for_impl_task;
|
|
|
|
GList *devices;
|
|
|
|
MetaKmsUpdate *pending_update;
|
|
|
|
GList *pending_callbacks;
|
|
guint callback_source_id;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT)
|
|
|
|
MetaKmsUpdate *
|
|
meta_kms_ensure_pending_update (MetaKms *kms)
|
|
{
|
|
if (!kms->pending_update)
|
|
kms->pending_update = meta_kms_update_new ();
|
|
|
|
return meta_kms_get_pending_update (kms);
|
|
}
|
|
|
|
MetaKmsUpdate *
|
|
meta_kms_get_pending_update (MetaKms *kms)
|
|
{
|
|
return kms->pending_update;
|
|
}
|
|
|
|
static void
|
|
meta_kms_predict_states_in_impl (MetaKms *kms,
|
|
MetaKmsUpdate *update)
|
|
{
|
|
meta_assert_in_kms_impl (kms);
|
|
|
|
g_list_foreach (kms->devices,
|
|
(GFunc) meta_kms_device_predict_states_in_impl,
|
|
update);
|
|
}
|
|
|
|
static gboolean
|
|
meta_kms_update_process_in_impl (MetaKmsImpl *impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
g_autoptr (MetaKmsUpdate) update = user_data;
|
|
gboolean ret;
|
|
|
|
ret = meta_kms_impl_process_update (impl, update, error);
|
|
|
|
meta_kms_predict_states_in_impl (meta_kms_impl_get_kms (impl), update);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
meta_kms_post_update_sync (MetaKms *kms,
|
|
MetaKmsUpdate *update,
|
|
GError **error)
|
|
{
|
|
meta_kms_update_seal (update);
|
|
|
|
COGL_TRACE_BEGIN_SCOPED (MetaKmsPostUpdateSync,
|
|
"KMS (post update)");
|
|
|
|
return meta_kms_run_impl_task_sync (kms,
|
|
meta_kms_update_process_in_impl,
|
|
update,
|
|
error);
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_post_pending_update_sync (MetaKms *kms,
|
|
GError **error)
|
|
{
|
|
return meta_kms_post_update_sync (kms,
|
|
g_steal_pointer (&kms->pending_update),
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
meta_kms_discard_pending_page_flips_in_impl (MetaKmsImpl *impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
meta_kms_impl_discard_pending_page_flips (impl);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
meta_kms_discard_pending_page_flips (MetaKms *kms)
|
|
{
|
|
meta_kms_run_impl_task_sync (kms,
|
|
meta_kms_discard_pending_page_flips_in_impl,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
meta_kms_callback_data_free (MetaKmsCallbackData *callback_data)
|
|
{
|
|
if (callback_data->user_data_destroy)
|
|
callback_data->user_data_destroy (callback_data->user_data);
|
|
g_slice_free (MetaKmsCallbackData, callback_data);
|
|
}
|
|
|
|
static int
|
|
flush_callbacks (MetaKms *kms)
|
|
{
|
|
GList *l;
|
|
int callback_count = 0;
|
|
|
|
for (l = kms->pending_callbacks; l; l = l->next)
|
|
{
|
|
MetaKmsCallbackData *callback_data = l->data;
|
|
|
|
callback_data->callback (kms, callback_data->user_data);
|
|
meta_kms_callback_data_free (callback_data);
|
|
callback_count++;
|
|
}
|
|
|
|
g_list_free (kms->pending_callbacks);
|
|
kms->pending_callbacks = NULL;
|
|
|
|
return callback_count;
|
|
}
|
|
|
|
static gboolean
|
|
callback_idle (gpointer user_data)
|
|
{
|
|
MetaKms *kms = user_data;
|
|
|
|
flush_callbacks (kms);
|
|
|
|
kms->callback_source_id = 0;
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
void
|
|
meta_kms_queue_callback (MetaKms *kms,
|
|
MetaKmsCallback callback,
|
|
gpointer user_data,
|
|
GDestroyNotify user_data_destroy)
|
|
{
|
|
MetaKmsCallbackData *callback_data;
|
|
|
|
callback_data = g_slice_new0 (MetaKmsCallbackData);
|
|
*callback_data = (MetaKmsCallbackData) {
|
|
.callback = callback,
|
|
.user_data = user_data,
|
|
.user_data_destroy = user_data_destroy,
|
|
};
|
|
kms->pending_callbacks = g_list_append (kms->pending_callbacks,
|
|
callback_data);
|
|
if (!kms->callback_source_id)
|
|
kms->callback_source_id = g_idle_add (callback_idle, kms);
|
|
}
|
|
|
|
int
|
|
meta_kms_flush_callbacks (MetaKms *kms)
|
|
{
|
|
int callback_count;
|
|
|
|
callback_count = flush_callbacks (kms);
|
|
g_clear_handle_id (&kms->callback_source_id, g_source_remove);
|
|
|
|
return callback_count;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_run_impl_task_sync (MetaKms *kms,
|
|
MetaKmsImplTaskFunc func,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
gboolean ret;
|
|
|
|
kms->in_impl_task = TRUE;
|
|
kms->waiting_for_impl_task = TRUE;
|
|
ret = func (kms->impl, user_data, error);
|
|
kms->waiting_for_impl_task = FALSE;
|
|
kms->in_impl_task = FALSE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
simple_impl_source_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
MetaKmsSimpleImplSource *simple_impl_source =
|
|
(MetaKmsSimpleImplSource *) source;
|
|
MetaKms *kms = simple_impl_source->kms;
|
|
gboolean ret;
|
|
|
|
kms->in_impl_task = TRUE;
|
|
ret = callback (user_data);
|
|
kms->in_impl_task = FALSE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GSourceFuncs simple_impl_source_funcs = {
|
|
.dispatch = simple_impl_source_dispatch,
|
|
};
|
|
|
|
GSource *
|
|
meta_kms_add_source_in_impl (MetaKms *kms,
|
|
GSourceFunc func,
|
|
gpointer user_data,
|
|
GDestroyNotify user_data_destroy)
|
|
{
|
|
GSource *source;
|
|
MetaKmsSimpleImplSource *simple_impl_source;
|
|
|
|
meta_assert_in_kms_impl (kms);
|
|
|
|
source = g_source_new (&simple_impl_source_funcs,
|
|
sizeof (MetaKmsSimpleImplSource));
|
|
simple_impl_source = (MetaKmsSimpleImplSource *) source;
|
|
simple_impl_source->kms = kms;
|
|
|
|
g_source_set_callback (source, func, user_data, user_data_destroy);
|
|
g_source_attach (source, g_main_context_get_thread_default ());
|
|
|
|
return source;
|
|
}
|
|
|
|
static gboolean
|
|
meta_kms_fd_impl_source_check (GSource *source)
|
|
{
|
|
MetaKmsFdImplSource *fd_impl_source = (MetaKmsFdImplSource *) source;
|
|
|
|
return g_source_query_unix_fd (source, fd_impl_source->fd_tag) & G_IO_IN;
|
|
}
|
|
|
|
static gboolean
|
|
meta_kms_fd_impl_source_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
MetaKmsFdImplSource *fd_impl_source = (MetaKmsFdImplSource *) source;
|
|
MetaKms *kms = fd_impl_source->kms;
|
|
gboolean ret;
|
|
GError *error = NULL;
|
|
|
|
kms->in_impl_task = TRUE;
|
|
ret = fd_impl_source->dispatch (kms->impl,
|
|
fd_impl_source->user_data,
|
|
&error);
|
|
kms->in_impl_task = FALSE;
|
|
|
|
if (!ret)
|
|
{
|
|
g_warning ("Failed to dispatch fd source: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static GSourceFuncs fd_impl_source_funcs = {
|
|
NULL,
|
|
meta_kms_fd_impl_source_check,
|
|
meta_kms_fd_impl_source_dispatch
|
|
};
|
|
|
|
GSource *
|
|
meta_kms_register_fd_in_impl (MetaKms *kms,
|
|
int fd,
|
|
MetaKmsImplTaskFunc dispatch,
|
|
gpointer user_data)
|
|
{
|
|
GSource *source;
|
|
MetaKmsFdImplSource *fd_impl_source;
|
|
|
|
meta_assert_in_kms_impl (kms);
|
|
|
|
source = g_source_new (&fd_impl_source_funcs, sizeof (MetaKmsFdImplSource));
|
|
fd_impl_source = (MetaKmsFdImplSource *) source;
|
|
fd_impl_source->dispatch = dispatch;
|
|
fd_impl_source->user_data = user_data;
|
|
fd_impl_source->kms = kms;
|
|
fd_impl_source->fd_tag = g_source_add_unix_fd (source, fd,
|
|
G_IO_IN | G_IO_ERR);
|
|
|
|
g_source_attach (source, g_main_context_get_thread_default ());
|
|
|
|
return source;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_in_impl_task (MetaKms *kms)
|
|
{
|
|
return kms->in_impl_task;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_is_waiting_for_impl_task (MetaKms *kms)
|
|
{
|
|
return kms->waiting_for_impl_task;
|
|
}
|
|
|
|
static void
|
|
meta_kms_update_states_in_impl (MetaKms *kms)
|
|
{
|
|
COGL_TRACE_BEGIN_SCOPED (MetaKmsUpdateStates,
|
|
"KMS (update states)");
|
|
|
|
meta_assert_in_kms_impl (kms);
|
|
|
|
g_list_foreach (kms->devices,
|
|
(GFunc) meta_kms_device_update_states_in_impl,
|
|
NULL);
|
|
}
|
|
|
|
static gboolean
|
|
update_states_in_impl (MetaKmsImpl *impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKms *kms = meta_kms_impl_get_kms (impl);;
|
|
|
|
meta_kms_update_states_in_impl (kms);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
meta_kms_update_states_sync (MetaKms *kms,
|
|
GError **error)
|
|
{
|
|
return meta_kms_run_impl_task_sync (kms, update_states_in_impl, NULL, error);
|
|
}
|
|
|
|
static void
|
|
handle_hotplug_event (MetaKms *kms)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!meta_kms_update_states_sync (kms, &error))
|
|
g_warning ("Updating KMS state failed: %s", error->message);
|
|
|
|
g_signal_emit (kms, signals[RESOURCES_CHANGED], 0);
|
|
}
|
|
|
|
static void
|
|
on_udev_hotplug (MetaUdev *udev,
|
|
MetaKms *kms)
|
|
{
|
|
handle_hotplug_event (kms);
|
|
}
|
|
|
|
static void
|
|
on_udev_device_removed (MetaUdev *udev,
|
|
GUdevDevice *device,
|
|
MetaKms *kms)
|
|
{
|
|
handle_hotplug_event (kms);
|
|
}
|
|
|
|
MetaBackend *
|
|
meta_kms_get_backend (MetaKms *kms)
|
|
{
|
|
return kms->backend;
|
|
}
|
|
|
|
MetaKmsDevice *
|
|
meta_kms_create_device (MetaKms *kms,
|
|
const char *path,
|
|
MetaKmsDeviceFlag flags,
|
|
GError **error)
|
|
{
|
|
MetaKmsDevice *device;
|
|
|
|
device = meta_kms_device_new (kms, path, flags, error);
|
|
if (!device)
|
|
return NULL;
|
|
|
|
kms->devices = g_list_append (kms->devices, device);
|
|
|
|
return device;
|
|
}
|
|
|
|
MetaKms *
|
|
meta_kms_new (MetaBackend *backend,
|
|
GError **error)
|
|
{
|
|
MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
|
|
MetaUdev *udev = meta_backend_native_get_udev (backend_native);
|
|
MetaKms *kms;
|
|
|
|
kms = g_object_new (META_TYPE_KMS, NULL);
|
|
kms->backend = backend;
|
|
kms->impl = META_KMS_IMPL (meta_kms_impl_simple_new (kms, error));
|
|
if (!kms->impl)
|
|
{
|
|
g_object_unref (kms);
|
|
return NULL;
|
|
}
|
|
|
|
kms->hotplug_handler_id =
|
|
g_signal_connect (udev, "hotplug", G_CALLBACK (on_udev_hotplug), kms);
|
|
kms->removed_handler_id =
|
|
g_signal_connect (udev, "device-removed",
|
|
G_CALLBACK (on_udev_device_removed), kms);
|
|
|
|
return kms;
|
|
}
|
|
|
|
static void
|
|
meta_kms_finalize (GObject *object)
|
|
{
|
|
MetaKms *kms = META_KMS (object);
|
|
MetaBackendNative *backend_native = META_BACKEND_NATIVE (kms->backend);
|
|
MetaUdev *udev = meta_backend_native_get_udev (backend_native);
|
|
GList *l;
|
|
|
|
for (l = kms->pending_callbacks; l; l = l->next)
|
|
meta_kms_callback_data_free (l->data);
|
|
g_list_free (kms->pending_callbacks);
|
|
|
|
g_clear_handle_id (&kms->callback_source_id, g_source_remove);
|
|
|
|
g_list_free_full (kms->devices, g_object_unref);
|
|
|
|
g_clear_signal_handler (&kms->hotplug_handler_id, udev);
|
|
g_clear_signal_handler (&kms->removed_handler_id, udev);
|
|
|
|
G_OBJECT_CLASS (meta_kms_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_kms_init (MetaKms *kms)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_kms_class_init (MetaKmsClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_kms_finalize;
|
|
|
|
signals[RESOURCES_CHANGED] =
|
|
g_signal_new ("resources-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|