From 6d873036e0dd37ea48155f1a2ddda755337e8870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 21 Dec 2022 18:54:17 +0100 Subject: [PATCH] Add KMS cursor manager 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: --- src/backends/native/meta-kms-cursor-manager.c | 853 ++++++++++++++++++ src/backends/native/meta-kms-cursor-manager.h | 62 ++ src/backends/native/meta-kms-types.h | 2 + src/backends/native/meta-kms.c | 12 + src/meson.build | 2 + 5 files changed, 931 insertions(+) create mode 100644 src/backends/native/meta-kms-cursor-manager.c create mode 100644 src/backends/native/meta-kms-cursor-manager.h diff --git a/src/backends/native/meta-kms-cursor-manager.c b/src/backends/native/meta-kms-cursor-manager.c new file mode 100644 index 000000000..fe838431e --- /dev/null +++ b/src/backends/native/meta-kms-cursor-manager.c @@ -0,0 +1,853 @@ +/* + * Copyright (C) 2021 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-cursor-manager.h" + +#include + +#include "backends/native/meta-kms-crtc.h" +#include "backends/native/meta-kms-impl.h" +#include "backends/native/meta-kms-device-private.h" +#include "backends/native/meta-kms-update-private.h" +#include "backends/native/meta-thread.h" + +typedef struct _MetaKmsCursorManagerImpl +{ + MetaKmsImpl *impl; + GPtrArray *crtc_states; + + MetaKmsCursorQueryInImpl cursor_query_in_impl_func; + gpointer cursor_query_in_impl_func_user_data; + + MetaKmsUpdateFilter *update_filter; +} MetaKmsCursorManagerImpl; + +typedef struct _CrtcStateImpl +{ + gatomicrefcount ref_count; + + MetaKmsCursorManagerImpl *cursor_manager_impl; + + MetaKmsCrtc *crtc; + graphene_rect_t layout; + float scale; + MetaDrmBuffer *buffer; + graphene_point_t hotspot; + + gboolean cursor_invalidated; + gboolean force_update; + gboolean has_cursor; + + graphene_point_t pending_hotspot; + MetaDrmBuffer *pending_buffer; + MetaDrmBuffer *active_buffer; + MetaDrmBuffer *presenting_buffer; +} CrtcStateImpl; + +struct _MetaKmsCursorManager +{ + GObject parent; + + MetaKms *kms; +}; + +G_DEFINE_TYPE (MetaKmsCursorManager, meta_kms_cursor_manager, + G_TYPE_OBJECT) + +static GQuark quark_cursor_manager_impl; + +static CrtcStateImpl * +find_crtc_state (MetaKmsCursorManagerImpl *cursor_manager_impl, + MetaKmsCrtc *crtc) +{ + int i; + + if (!cursor_manager_impl->crtc_states) + return NULL; + + for (i = 0; i < cursor_manager_impl->crtc_states->len; i++) + { + CrtcStateImpl *crtc_state_impl = + g_ptr_array_index (cursor_manager_impl->crtc_states, i); + + if (crtc_state_impl->crtc == crtc) + return crtc_state_impl; + } + + return NULL; +} + +static CrtcStateImpl * +crtc_state_impl_new (MetaKmsCursorManagerImpl *cursor_manager_impl, + MetaKmsCrtc *crtc, + graphene_rect_t layout, + float scale, + MetaDrmBuffer *buffer) +{ + CrtcStateImpl *crtc_state_impl; + + crtc_state_impl = g_new0 (CrtcStateImpl, 1); + g_atomic_ref_count_init (&crtc_state_impl->ref_count); + crtc_state_impl->cursor_manager_impl = cursor_manager_impl; + crtc_state_impl->crtc = crtc; + crtc_state_impl->layout = layout; + crtc_state_impl->scale = scale; + crtc_state_impl->buffer = buffer; + + return crtc_state_impl; +} + +static CrtcStateImpl * +crtc_state_impl_ref (CrtcStateImpl *crtc_state_impl) +{ + g_atomic_ref_count_inc (&crtc_state_impl->ref_count); + return crtc_state_impl; +} + +static void +crtc_state_impl_unref (CrtcStateImpl *crtc_state_impl) +{ + if (g_atomic_ref_count_dec (&crtc_state_impl->ref_count)) + { + g_warn_if_fail (!crtc_state_impl->buffer); + g_warn_if_fail (!crtc_state_impl->pending_buffer); + g_warn_if_fail (!crtc_state_impl->active_buffer); + g_warn_if_fail (!crtc_state_impl->presenting_buffer); + g_free (crtc_state_impl); + } +} + +static void +crtc_state_impl_swap_buffer (CrtcStateImpl *crtc_state_impl, + MetaDrmBuffer **from_buffer_ref, + MetaDrmBuffer **to_buffer_ref) +{ + MetaDrmBuffer *buffer_to_release; + + if (*from_buffer_ref == *to_buffer_ref) + { + buffer_to_release = g_steal_pointer (from_buffer_ref); + } + else + { + buffer_to_release = g_steal_pointer (to_buffer_ref); + *to_buffer_ref = g_steal_pointer (from_buffer_ref); + } + + if (buffer_to_release) + { + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc_state_impl->crtc); + MetaKms *kms = meta_kms_device_get_kms (device); + + meta_thread_queue_callback (META_THREAD (kms), + NULL, NULL, + buffer_to_release, + g_object_unref); + } +} + +static void +crtc_state_impl_notify_presented (CrtcStateImpl *crtc_state_impl) +{ + crtc_state_impl_swap_buffer (crtc_state_impl, + &crtc_state_impl->active_buffer, + &crtc_state_impl->presenting_buffer); +} + +static void +cursor_page_flip_feedback_flipped (MetaKmsCrtc *crtc, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + gpointer user_data) +{ + CrtcStateImpl *crtc_state_impl = user_data; + + crtc_state_impl_notify_presented (crtc_state_impl); +} + +static void +cursor_page_flip_feedback_ready (MetaKmsCrtc *crtc, + gpointer user_data) +{ +} + +static void +cursor_page_flip_feedback_mode_set_fallback (MetaKmsCrtc *crtc, + gpointer user_data) +{ + CrtcStateImpl *crtc_state_impl = user_data; + + crtc_state_impl_notify_presented (crtc_state_impl); +} + +static void +cursor_page_flip_feedback_discarded (MetaKmsCrtc *crtc, + gpointer user_data, + const GError *error) +{ +} + +static const MetaKmsPageFlipListenerVtable cursor_page_flip_listener_vtable = { + .flipped = cursor_page_flip_feedback_flipped, + .ready = cursor_page_flip_feedback_ready, + .mode_set_fallback = cursor_page_flip_feedback_mode_set_fallback, + .discarded = cursor_page_flip_feedback_discarded, +}; + +static void +cursor_result_feedback (const MetaKmsFeedback *feedback, + gpointer user_data) +{ + CrtcStateImpl *crtc_state_impl = user_data; + + switch (meta_kms_feedback_get_result (feedback)) + { + case META_KMS_FEEDBACK_PASSED: + break; + case META_KMS_FEEDBACK_FAILED: + return; + } + + crtc_state_impl->cursor_invalidated = FALSE; + + crtc_state_impl_swap_buffer (crtc_state_impl, + &crtc_state_impl->pending_buffer, + &crtc_state_impl->active_buffer); +} + +static const MetaKmsResultListenerVtable cursor_result_listener_vtable = { + .feedback = cursor_result_feedback, +}; + +static gboolean +get_current_cursor_position (MetaKmsCursorManagerImpl *cursor_manager_impl, + float *x, + float *y) +{ + gpointer user_data; + + if (!cursor_manager_impl->cursor_query_in_impl_func) + return FALSE; + + user_data = cursor_manager_impl->cursor_query_in_impl_func_user_data; + cursor_manager_impl->cursor_query_in_impl_func (x, y, user_data); + + return TRUE; +} + +static gboolean +calculate_cursor_rect (CrtcStateImpl *crtc_state_impl, + MetaDrmBuffer *buffer, + const graphene_point_t *hotspot, + float x, + float y, + graphene_rect_t *out_cursor_rect) +{ + float crtc_x, crtc_y; + int buffer_width, buffer_height; + graphene_rect_t cursor_rect; + + crtc_x = (x - crtc_state_impl->layout.origin.x) * crtc_state_impl->scale; + crtc_y = (y - crtc_state_impl->layout.origin.y) * crtc_state_impl->scale; + + buffer_width = meta_drm_buffer_get_width (buffer); + buffer_height = meta_drm_buffer_get_height (buffer); + + cursor_rect = (graphene_rect_t) { + .origin = { + .x = crtc_x - hotspot->x, + .y = crtc_y - hotspot->y, + }, + .size = { + .width = buffer_width, + .height = buffer_height, + }, + }; + if (cursor_rect.origin.x + cursor_rect.size.width > 0.0 && + cursor_rect.origin.y + cursor_rect.size.height > 0.0 && + cursor_rect.origin.x < (crtc_state_impl->layout.size.width * + crtc_state_impl->scale) && + cursor_rect.origin.y < (crtc_state_impl->layout.size.height * + crtc_state_impl->scale)) + { + if (out_cursor_rect) + *out_cursor_rect = cursor_rect; + return TRUE; + } + else + { + return FALSE; + } +} + +static MetaKmsUpdate * +maybe_update_cursor_plane (MetaKmsCursorManagerImpl *cursor_manager_impl, + MetaKmsCrtc *crtc, + MetaKmsUpdate *update, + MetaDrmBuffer **old_buffer) +{ + MetaKmsImpl *impl = cursor_manager_impl->impl; + MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); + MetaKmsDevice *device; + CrtcStateImpl *crtc_state_impl; + float x, y; + MetaDrmBuffer *buffer; + const graphene_point_t *hotspot; + gboolean should_have_cursor; + gboolean did_have_cursor; + graphene_rect_t cursor_rect; + MetaKmsPlane *cursor_plane; + + g_assert (old_buffer && !*old_buffer); + + if (!get_current_cursor_position (cursor_manager_impl, &x, &y)) + return update; + + crtc_state_impl = find_crtc_state (cursor_manager_impl, crtc); + g_return_val_if_fail (crtc_state_impl, update); + + if (!crtc_state_impl->cursor_invalidated) + return update; + + device = meta_kms_crtc_get_device (crtc_state_impl->crtc); + buffer = crtc_state_impl->buffer; + hotspot = &crtc_state_impl->hotspot; + + if (buffer) + { + should_have_cursor = calculate_cursor_rect (crtc_state_impl, + buffer, + hotspot, + x, y, + &cursor_rect); + } + else + { + should_have_cursor = FALSE; + } + + did_have_cursor = crtc_state_impl->has_cursor; + crtc_state_impl->has_cursor = should_have_cursor; + + if (!should_have_cursor && !did_have_cursor) + return update; + + if (!update) + { + MetaKmsImplDevice *impl_device = + meta_kms_device_get_impl_device (device); + + update = meta_kms_update_new (device); + meta_kms_update_realize (update, impl_device); + } + + cursor_plane = meta_kms_device_get_cursor_plane_for (device, crtc); + + if (should_have_cursor) + { + int width, height; + MetaFixed16Rectangle src_rect; + MetaRectangle dst_rect; + MetaKmsAssignPlaneFlag assign_plane_flags = + META_KMS_ASSIGN_PLANE_FLAG_NONE; + + if (crtc_state_impl->pending_buffer != crtc_state_impl->buffer) + { + assign_plane_flags |= META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED; + *old_buffer = g_steal_pointer (&crtc_state_impl->pending_buffer); + crtc_state_impl->pending_buffer = g_object_ref (buffer); + } + + width = meta_drm_buffer_get_width (buffer); + height = meta_drm_buffer_get_height (buffer); + + src_rect = (MetaFixed16Rectangle) { + .x = meta_fixed_16_from_int (0), + .y = meta_fixed_16_from_int (0), + .width = meta_fixed_16_from_int (width), + .height = meta_fixed_16_from_int (height), + }; + dst_rect = (MetaRectangle) { + .x = round (cursor_rect.origin.x), + .y = round (cursor_rect.origin.y), + .width = round (cursor_rect.size.width), + .height = round (cursor_rect.size.height), + }; + + meta_kms_update_assign_plane (update, + crtc, cursor_plane, + buffer, + src_rect, dst_rect, + assign_plane_flags); + } + else + { + *old_buffer = g_steal_pointer (&crtc_state_impl->pending_buffer); + meta_kms_update_unassign_plane (update, crtc, cursor_plane); + } + + meta_kms_update_add_page_flip_listener (update, + crtc, + &cursor_page_flip_listener_vtable, + META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, + meta_thread_impl_get_main_context (thread_impl), + crtc_state_impl_ref (crtc_state_impl), + (GDestroyNotify) crtc_state_impl_unref); + meta_kms_update_add_result_listener (update, + &cursor_result_listener_vtable, + meta_thread_impl_get_main_context (thread_impl), + crtc_state_impl_ref (crtc_state_impl), + (GDestroyNotify) crtc_state_impl_unref); + + return update; +} + +static void +free_old_buffers (gpointer retval, + const GError *error, + gpointer user_data) +{ + GList *old_buffers = retval; + + g_list_free_full (old_buffers, g_object_unref); +} + +static MetaKmsUpdate * +update_filter_cb (MetaKmsImpl *impl, + MetaKmsCrtc *crtc, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags, + gpointer user_data) +{ + MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); + MetaKmsCursorManagerImpl *cursor_manager_impl = user_data; + + if (flags & META_KMS_UPDATE_FLAG_TEST_ONLY) + return update; + + if (flags & META_KMS_UPDATE_FLAG_MODE_SET) + { + GPtrArray *crtc_states = cursor_manager_impl->crtc_states; + GList *old_buffers = NULL; + int i; + + g_return_val_if_fail (crtc_states, update); + + for (i = 0; i < crtc_states->len; i++) + { + CrtcStateImpl *crtc_state_impl = g_ptr_array_index (crtc_states, i); + MetaKmsCrtc *crtc = crtc_state_impl->crtc; + MetaDrmBuffer *old_buffer = NULL; + + update = maybe_update_cursor_plane (cursor_manager_impl, + crtc, update, &old_buffer); + if (old_buffer) + old_buffers = g_list_prepend (old_buffers, old_buffer); + } + + if (old_buffers) + { + meta_thread_queue_callback (meta_thread_impl_get_thread (thread_impl), + g_main_context_default (), + NULL, + old_buffers, + (GDestroyNotify) free_old_buffers); + } + } + else + { + MetaDrmBuffer *old_buffer = NULL; + + update = maybe_update_cursor_plane (cursor_manager_impl, + crtc, update, &old_buffer); + + if (old_buffer) + { + meta_thread_queue_callback (meta_thread_impl_get_thread (thread_impl), + g_main_context_default (), + NULL, + old_buffer, g_object_unref); + } + } + + return update; +} + +static void +meta_kms_cursor_manager_impl_free (MetaKmsCursorManagerImpl *cursor_manager_impl) +{ + g_warn_if_fail (!cursor_manager_impl->crtc_states); +} + +static MetaKmsCursorManagerImpl * +ensure_cursor_manager_impl (MetaKmsImpl *impl) +{ + MetaKmsCursorManagerImpl *cursor_manager_impl; + + cursor_manager_impl = g_object_get_qdata (G_OBJECT (impl), + quark_cursor_manager_impl); + if (!cursor_manager_impl) + { + cursor_manager_impl = g_new0 (MetaKmsCursorManagerImpl, 1); + cursor_manager_impl->impl = impl; + + g_object_set_qdata (G_OBJECT (impl), + quark_cursor_manager_impl, + cursor_manager_impl); + + cursor_manager_impl->update_filter = + meta_kms_impl_add_update_filter (impl, update_filter_cb, + cursor_manager_impl); + } + return cursor_manager_impl; +} + +static gpointer +finalize_in_impl (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + MetaKmsImpl *impl = META_KMS_IMPL (thread_impl); + MetaKmsCursorManagerImpl *cursor_manager_impl; + + cursor_manager_impl = g_object_steal_qdata (G_OBJECT (impl), + quark_cursor_manager_impl); + if (cursor_manager_impl) + { + GPtrArray *crtc_states; + + meta_kms_impl_remove_update_filter (impl, + cursor_manager_impl->update_filter); + crtc_states = g_steal_pointer (&cursor_manager_impl->crtc_states); + meta_kms_cursor_manager_impl_free (cursor_manager_impl); + return crtc_states; + } + else + { + return NULL; + } +} + +static void +meta_kms_cursor_manager_finalize (GObject *object) +{ + MetaKmsCursorManager *cursor_manager = META_KMS_CURSOR_MANAGER (object); + GPtrArray *crtc_states; + + crtc_states = + meta_thread_run_impl_task_sync (META_THREAD (cursor_manager->kms), + finalize_in_impl, NULL, NULL); + g_clear_pointer (&crtc_states, g_ptr_array_unref); + + G_OBJECT_CLASS (meta_kms_cursor_manager_parent_class)->finalize (object); +} + +static void +meta_kms_cursor_manager_class_init (MetaKmsCursorManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_kms_cursor_manager_finalize; + + quark_cursor_manager_impl = + g_quark_from_static_string ("-meta-kms-cursor-manager-quark"); +} + +static void +meta_kms_cursor_manager_init (MetaKmsCursorManager *cursor_manager) +{ +} + +MetaKmsCursorManager * +meta_kms_cursor_manager_new (MetaKms *kms) +{ + MetaKmsCursorManager *cursor_manager; + + cursor_manager = g_object_new (META_TYPE_KMS_CURSOR_MANAGER, NULL); + cursor_manager->kms = kms; + + return cursor_manager; +} + +typedef struct +{ + MetaKmsCursorQueryInImpl func; + gpointer user_data; +} SetQueryFuncData; + +static gpointer +set_query_func_in_impl (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + SetQueryFuncData *data = user_data; + MetaKmsCursorManagerImpl *cursor_manager_impl = + ensure_cursor_manager_impl (META_KMS_IMPL (thread_impl)); + + cursor_manager_impl->cursor_query_in_impl_func = data->func; + cursor_manager_impl->cursor_query_in_impl_func_user_data = data->user_data; + + return NULL; +} + +void +meta_kms_cursor_manager_set_query_func (MetaKmsCursorManager *cursor_manager, + MetaKmsCursorQueryInImpl func, + gpointer user_data) +{ + SetQueryFuncData *data; + + data = g_new0 (SetQueryFuncData, 1); + data->func = func; + data->user_data = user_data; + + meta_thread_post_impl_task (META_THREAD (cursor_manager->kms), + set_query_func_in_impl, + data, g_free, + NULL, NULL); +} + +static gpointer +position_changed_in_impl (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + MetaKmsCursorManagerImpl *cursor_manager_impl = + ensure_cursor_manager_impl (META_KMS_IMPL (thread_impl)); + const graphene_point_t *position = user_data; + GPtrArray *crtc_states; + int i; + + crtc_states = cursor_manager_impl->crtc_states; + g_return_val_if_fail (crtc_states, NULL); + + for (i = 0; i < crtc_states->len; i++) + { + CrtcStateImpl *crtc_state_impl = g_ptr_array_index (crtc_states, i); + MetaDrmBuffer *buffer; + const graphene_point_t *hotspot; + gboolean did_have_cursor; + gboolean should_have_cursor; + + buffer = crtc_state_impl->buffer; + hotspot = &crtc_state_impl->hotspot; + + if (buffer) + { + should_have_cursor = calculate_cursor_rect (crtc_state_impl, + buffer, + hotspot, + position->x, + position->y, + NULL); + } + else + { + should_have_cursor = FALSE; + } + + did_have_cursor = crtc_state_impl->has_cursor; + + if (did_have_cursor != should_have_cursor || + should_have_cursor) + { + MetaKmsCrtc *crtc = crtc_state_impl->crtc; + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); + MetaKmsImplDevice *impl_device = + meta_kms_device_get_impl_device (device); + + crtc_state_impl->cursor_invalidated = TRUE; + + meta_kms_impl_device_schedule_process (impl_device, + crtc_state_impl->crtc); + } + } + + return NULL; +} + +static graphene_point_t * +copy_point (const graphene_point_t *point) +{ + return graphene_point_init_from_point (graphene_point_alloc (), point); +} + +void +meta_kms_cursor_manager_position_changed_in_input_impl (MetaKmsCursorManager *cursor_manager, + const graphene_point_t *position) +{ + meta_thread_post_impl_task (META_THREAD (cursor_manager->kms), + position_changed_in_impl, + copy_point (position), + (GDestroyNotify) graphene_point_free, + NULL, NULL); +} + +typedef struct +{ + MetaKmsCrtc *crtc; + MetaDrmBuffer *buffer; + graphene_point_t hotspot; +} UpdateSpriteData; + +static gpointer +update_sprite_in_impl (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + MetaKmsCursorManagerImpl *cursor_manager_impl = + ensure_cursor_manager_impl (META_KMS_IMPL (thread_impl)); + UpdateSpriteData *data = user_data; + MetaKmsCrtc *crtc = data->crtc; + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); + MetaKmsImplDevice *impl_device = + meta_kms_device_get_impl_device (device); + CrtcStateImpl *crtc_state_impl; + MetaDrmBuffer *old_buffer; + + crtc_state_impl = find_crtc_state (cursor_manager_impl, crtc); + g_return_val_if_fail (crtc_state_impl, NULL); + + old_buffer = g_steal_pointer (&crtc_state_impl->buffer); + crtc_state_impl->buffer = g_steal_pointer (&data->buffer); + crtc_state_impl->hotspot = data->hotspot; + crtc_state_impl->cursor_invalidated = TRUE; + + meta_kms_impl_device_schedule_process (impl_device, + crtc_state_impl->crtc); + + if (old_buffer) + { + MetaThread *thread = meta_thread_impl_get_thread (thread_impl); + + meta_thread_queue_callback (thread, + NULL, NULL, + old_buffer, g_object_unref); + } + + return NULL; +} + +void +meta_kms_cursor_manager_update_sprite (MetaKmsCursorManager *cursor_manager, + MetaKmsCrtc *crtc, + MetaDrmBuffer *buffer, + const graphene_point_t *hotspot) +{ + UpdateSpriteData *data; + + data = g_new0 (UpdateSpriteData, 1); + data->crtc = crtc; + data->buffer = buffer ? g_object_ref (buffer) : NULL; + if (hotspot) + data->hotspot = *hotspot; + + meta_thread_post_impl_task (META_THREAD (cursor_manager->kms), + update_sprite_in_impl, + data, g_free, + NULL, NULL); +} + +static void +crtc_state_impl_clear_in_main (CrtcStateImpl *crtc_state_impl) +{ + g_clear_object (&crtc_state_impl->buffer); + g_clear_object (&crtc_state_impl->pending_buffer); + g_clear_object (&crtc_state_impl->active_buffer); + g_clear_object (&crtc_state_impl->presenting_buffer); + crtc_state_impl_unref (crtc_state_impl); +} + +static void +clear_crtc_states_in_impl (MetaThreadImpl *thread_impl, + GPtrArray *crtc_states) +{ + MetaThread *thread = meta_thread_impl_get_thread (thread_impl); + + meta_thread_queue_callback (thread, + NULL, NULL, + crtc_states, + (GDestroyNotify) g_ptr_array_unref); +} + +static gpointer +update_viewports_in_impl (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + MetaKmsCursorManagerImpl *cursor_manager_impl = + ensure_cursor_manager_impl (META_KMS_IMPL (thread_impl)); + GArray *layouts = user_data; + GPtrArray *crtc_states; + int i; + + crtc_states = + g_ptr_array_new_full (layouts->len, + (GDestroyNotify) crtc_state_impl_clear_in_main); + + for (i = 0; i < layouts->len; i++) + { + MetaKmsCrtcLayout *crtc_layout = + &g_array_index (layouts, MetaKmsCrtcLayout, i); + CrtcStateImpl *crtc_state_impl; + CrtcStateImpl *old_crtc_state; + + old_crtc_state = find_crtc_state (cursor_manager_impl, crtc_layout->crtc); + if (old_crtc_state) + { + crtc_state_impl = + crtc_state_impl_new (cursor_manager_impl, + crtc_layout->crtc, + crtc_layout->layout, + crtc_layout->scale, + g_steal_pointer (&old_crtc_state->buffer)); + } + else + { + crtc_state_impl = + crtc_state_impl_new (cursor_manager_impl, + crtc_layout->crtc, + crtc_layout->layout, + crtc_layout->scale, + NULL); + } + + crtc_state_impl->cursor_invalidated = TRUE; + g_ptr_array_add (crtc_states, crtc_state_impl); + } + + if (cursor_manager_impl->crtc_states) + clear_crtc_states_in_impl (thread_impl, cursor_manager_impl->crtc_states); + cursor_manager_impl->crtc_states = crtc_states; + return NULL; +} + +void +meta_kms_cursor_manager_update_crtc_layout (MetaKmsCursorManager *cursor_manager, + GArray *layouts) +{ + meta_thread_post_impl_task (META_THREAD (cursor_manager->kms), + update_viewports_in_impl, + g_array_ref (layouts), + (GDestroyNotify) g_array_unref, + NULL, NULL); +} diff --git a/src/backends/native/meta-kms-cursor-manager.h b/src/backends/native/meta-kms-cursor-manager.h new file mode 100644 index 000000000..c966b1be6 --- /dev/null +++ b/src/backends/native/meta-kms-cursor-manager.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifndef META_KMS_CURSOR_MANAGER_H +#define META_KMS_CURSOR_MANAGER_H + +#include +#include + +#include "backends/native/meta-backend-native-types.h" +#include "backends/native/meta-kms-types.h" +#include "core/util-private.h" + +typedef struct _MetaKmsCrtcLayout +{ + MetaKmsCrtc *crtc; + graphene_rect_t layout; + float scale; +} MetaKmsCrtcLayout; + +typedef void (* MetaKmsCursorQueryInImpl) (float *x, + float *y, + gpointer user_data); + +#define META_TYPE_KMS_CURSOR_MANAGER (meta_kms_cursor_manager_get_type ()) +G_DECLARE_FINAL_TYPE (MetaKmsCursorManager, meta_kms_cursor_manager, + META, KMS_CURSOR_MANAGER, GObject) + +MetaKmsCursorManager * meta_kms_cursor_manager_new (MetaKms *kms); + +void meta_kms_cursor_manager_set_query_func (MetaKmsCursorManager *cursor_manager, + MetaKmsCursorQueryInImpl func, + gpointer user_data); + +void meta_kms_cursor_manager_position_changed_in_input_impl (MetaKmsCursorManager *cursor_manager, + const graphene_point_t *position); + +void meta_kms_cursor_manager_update_sprite (MetaKmsCursorManager *cursor_manager, + MetaKmsCrtc *crtc, + MetaDrmBuffer *buffer, + const graphene_point_t *hotspot); + +void meta_kms_cursor_manager_update_crtc_layout (MetaKmsCursorManager *cursor_manager, + GArray *layouts); + +#endif /* META_KMS_CURSOR_MANAGER_H */ diff --git a/src/backends/native/meta-kms-types.h b/src/backends/native/meta-kms-types.h index b216dc019..191d1afd4 100644 --- a/src/backends/native/meta-kms-types.h +++ b/src/backends/native/meta-kms-types.h @@ -43,6 +43,8 @@ typedef enum _MetaKmsPageFlipListenerFlag MetaKmsPageFlipListenerFlag; typedef struct _MetaKmsImpl MetaKmsImpl; typedef struct _MetaKmsImplDevice MetaKmsImplDevice; +typedef struct _MetaKmsCursorManager MetaKmsCursorManager; + /* 16:16 fixed point */ typedef int32_t MetaFixed16; diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c index 2ff3b5eb0..31249adde 100644 --- a/src/backends/native/meta-kms.c +++ b/src/backends/native/meta-kms.c @@ -23,6 +23,7 @@ #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" @@ -154,6 +155,8 @@ struct _MetaKms guint callback_source_id; int kernel_thread_inhibit_count; + + MetaKmsCursorManager *cursor_manager; }; G_DEFINE_TYPE (MetaKms, meta_kms, META_TYPE_THREAD) @@ -434,6 +437,8 @@ on_prepare_shutdown (MetaBackend *backend, { 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 * @@ -508,6 +513,7 @@ meta_kms_constructed (GObject *object) static void meta_kms_init (MetaKms *kms) { + kms->cursor_manager = meta_kms_cursor_manager_new (kms); } static void @@ -557,3 +563,9 @@ meta_kms_uninhibit_kernel_thread (MetaKms *kms) 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; +} diff --git a/src/meson.build b/src/meson.build index 9b36de52a..273127fec 100644 --- a/src/meson.build +++ b/src/meson.build @@ -784,6 +784,8 @@ if have_native_backend 'backends/native/meta-kms-crtc-private.h', 'backends/native/meta-kms-crtc.c', 'backends/native/meta-kms-crtc.h', + 'backends/native/meta-kms-cursor-manager.c', + 'backends/native/meta-kms-cursor-manager.h', 'backends/native/meta-kms-device-private.h', 'backends/native/meta-kms-device.c', 'backends/native/meta-kms-device.h',