mirror of
https://github.com/brl/mutter.git
synced 2024-11-29 19:40:43 -05:00
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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
This commit is contained in:
parent
5a798989c4
commit
6d873036e0
853
src/backends/native/meta-kms-cursor-manager.c
Normal file
853
src/backends/native/meta-kms-cursor-manager.c
Normal file
@ -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 <glib.h>
|
||||
|
||||
#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);
|
||||
}
|
62
src/backends/native/meta-kms-cursor-manager.h
Normal file
62
src/backends/native/meta-kms-cursor-manager.h
Normal file
@ -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 <glib-object.h>
|
||||
#include <graphene.h>
|
||||
|
||||
#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 */
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user