mirror of
https://github.com/brl/mutter.git
synced 2025-05-12 01:54:56 +00:00

This new manager object intends to take over management of the cursor plane from the native cursor renderer. It's API is intended to be used from the main thread, except for the _in_input() function, but mainly operates in the KMS context, i.e. the KMS thread. It makes use of an "update filter" that is called before each MetaKmsUpdate is turned into a atomic KMS commit or a set of legacy drmMode*() API calls. When the cursor position has been invalidated, it'll assign the cursor plane in the filter callback, using an as up to date as possible pointer position as the source for the cursor plane position. Cursor updates from the input thread schedules updates for the affected CRTCs which will cause the filter to be run, potentially for cursor-only commits. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
572 lines
16 KiB
C
572 lines
16 KiB
C
/*
|
|
* Copyright (C) 2018 Red Hat
|
|
* Copyright 2020 DisplayLink (UK) Ltd.
|
|
*
|
|
* 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-cursor-manager.h"
|
|
#include "backends/native/meta-kms-device-private.h"
|
|
#include "backends/native/meta-kms-impl.h"
|
|
#include "backends/native/meta-kms-update-private.h"
|
|
#include "backends/native/meta-thread-private.h"
|
|
#include "backends/native/meta-udev.h"
|
|
#include "cogl/cogl.h"
|
|
|
|
#include "meta-private-enum-types.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.
|
|
*
|
|
* #MetaKmsMode:
|
|
*
|
|
* Represents a mode a CRTC and connector can be configured with.
|
|
* Represents both modes directly derived from the devices, as well as
|
|
* fall back modes when the CRTC supports scaling.
|
|
*
|
|
* #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. Each #MetaKmsUpdate deals with
|
|
* updating a single device.
|
|
*
|
|
*
|
|
* There are also these private objects, without public facing API:
|
|
*
|
|
* #MetaKmsImpl:
|
|
*
|
|
* The KMS impl context object, managing things in the impl context.
|
|
*
|
|
* #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.
|
|
*
|
|
* This is an abstract type, with currently #MetaKmsImplDeviceSimple,
|
|
* implementing mode setting and page flipping using legacy DRM API.
|
|
*
|
|
* #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];
|
|
|
|
struct _MetaKms
|
|
{
|
|
MetaThread parent;
|
|
|
|
MetaKmsFlags flags;
|
|
|
|
gulong hotplug_handler_id;
|
|
gulong removed_handler_id;
|
|
|
|
MetaKmsImpl *impl;
|
|
gboolean in_impl_task;
|
|
gboolean waiting_for_impl_task;
|
|
|
|
GList *devices;
|
|
|
|
GList *pending_updates;
|
|
|
|
GList *pending_callbacks;
|
|
guint callback_source_id;
|
|
|
|
int kernel_thread_inhibit_count;
|
|
|
|
MetaKmsCursorManager *cursor_manager;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaKms, meta_kms, META_TYPE_THREAD)
|
|
|
|
G_DEFINE_QUARK (-meta-kms-error-quark, meta_kms_error)
|
|
|
|
static void
|
|
invoke_result_listener (MetaThread *thread,
|
|
gpointer user_data)
|
|
{
|
|
MetaKmsResultListener *listener = user_data;
|
|
|
|
meta_kms_result_listener_notify (listener);
|
|
}
|
|
|
|
void
|
|
meta_kms_queue_result_callback (MetaKms *kms,
|
|
MetaKmsResultListener *listener)
|
|
{
|
|
GMainContext *main_context =
|
|
meta_kms_result_listener_get_main_context (listener);
|
|
|
|
meta_kms_queue_callback (kms,
|
|
main_context,
|
|
invoke_result_listener,
|
|
listener,
|
|
(GDestroyNotify) meta_kms_result_listener_free);
|
|
}
|
|
|
|
static gpointer
|
|
meta_kms_discard_pending_page_flips_in_impl (MetaThreadImpl *thread_impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsImpl *impl = META_KMS_IMPL (thread_impl);
|
|
|
|
meta_kms_impl_discard_pending_page_flips (impl);
|
|
return GINT_TO_POINTER (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 gpointer
|
|
meta_kms_notify_modes_set_in_impl (MetaThreadImpl *thread_impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsImpl *impl = META_KMS_IMPL (thread_impl);
|
|
|
|
meta_kms_impl_notify_modes_set (impl);
|
|
return GINT_TO_POINTER (TRUE);
|
|
}
|
|
|
|
void
|
|
meta_kms_notify_modes_set (MetaKms *kms)
|
|
{
|
|
MetaThread *thread = META_THREAD (kms);
|
|
|
|
meta_thread_run_impl_task_sync (thread,
|
|
meta_kms_notify_modes_set_in_impl,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
meta_kms_queue_callback (MetaKms *kms,
|
|
GMainContext *main_context,
|
|
MetaThreadCallback callback,
|
|
gpointer user_data,
|
|
GDestroyNotify user_data_destroy)
|
|
{
|
|
MetaThread *thread = META_THREAD (kms);
|
|
|
|
meta_thread_queue_callback (thread,
|
|
main_context,
|
|
callback,
|
|
user_data,
|
|
user_data_destroy);
|
|
}
|
|
|
|
gpointer
|
|
meta_kms_run_impl_task_sync (MetaKms *kms,
|
|
MetaThreadTaskFunc func,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaThread *thread = META_THREAD (kms);
|
|
|
|
return meta_thread_run_impl_task_sync (thread, func, user_data, error);
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_in_impl_task (MetaKms *kms)
|
|
{
|
|
MetaThread *thread = META_THREAD (kms);
|
|
|
|
return meta_thread_is_in_impl_task (thread);
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_is_waiting_for_impl_task (MetaKms *kms)
|
|
{
|
|
MetaThread *thread = META_THREAD (kms);
|
|
|
|
return meta_thread_is_waiting_for_impl_task (thread);
|
|
}
|
|
|
|
typedef struct _UpdateStatesData
|
|
{
|
|
const char *device_path;
|
|
uint32_t crtc_id;
|
|
uint32_t connector_id;
|
|
} UpdateStatesData;
|
|
|
|
static MetaKmsResourceChanges
|
|
meta_kms_update_states_in_impl (MetaKms *kms,
|
|
UpdateStatesData *update_data)
|
|
{
|
|
MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE;
|
|
GList *l;
|
|
|
|
COGL_TRACE_BEGIN_SCOPED (MetaKmsUpdateStates,
|
|
"KMS (update states)");
|
|
|
|
meta_assert_in_kms_impl (kms);
|
|
|
|
if (!kms->devices)
|
|
return META_KMS_RESOURCE_CHANGE_NO_DEVICES;
|
|
|
|
for (l = kms->devices; l; l = l->next)
|
|
{
|
|
MetaKmsDevice *kms_device = META_KMS_DEVICE (l->data);
|
|
const char *kms_device_path = meta_kms_device_get_path (kms_device);
|
|
|
|
if (update_data->device_path &&
|
|
g_strcmp0 (kms_device_path, update_data->device_path) != 0)
|
|
continue;
|
|
|
|
if (update_data->crtc_id > 0 &&
|
|
!meta_kms_device_find_crtc_in_impl (kms_device, update_data->crtc_id))
|
|
continue;
|
|
|
|
if (update_data->connector_id > 0 &&
|
|
!meta_kms_device_find_connector_in_impl (kms_device,
|
|
update_data->connector_id))
|
|
continue;
|
|
|
|
changes |=
|
|
meta_kms_device_update_states_in_impl (kms_device,
|
|
update_data->crtc_id,
|
|
update_data->connector_id);
|
|
}
|
|
|
|
return changes;
|
|
}
|
|
|
|
static gpointer
|
|
update_states_in_impl (MetaThreadImpl *thread_impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
UpdateStatesData *data = user_data;
|
|
MetaKmsImpl *impl = META_KMS_IMPL (thread_impl);
|
|
MetaKms *kms = meta_kms_impl_get_kms (impl);
|
|
|
|
return GUINT_TO_POINTER (meta_kms_update_states_in_impl (kms, data));
|
|
}
|
|
|
|
MetaKmsResourceChanges
|
|
meta_kms_update_states_sync (MetaKms *kms,
|
|
GUdevDevice *udev_device)
|
|
{
|
|
UpdateStatesData data = {};
|
|
gpointer ret;
|
|
|
|
if (udev_device)
|
|
{
|
|
data.device_path = g_udev_device_get_device_file (udev_device);
|
|
data.crtc_id =
|
|
CLAMP (g_udev_device_get_property_as_int (udev_device, "CRTC"),
|
|
0, UINT32_MAX);
|
|
data.connector_id =
|
|
CLAMP (g_udev_device_get_property_as_int (udev_device, "CONNECTOR"),
|
|
0, UINT32_MAX);
|
|
}
|
|
|
|
ret = meta_kms_run_impl_task_sync (kms, update_states_in_impl, &data, NULL);
|
|
|
|
return GPOINTER_TO_UINT (ret);
|
|
}
|
|
|
|
static void
|
|
handle_hotplug_event (MetaKms *kms,
|
|
GUdevDevice *udev_device,
|
|
MetaKmsResourceChanges changes)
|
|
{
|
|
changes |= meta_kms_update_states_sync (kms, udev_device);
|
|
|
|
if (changes != META_KMS_RESOURCE_CHANGE_NONE)
|
|
meta_kms_emit_resources_changed (kms, changes);
|
|
}
|
|
|
|
void
|
|
meta_kms_resume (MetaKms *kms)
|
|
{
|
|
handle_hotplug_event (kms, NULL, META_KMS_RESOURCE_CHANGE_FULL);
|
|
}
|
|
|
|
static void
|
|
on_udev_hotplug (MetaUdev *udev,
|
|
GUdevDevice *udev_device,
|
|
MetaKms *kms)
|
|
{
|
|
handle_hotplug_event (kms, udev_device, META_KMS_RESOURCE_CHANGE_NONE);
|
|
}
|
|
|
|
static void
|
|
on_udev_device_removed (MetaUdev *udev,
|
|
GUdevDevice *device,
|
|
MetaKms *kms)
|
|
{
|
|
handle_hotplug_event (kms, NULL, META_KMS_RESOURCE_CHANGE_NONE);
|
|
}
|
|
|
|
MetaBackend *
|
|
meta_kms_get_backend (MetaKms *kms)
|
|
{
|
|
return meta_thread_get_backend (META_THREAD (kms));
|
|
}
|
|
|
|
GList *
|
|
meta_kms_get_devices (MetaKms *kms)
|
|
{
|
|
return kms->devices;
|
|
}
|
|
|
|
MetaKmsDevice *
|
|
meta_kms_create_device (MetaKms *kms,
|
|
const char *path,
|
|
MetaKmsDeviceFlag flags,
|
|
GError **error)
|
|
{
|
|
MetaKmsDevice *device;
|
|
|
|
if (kms->flags & META_KMS_FLAG_NO_MODE_SETTING)
|
|
flags |= META_KMS_DEVICE_FLAG_NO_MODE_SETTING;
|
|
|
|
device = meta_kms_device_new (kms, path, flags, error);
|
|
if (!device)
|
|
return NULL;
|
|
|
|
kms->devices = g_list_append (kms->devices, device);
|
|
|
|
return device;
|
|
}
|
|
|
|
static gpointer
|
|
prepare_shutdown_in_impl (MetaThreadImpl *thread_impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsImpl *impl = META_KMS_IMPL (thread_impl);
|
|
|
|
meta_kms_impl_prepare_shutdown (impl);
|
|
return GINT_TO_POINTER (TRUE);
|
|
}
|
|
|
|
static void
|
|
on_prepare_shutdown (MetaBackend *backend,
|
|
MetaKms *kms)
|
|
{
|
|
meta_kms_run_impl_task_sync (kms, prepare_shutdown_in_impl, NULL, NULL);
|
|
meta_thread_flush_callbacks (META_THREAD (kms));
|
|
|
|
g_clear_object (&kms->cursor_manager);
|
|
}
|
|
|
|
MetaKms *
|
|
meta_kms_new (MetaBackend *backend,
|
|
MetaKmsFlags flags,
|
|
GError **error)
|
|
{
|
|
MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
|
|
MetaUdev *udev = meta_backend_native_get_udev (backend_native);
|
|
MetaKms *kms;
|
|
const char *thread_type_string;
|
|
MetaThreadType thread_type = META_THREAD_TYPE_KERNEL;
|
|
|
|
thread_type_string = g_getenv ("MUTTER_DEBUG_KMS_THREAD_TYPE");
|
|
if (thread_type_string)
|
|
{
|
|
if (g_strcmp0 (thread_type_string, "user") == 0)
|
|
thread_type = META_THREAD_TYPE_USER;
|
|
else if (g_strcmp0 (thread_type_string, "kernel") == 0)
|
|
thread_type = META_THREAD_TYPE_KERNEL;
|
|
else
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
kms = g_initable_new (META_TYPE_KMS,
|
|
NULL, error,
|
|
"backend", backend,
|
|
"name", "KMS thread",
|
|
"thread-type", thread_type,
|
|
"wants-realtime", TRUE,
|
|
NULL);
|
|
kms->flags = flags;
|
|
|
|
if (!(flags & META_KMS_FLAG_NO_MODE_SETTING))
|
|
{
|
|
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);
|
|
|
|
g_signal_connect (backend, "prepare-shutdown",
|
|
G_CALLBACK (on_prepare_shutdown),
|
|
kms);
|
|
|
|
return kms;
|
|
}
|
|
|
|
static void
|
|
meta_kms_finalize (GObject *object)
|
|
{
|
|
MetaKms *kms = META_KMS (object);
|
|
MetaBackend *backend = meta_thread_get_backend (META_THREAD (kms));
|
|
MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
|
|
MetaUdev *udev = meta_backend_native_get_udev (backend_native);
|
|
|
|
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_constructed (GObject *object)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_kms_init (MetaKms *kms)
|
|
{
|
|
kms->cursor_manager = meta_kms_cursor_manager_new (kms);
|
|
}
|
|
|
|
static void
|
|
meta_kms_class_init (MetaKmsClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
MetaThreadClass *thread_class = META_THREAD_CLASS (klass);
|
|
|
|
object_class->finalize = meta_kms_finalize;
|
|
object_class->constructed = meta_kms_constructed;
|
|
|
|
signals[RESOURCES_CHANGED] =
|
|
g_signal_new ("resources-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1,
|
|
META_TYPE_KMS_RESOURCE_CHANGES);
|
|
|
|
meta_thread_class_register_impl_type (thread_class, META_TYPE_KMS_IMPL);
|
|
}
|
|
|
|
void
|
|
meta_kms_emit_resources_changed (MetaKms *kms,
|
|
MetaKmsResourceChanges changes)
|
|
{
|
|
g_signal_emit (kms, signals[RESOURCES_CHANGED], 0, changes);
|
|
}
|
|
|
|
void
|
|
meta_kms_inhibit_kernel_thread (MetaKms *kms)
|
|
{
|
|
kms->kernel_thread_inhibit_count++;
|
|
|
|
if (kms->kernel_thread_inhibit_count == 1)
|
|
meta_thread_reset_thread_type (META_THREAD (kms), META_THREAD_TYPE_USER);
|
|
}
|
|
|
|
void
|
|
meta_kms_uninhibit_kernel_thread (MetaKms *kms)
|
|
{
|
|
g_return_if_fail (kms->kernel_thread_inhibit_count > 0);
|
|
|
|
kms->kernel_thread_inhibit_count--;
|
|
|
|
if (kms->kernel_thread_inhibit_count == 0)
|
|
meta_thread_reset_thread_type (META_THREAD (kms), META_THREAD_TYPE_KERNEL);
|
|
}
|
|
|
|
MetaKmsCursorManager *
|
|
meta_kms_get_cursor_manager (MetaKms *kms)
|
|
{
|
|
return kms->cursor_manager;
|
|
}
|