/* * Copyright (C) 2019 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, see . */ #include "config.h" #include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-update.h" typedef struct _MetaKmsPageFlipClosure { const MetaKmsPageFlipListenerVtable *vtable; MetaKmsPageFlipListenerFlag flags; GMainContext *main_context; gpointer user_data; GDestroyNotify destroy_notify; MetaKmsPageFlipData *page_flip_data; } MetaKmsPageFlipClosure; struct _MetaKmsPageFlipData { gatomicrefcount ref_count; MetaKmsImplDevice *impl_device; MetaKmsCrtc *crtc; GList *closures; unsigned int sequence; unsigned int sec; unsigned int usec; gboolean is_symbolic; GError *error; }; static MetaKmsPageFlipClosure * meta_kms_page_flip_closure_new (const MetaKmsPageFlipListenerVtable *vtable, MetaKmsPageFlipListenerFlag flags, GMainContext *main_context, gpointer user_data, GDestroyNotify destroy_notify) { MetaKmsPageFlipClosure *closure; closure = g_new0 (MetaKmsPageFlipClosure, 1); *closure = (MetaKmsPageFlipClosure) { .vtable = vtable, .flags = flags, .main_context = main_context, .user_data = user_data, .destroy_notify = destroy_notify, }; return closure; } static void meta_kms_page_flip_closure_free (MetaKmsPageFlipClosure *closure) { g_clear_pointer (&closure->page_flip_data, meta_kms_page_flip_data_unref); if (closure->destroy_notify) g_clear_pointer (&closure->user_data, closure->destroy_notify); g_free (closure); } static void meta_kms_page_closure_set_data (MetaKmsPageFlipClosure *closure, MetaKmsPageFlipData *page_flip_data) { g_return_if_fail (!closure->page_flip_data); closure->page_flip_data = meta_kms_page_flip_data_ref (page_flip_data); } MetaKmsPageFlipData * meta_kms_page_flip_data_new (MetaKmsImplDevice *impl_device, MetaKmsCrtc *crtc) { MetaKmsPageFlipData *page_flip_data; page_flip_data = g_new0 (MetaKmsPageFlipData , 1); *page_flip_data = (MetaKmsPageFlipData) { .impl_device = impl_device, .crtc = crtc, }; g_atomic_ref_count_init (&page_flip_data->ref_count); return page_flip_data; } MetaKmsPageFlipData * meta_kms_page_flip_data_ref (MetaKmsPageFlipData *page_flip_data) { g_atomic_ref_count_inc (&page_flip_data->ref_count); return page_flip_data; } void meta_kms_page_flip_data_unref (MetaKmsPageFlipData *page_flip_data) { if (g_atomic_ref_count_dec (&page_flip_data->ref_count)) { g_list_free_full (page_flip_data->closures, (GDestroyNotify) meta_kms_page_flip_closure_free); g_clear_error (&page_flip_data->error); g_free (page_flip_data); } } void meta_kms_page_flip_data_add_listener (MetaKmsPageFlipData *page_flip_data, const MetaKmsPageFlipListenerVtable *vtable, MetaKmsPageFlipListenerFlag flags, GMainContext *main_context, gpointer user_data, GDestroyNotify destroy_notify) { MetaKmsPageFlipClosure *closure; closure = meta_kms_page_flip_closure_new (vtable, flags, main_context, user_data, destroy_notify); page_flip_data->closures = g_list_append (page_flip_data->closures, closure); } MetaKmsImplDevice * meta_kms_page_flip_data_get_impl_device (MetaKmsPageFlipData *page_flip_data) { return page_flip_data->impl_device; } MetaKmsCrtc * meta_kms_page_flip_data_get_crtc (MetaKmsPageFlipData *page_flip_data) { return page_flip_data->crtc; } static void invoke_page_flip_closure_flipped (MetaThread *thread, gpointer user_data) { MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipData *page_flip_data = closure->page_flip_data; if (page_flip_data->is_symbolic) { closure->vtable->ready (page_flip_data->crtc, closure->user_data); } else { closure->vtable->flipped (page_flip_data->crtc, page_flip_data->sequence, page_flip_data->sec, page_flip_data->usec, closure->user_data); } } static MetaKms * meta_kms_from_impl_device (MetaKmsImplDevice *impl_device) { MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device); return meta_kms_device_get_kms (device); } void meta_kms_page_flip_data_set_timings_in_impl (MetaKmsPageFlipData *page_flip_data, unsigned int sequence, unsigned int sec, unsigned int usec) { MetaKms *kms = meta_kms_from_impl_device (page_flip_data->impl_device); meta_assert_in_kms_impl (kms); meta_topic (META_DEBUG_KMS, "Setting page flip timings for CRTC (%u, %s), sequence: %u, sec: %u, usec: %u", meta_kms_crtc_get_id (page_flip_data->crtc), meta_kms_impl_device_get_path (page_flip_data->impl_device), sequence, sec, usec); page_flip_data->sequence = sequence; page_flip_data->sec = sec; page_flip_data->usec = usec; } void meta_kms_page_flip_data_make_symbolic (MetaKmsPageFlipData *page_flip_data) { page_flip_data->is_symbolic = TRUE; } void meta_kms_page_flip_data_flipped_in_impl (MetaKmsPageFlipData *page_flip_data) { MetaKms *kms = meta_kms_from_impl_device (page_flip_data->impl_device); g_autoptr (GList) closures = NULL; GList *l; meta_assert_in_kms_impl (kms); closures = g_steal_pointer (&page_flip_data->closures); for (l = closures; l; l = l->next) { MetaKmsPageFlipClosure *closure = l->data; meta_kms_page_closure_set_data (closure, page_flip_data); meta_kms_queue_callback (kms, closure->main_context, invoke_page_flip_closure_flipped, closure, (GDestroyNotify) meta_kms_page_flip_closure_free); } meta_kms_page_flip_data_unref (page_flip_data); } static void invoke_page_flip_closure_mode_set_fallback (MetaThread *thread, gpointer user_data) { MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipData *page_flip_data = closure->page_flip_data; closure->vtable->mode_set_fallback (page_flip_data->crtc, closure->user_data); } void meta_kms_page_flip_data_mode_set_fallback_in_impl (MetaKmsPageFlipData *page_flip_data) { MetaKms *kms = meta_kms_from_impl_device (page_flip_data->impl_device); g_autoptr (GList) closures = NULL; GList *l; meta_assert_in_kms_impl (kms); closures = g_steal_pointer (&page_flip_data->closures); for (l = closures; l; l = l->next) { MetaKmsPageFlipClosure *closure = l->data; meta_kms_page_closure_set_data (closure, page_flip_data); meta_kms_queue_callback (kms, closure->main_context, invoke_page_flip_closure_mode_set_fallback, closure, (GDestroyNotify) meta_kms_page_flip_closure_free); } meta_kms_page_flip_data_unref (page_flip_data); } static void invoke_page_flip_closure_discarded (MetaThread *thread, gpointer user_data) { MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipData *page_flip_data = closure->page_flip_data; closure->vtable->discarded (page_flip_data->crtc, closure->user_data, page_flip_data->error); } void meta_kms_page_flip_data_take_error (MetaKmsPageFlipData *page_flip_data, GError *error) { g_assert (!page_flip_data->error); page_flip_data->error = error; } void meta_kms_page_flip_data_discard_in_impl (MetaKmsPageFlipData *page_flip_data, const GError *error) { MetaKms *kms = meta_kms_from_impl_device (page_flip_data->impl_device); g_autoptr (GList) closures = NULL; GList *l; meta_assert_in_kms_impl (kms); if (error) meta_kms_page_flip_data_take_error (page_flip_data, g_error_copy (error)); closures = g_steal_pointer (&page_flip_data->closures); for (l = closures; l; l = l->next) { MetaKmsPageFlipClosure *closure = l->data; meta_kms_page_closure_set_data (closure, page_flip_data); meta_kms_queue_callback (kms, closure->main_context, invoke_page_flip_closure_discarded, closure, (GDestroyNotify) meta_kms_page_flip_closure_free); } meta_kms_page_flip_data_unref (page_flip_data); }