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',