mutter/src/backends/native/meta-kms-update.c
Sebastian Wick f092b6c78c backends/native: Add color space and HDR metadata updates
Allows to prepare KMS updates to set the color space and HDR Static
Metadata on the output.

For some reason we need ALLOW_MODESET on commits which change the HDR
Static Metadata InfoFrame on AMDGPU. There is no technical reason why
one needs to mode set to send an InfoFrame and the driver should just
manage without ALLOW_MODESET. Until this is resolved in the kernel we
just prepare KMS updates which might mode set.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2879>
2023-03-04 09:30:41 +00:00

1049 lines
30 KiB
C

/*
* Copyright (C) 2018 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-update.h"
#include "backends/native/meta-kms-update-private.h"
#include "backends/meta-display-config-shared.h"
#include "backends/native/meta-kms-connector.h"
#include "backends/native/meta-kms-crtc.h"
#include "backends/native/meta-kms-impl-device.h"
#include "backends/native/meta-kms-mode-private.h"
#include "backends/native/meta-kms-plane.h"
#include "backends/native/meta-kms-private.h"
struct _MetaKmsUpdate
{
MetaKmsDevice *device;
gboolean is_sealed;
GList *mode_sets;
GList *plane_assignments;
GList *connector_updates;
GList *crtc_color_updates;
MetaKmsCustomPageFlip *custom_page_flip;
GList *page_flip_listeners;
GList *result_listeners;
gboolean needs_modeset;
MetaKmsImplDevice *impl_device;
};
void
meta_kms_plane_feedback_free (MetaKmsPlaneFeedback *plane_feedback)
{
g_error_free (plane_feedback->error);
g_free (plane_feedback);
}
MetaKmsPlaneFeedback *
meta_kms_plane_feedback_new_take_error (MetaKmsPlane *plane,
MetaKmsCrtc *crtc,
GError *error)
{
MetaKmsPlaneFeedback *plane_feedback;
plane_feedback = g_new0 (MetaKmsPlaneFeedback, 1);
*plane_feedback = (MetaKmsPlaneFeedback) {
.plane = plane,
.crtc = crtc,
.error = error,
};
return plane_feedback;
}
MetaKmsPlaneFeedback *
meta_kms_plane_feedback_new_failed (MetaKmsPlane *plane,
MetaKmsCrtc *crtc,
const char *error_message)
{
GError *error;
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, error_message);
return meta_kms_plane_feedback_new_take_error (plane, crtc, error);
}
MetaKmsFeedback *
meta_kms_feedback_new_passed (GList *failed_planes)
{
MetaKmsFeedback *feedback;
feedback = g_new0 (MetaKmsFeedback, 1);
*feedback = (MetaKmsFeedback) {
.result = META_KMS_FEEDBACK_PASSED,
.failed_planes = failed_planes,
};
g_atomic_ref_count_init (&feedback->ref_count);
return feedback;
}
MetaKmsFeedback *
meta_kms_feedback_new_failed (GList *failed_planes,
GError *error)
{
MetaKmsFeedback *feedback;
feedback = g_new0 (MetaKmsFeedback, 1);
*feedback = (MetaKmsFeedback) {
.result = META_KMS_FEEDBACK_FAILED,
.error = error,
.failed_planes = failed_planes,
};
g_atomic_ref_count_init (&feedback->ref_count);
return feedback;
}
MetaKmsFeedback *
meta_kms_feedback_ref (MetaKmsFeedback *feedback)
{
g_atomic_ref_count_inc (&feedback->ref_count);
return feedback;
}
void
meta_kms_feedback_unref (MetaKmsFeedback *feedback)
{
if (g_atomic_ref_count_dec (&feedback->ref_count))
{
g_list_free_full (feedback->failed_planes,
(GDestroyNotify) meta_kms_plane_feedback_free);
g_clear_error (&feedback->error);
g_free (feedback);
}
}
MetaKmsFeedbackResult
meta_kms_feedback_get_result (const MetaKmsFeedback *feedback)
{
return feedback->result;
}
GList *
meta_kms_feedback_get_failed_planes (const MetaKmsFeedback *feedback)
{
return feedback->failed_planes;
}
const GError *
meta_kms_feedback_get_error (const MetaKmsFeedback *feedback)
{
return feedback->error;
}
void
meta_kms_feedback_dispatch_result (MetaKmsFeedback *feedback,
MetaKms *kms,
GList *result_listeners)
{
GList *l;
for (l = result_listeners; l; l = l->next)
{
MetaKmsResultListener *listener = l->data;
meta_kms_result_listener_set_feedback (listener, feedback);
meta_kms_queue_result_callback (kms, listener);
}
g_list_free (result_listeners);
}
static void
meta_kms_fb_damage_free (MetaKmsFbDamage *fb_damage)
{
g_free (fb_damage->rects);
g_free (fb_damage);
}
static void
meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment)
{
g_clear_pointer (&plane_assignment->fb_damage, meta_kms_fb_damage_free);
g_free (plane_assignment);
}
static void
meta_kms_mode_set_free (MetaKmsModeSet *mode_set)
{
g_list_free (mode_set->connectors);
g_free (mode_set);
}
static void
meta_kms_page_flip_listener_unref (MetaKmsPageFlipListener *listener)
{
if (!g_atomic_ref_count_dec (&listener->ref_count))
return;
g_clear_pointer (&listener->user_data, listener->destroy_notify);
g_free (listener);
}
static gboolean
drop_plane_assignment (MetaKmsUpdate *update,
MetaKmsPlane *plane,
MetaKmsAssignPlaneFlag *out_flags)
{
GList *l;
for (l = update->plane_assignments; l; l = l->next)
{
MetaKmsPlaneAssignment *plane_assignment = l->data;
if (plane_assignment->plane == plane)
{
update->plane_assignments =
g_list_delete_link (update->plane_assignments, l);
if (out_flags)
*out_flags = plane_assignment->flags;
meta_kms_plane_assignment_free (plane_assignment);
return TRUE;
}
}
return FALSE;
}
void
meta_kms_update_drop_plane_assignment (MetaKmsUpdate *update,
MetaKmsPlane *plane)
{
drop_plane_assignment (update, plane, NULL);
}
MetaKmsPlaneAssignment *
meta_kms_update_assign_plane (MetaKmsUpdate *update,
MetaKmsCrtc *crtc,
MetaKmsPlane *plane,
MetaDrmBuffer *buffer,
MetaFixed16Rectangle src_rect,
MetaRectangle dst_rect,
MetaKmsAssignPlaneFlag flags)
{
MetaKmsPlaneAssignment *plane_assignment;
MetaKmsAssignPlaneFlag old_flags;
g_assert (!meta_kms_update_is_sealed (update));
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
g_assert (meta_kms_plane_get_device (plane) == update->device);
g_assert (meta_kms_plane_get_plane_type (plane) !=
META_KMS_PLANE_TYPE_PRIMARY ||
!(flags & META_KMS_ASSIGN_PLANE_FLAG_ALLOW_FAIL));
if (drop_plane_assignment (update, plane, &old_flags))
{
if (!(old_flags & META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED))
flags &= ~META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED;
}
plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1);
*plane_assignment = (MetaKmsPlaneAssignment) {
.update = update,
.crtc = crtc,
.plane = plane,
.buffer = buffer,
.src_rect = src_rect,
.dst_rect = dst_rect,
.flags = flags,
};
update->plane_assignments = g_list_prepend (update->plane_assignments,
plane_assignment);
return plane_assignment;
}
MetaKmsPlaneAssignment *
meta_kms_update_unassign_plane (MetaKmsUpdate *update,
MetaKmsCrtc *crtc,
MetaKmsPlane *plane)
{
MetaKmsPlaneAssignment *plane_assignment;
g_assert (!meta_kms_update_is_sealed (update));
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
g_assert (meta_kms_plane_get_device (plane) == update->device);
plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1);
*plane_assignment = (MetaKmsPlaneAssignment) {
.update = update,
.crtc = crtc,
.plane = plane,
.buffer = NULL,
};
update->plane_assignments = g_list_prepend (update->plane_assignments,
plane_assignment);
return plane_assignment;
}
void
meta_kms_update_mode_set (MetaKmsUpdate *update,
MetaKmsCrtc *crtc,
GList *connectors,
MetaKmsMode *mode)
{
MetaKmsModeSet *mode_set;
g_assert (!meta_kms_update_is_sealed (update));
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
mode_set = g_new0 (MetaKmsModeSet, 1);
*mode_set = (MetaKmsModeSet) {
.crtc = crtc,
.connectors = connectors,
.mode = mode,
};
update->mode_sets = g_list_prepend (update->mode_sets, mode_set);
}
static MetaKmsConnectorUpdate *
ensure_connector_update (MetaKmsUpdate *update,
MetaKmsConnector *connector)
{
GList *l;
MetaKmsConnectorUpdate *connector_update;
for (l = update->connector_updates; l; l = l->next)
{
connector_update = l->data;
if (connector_update->connector == connector)
return connector_update;
}
connector_update = g_new0 (MetaKmsConnectorUpdate, 1);
connector_update->connector = connector;
update->connector_updates = g_list_prepend (update->connector_updates,
connector_update);
return connector_update;
}
void
meta_kms_update_set_underscanning (MetaKmsUpdate *update,
MetaKmsConnector *connector,
uint64_t hborder,
uint64_t vborder)
{
MetaKmsConnectorUpdate *connector_update;
g_assert (!meta_kms_update_is_sealed (update));
g_assert (meta_kms_connector_get_device (connector) == update->device);
connector_update = ensure_connector_update (update, connector);
connector_update->underscanning.has_update = TRUE;
connector_update->underscanning.is_active = TRUE;
connector_update->underscanning.hborder = hborder;
connector_update->underscanning.vborder = vborder;
}
void
meta_kms_update_unset_underscanning (MetaKmsUpdate *update,
MetaKmsConnector *connector)
{
MetaKmsConnectorUpdate *connector_update;
g_assert (!meta_kms_update_is_sealed (update));
g_assert (meta_kms_connector_get_device (connector) == update->device);
connector_update = ensure_connector_update (update, connector);
connector_update->underscanning.has_update = TRUE;
connector_update->underscanning.is_active = FALSE;
}
void
meta_kms_update_set_privacy_screen (MetaKmsUpdate *update,
MetaKmsConnector *connector,
gboolean enabled)
{
MetaKmsConnectorUpdate *connector_update;
g_assert (meta_kms_connector_get_device (connector) == update->device);
connector_update = ensure_connector_update (update, connector);
connector_update->privacy_screen.has_update = TRUE;
connector_update->privacy_screen.is_enabled = enabled;
}
void
meta_kms_update_set_max_bpc (MetaKmsUpdate *update,
MetaKmsConnector *connector,
uint64_t max_bpc)
{
MetaKmsConnectorUpdate *connector_update;
g_assert (meta_kms_connector_get_device (connector) == update->device);
connector_update = ensure_connector_update (update, connector);
connector_update->max_bpc.value = max_bpc;
connector_update->max_bpc.has_update = TRUE;
}
void
meta_kms_update_set_color_space (MetaKmsUpdate *update,
MetaKmsConnector *connector,
MetaOutputColorspace color_space)
{
MetaKmsConnectorUpdate *connector_update;
g_assert (meta_kms_connector_get_device (connector) == update->device);
g_return_if_fail (meta_kms_connector_is_color_space_supported (connector,
color_space));
connector_update = ensure_connector_update (update, connector);
connector_update->colorspace.has_update = TRUE;
connector_update->colorspace.value = color_space;
}
void
meta_kms_update_set_hdr_metadata (MetaKmsUpdate *update,
MetaKmsConnector *connector,
MetaOutputHdrMetadata *metadata)
{
MetaKmsConnectorUpdate *connector_update;
g_assert (meta_kms_connector_get_device (connector) == update->device);
g_return_if_fail (meta_kms_connector_is_hdr_metadata_supported (connector));
connector_update = ensure_connector_update (update, connector);
connector_update->hdr.has_update = TRUE;
connector_update->hdr.value = *metadata;
/* Currently required on AMDGPU but should in general not require mode sets */
update->needs_modeset = TRUE;
}
static MetaKmsCrtcColorUpdate *
ensure_color_update (MetaKmsUpdate *update,
MetaKmsCrtc *crtc)
{
GList *l;
MetaKmsCrtcColorUpdate *color_update;
for (l = update->crtc_color_updates; l; l = l->next)
{
color_update = l->data;
if (color_update->crtc == crtc)
return color_update;
}
color_update = g_new0 (MetaKmsCrtcColorUpdate, 1);
color_update->crtc = crtc;
update->crtc_color_updates = g_list_prepend (update->crtc_color_updates,
color_update);
return color_update;
}
void
meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update,
MetaKmsCrtc *crtc,
const MetaGammaLut *gamma)
{
MetaKmsCrtcColorUpdate *color_update;
MetaGammaLut *gamma_update = NULL;
const MetaKmsCrtcState *crtc_state = meta_kms_crtc_get_current_state (crtc);
g_assert (!meta_kms_update_is_sealed (update));
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
if (gamma)
gamma_update = meta_gamma_lut_copy_to_size (gamma, crtc_state->gamma.size);
color_update = ensure_color_update (update, crtc);
color_update->gamma.state = gamma_update;
color_update->gamma.has_update = TRUE;
}
static void
meta_kms_crtc_color_updates_free (MetaKmsCrtcColorUpdate *color_update)
{
if (color_update->gamma.has_update)
g_clear_pointer (&color_update->gamma.state, meta_gamma_lut_free);
}
void
meta_kms_update_add_page_flip_listener (MetaKmsUpdate *update,
MetaKmsCrtc *crtc,
const MetaKmsPageFlipListenerVtable *vtable,
MetaKmsPageFlipListenerFlag flags,
gpointer user_data,
GDestroyNotify destroy_notify)
{
MetaKmsPageFlipListener *listener;
g_assert (!meta_kms_update_is_sealed (update));
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
listener = g_new0 (MetaKmsPageFlipListener, 1);
*listener = (MetaKmsPageFlipListener) {
.crtc = crtc,
.vtable = vtable,
.flags = flags,
.user_data = user_data,
.destroy_notify = destroy_notify,
};
g_atomic_ref_count_init (&listener->ref_count);
update->page_flip_listeners = g_list_prepend (update->page_flip_listeners,
listener);
}
void
meta_kms_update_set_custom_page_flip (MetaKmsUpdate *update,
MetaKmsCustomPageFlipFunc func,
gpointer user_data)
{
MetaKmsCustomPageFlip *custom_page_flip;
g_assert (!meta_kms_update_is_sealed (update));
custom_page_flip = g_new0 (MetaKmsCustomPageFlip, 1);
custom_page_flip->func = func;
custom_page_flip->user_data = user_data;
update->custom_page_flip = custom_page_flip;
}
void
meta_kms_plane_assignment_set_fb_damage (MetaKmsPlaneAssignment *plane_assignment,
const int *rectangles,
int n_rectangles)
{
MetaKmsFbDamage *fb_damage;
struct drm_mode_rect *mode_rects;
int i;
mode_rects = g_new0 (struct drm_mode_rect, n_rectangles);
for (i = 0; i < n_rectangles; ++i)
{
mode_rects[i].x1 = rectangles[i * 4];
mode_rects[i].y1 = rectangles[i * 4 + 1];
mode_rects[i].x2 = mode_rects[i].x1 + rectangles[i * 4 + 2];
mode_rects[i].y2 = mode_rects[i].y1 + rectangles[i * 4 + 3];
}
fb_damage = g_new0 (MetaKmsFbDamage, 1);
*fb_damage = (MetaKmsFbDamage) {
.rects = mode_rects,
.n_rects = n_rectangles,
};
plane_assignment->fb_damage = fb_damage;
}
void
meta_kms_plane_assignment_set_rotation (MetaKmsPlaneAssignment *plane_assignment,
MetaKmsPlaneRotation rotation)
{
g_assert (!meta_kms_update_is_sealed (plane_assignment->update));
g_warn_if_fail (rotation);
plane_assignment->rotation = rotation;
}
void
meta_kms_plane_assignment_set_cursor_hotspot (MetaKmsPlaneAssignment *plane_assignment,
int x,
int y)
{
plane_assignment->cursor_hotspot.is_valid = TRUE;
plane_assignment->cursor_hotspot.x = x;
plane_assignment->cursor_hotspot.y = y;
}
void
meta_kms_update_add_result_listener (MetaKmsUpdate *update,
MetaKmsResultListenerFunc func,
gpointer user_data)
{
MetaKmsResultListener *listener;
listener = g_new0 (MetaKmsResultListener, 1);
*listener = (MetaKmsResultListener) {
.func = func,
.user_data = user_data,
};
update->result_listeners = g_list_append (update->result_listeners,
listener);
}
GList *
meta_kms_update_take_result_listeners (MetaKmsUpdate *update)
{
return g_steal_pointer (&update->result_listeners);
}
void
meta_kms_result_listener_set_feedback (MetaKmsResultListener *listener,
MetaKmsFeedback *feedback)
{
g_return_if_fail (!listener->feedback);
listener->feedback = meta_kms_feedback_ref (feedback);
}
void
meta_kms_result_listener_notify (MetaKmsResultListener *listener)
{
g_return_if_fail (listener->feedback);
listener->func (listener->feedback, listener->user_data);
}
void
meta_kms_result_listener_free (MetaKmsResultListener *listener)
{
g_clear_pointer (&listener->feedback, meta_kms_feedback_unref);
g_free (listener);
}
static MetaKmsPlaneAssignment *
get_first_plane_assignment (MetaKmsUpdate *update,
MetaKmsCrtc *crtc,
MetaKmsPlaneType plane_type)
{
GList *l;
for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next)
{
MetaKmsPlaneAssignment *plane_assignment = l->data;
if (meta_kms_plane_get_plane_type (plane_assignment->plane) !=
plane_type)
continue;
if (plane_assignment->crtc != crtc)
continue;
return plane_assignment;
}
return NULL;
}
MetaKmsPlaneAssignment *
meta_kms_update_get_primary_plane_assignment (MetaKmsUpdate *update,
MetaKmsCrtc *crtc)
{
return get_first_plane_assignment (update, crtc, META_KMS_PLANE_TYPE_PRIMARY);
}
MetaKmsPlaneAssignment *
meta_kms_update_get_cursor_plane_assignment (MetaKmsUpdate *update,
MetaKmsCrtc *crtc)
{
return get_first_plane_assignment (update, crtc, META_KMS_PLANE_TYPE_CURSOR);
}
GList *
meta_kms_update_get_plane_assignments (MetaKmsUpdate *update)
{
return update->plane_assignments;
}
GList *
meta_kms_update_get_mode_sets (MetaKmsUpdate *update)
{
return update->mode_sets;
}
GList *
meta_kms_update_get_page_flip_listeners (MetaKmsUpdate *update)
{
return update->page_flip_listeners;
}
GList *
meta_kms_update_get_connector_updates (MetaKmsUpdate *update)
{
return update->connector_updates;
}
GList *
meta_kms_update_get_crtc_color_updates (MetaKmsUpdate *update)
{
return update->crtc_color_updates;
}
void
meta_kms_update_seal (MetaKmsUpdate *update)
{
g_warn_if_fail (!update->is_sealed);
update->is_sealed = TRUE;
}
gboolean
meta_kms_update_is_sealed (MetaKmsUpdate *update)
{
return update->is_sealed;
}
MetaKmsDevice *
meta_kms_update_get_device (MetaKmsUpdate *update)
{
return update->device;
}
MetaKmsCustomPageFlip *
meta_kms_update_take_custom_page_flip_func (MetaKmsUpdate *update)
{
return g_steal_pointer (&update->custom_page_flip);
}
void
meta_kms_custom_page_flip_free (MetaKmsCustomPageFlip *custom_page_flip)
{
g_free (custom_page_flip);
}
static GList *
find_mode_set_link_for (MetaKmsUpdate *update,
MetaKmsCrtc *crtc)
{
GList *l;
for (l = update->mode_sets; l; l = l->next)
{
MetaKmsModeSet *mode_set = l->data;
if (mode_set->crtc == crtc)
return l;
}
return NULL;
}
static void
merge_mode_sets (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
while (other_update->mode_sets)
{
GList *l = other_update->mode_sets;
MetaKmsModeSet *other_mode_set = l->data;
MetaKmsCrtc *crtc = other_mode_set->crtc;
GList *el;
other_update->mode_sets =
g_list_remove_link (other_update->mode_sets, l);
el = find_mode_set_link_for (update, crtc);
if (el)
{
meta_kms_mode_set_free (el->data);
update->mode_sets =
g_list_insert_before_link (update->mode_sets, el, l);
update->mode_sets =
g_list_delete_link (update->mode_sets, el);
}
else
{
update->mode_sets =
g_list_insert_before_link (update->mode_sets,
update->mode_sets,
l);
}
}
}
static GList *
find_plane_assignment_link_for (MetaKmsUpdate *update,
MetaKmsPlane *plane)
{
GList *l;
for (l = update->plane_assignments; l; l = l->next)
{
MetaKmsPlaneAssignment *plane_assignment = l->data;
if (plane_assignment->plane == plane)
return l;
}
return NULL;
}
static void
merge_plane_assignments_from (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
while (other_update->plane_assignments)
{
GList *l = other_update->plane_assignments;
MetaKmsPlaneAssignment *other_plane_assignment = l->data;
MetaKmsPlane *plane = other_plane_assignment->plane;
GList *el;
other_update->plane_assignments =
g_list_remove_link (other_update->plane_assignments, l);
el = find_plane_assignment_link_for (update, plane);
if (el)
{
meta_kms_plane_assignment_free (el->data);
update->plane_assignments =
g_list_insert_before_link (update->plane_assignments, el, l);
update->plane_assignments =
g_list_delete_link (update->plane_assignments, el);
}
else
{
update->plane_assignments =
g_list_insert_before_link (update->plane_assignments,
update->plane_assignments,
l);
}
other_plane_assignment->update = update;
}
}
static GList *
find_color_update_link_for (MetaKmsUpdate *update,
MetaKmsCrtc *crtc)
{
GList *l;
for (l = update->crtc_color_updates; l; l = l->next)
{
MetaKmsCrtcColorUpdate *color_update = l->data;
if (color_update->crtc == crtc)
return l;
}
return NULL;
}
static void
merge_crtc_color_updates_from (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
while (other_update->crtc_color_updates)
{
GList *l = other_update->crtc_color_updates;
MetaKmsCrtcColorUpdate *other_crtc_color_update = l->data;
MetaKmsCrtc *crtc = other_crtc_color_update->crtc;
GList *el;
other_update->crtc_color_updates =
g_list_remove_link (other_update->crtc_color_updates, l);
el = find_color_update_link_for (update, crtc);
if (el)
{
meta_kms_crtc_color_updates_free (el->data);
update->crtc_color_updates =
g_list_insert_before_link (update->crtc_color_updates, el, l);
update->crtc_color_updates =
g_list_delete_link (update->crtc_color_updates, el);
}
else
{
update->crtc_color_updates =
g_list_insert_before_link (update->crtc_color_updates,
update->crtc_color_updates,
l);
}
}
}
static GList *
find_connector_update_link_for (MetaKmsUpdate *update,
MetaKmsConnector *connector)
{
GList *l;
for (l = update->connector_updates; l; l = l->next)
{
MetaKmsConnectorUpdate *connector_update = l->data;
if (connector_update->connector == connector)
return l;
}
return NULL;
}
static void
merge_connector_updates_from (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
while (other_update->connector_updates)
{
GList *l = other_update->connector_updates;
MetaKmsConnectorUpdate *other_connector_update = l->data;
MetaKmsConnector *connector = other_connector_update->connector;
GList *el;
other_update->connector_updates =
g_list_remove_link (other_update->connector_updates, l);
el = find_connector_update_link_for (update, connector);
if (el)
{
MetaKmsConnectorUpdate *connector_update = el->data;
if (other_connector_update->underscanning.has_update)
{
connector_update->underscanning =
other_connector_update->underscanning;
}
if (other_connector_update->privacy_screen.has_update)
{
connector_update->privacy_screen =
other_connector_update->privacy_screen;
}
if (other_connector_update->max_bpc.has_update)
{
connector_update->max_bpc =
other_connector_update->max_bpc;
}
if (other_connector_update->colorspace.has_update)
{
connector_update->colorspace =
other_connector_update->colorspace;
}
if (other_connector_update->hdr.has_update)
{
connector_update->hdr = other_connector_update->hdr;
}
}
else
{
update->connector_updates =
g_list_insert_before_link (update->connector_updates,
update->connector_updates,
l);
}
}
}
static void
merge_custom_page_flip_from (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
g_warn_if_fail ((!update->custom_page_flip &&
!other_update->custom_page_flip) ||
((!!update->custom_page_flip) ^
(!!other_update->custom_page_flip)));
g_clear_pointer (&update->custom_page_flip, meta_kms_custom_page_flip_free);
update->custom_page_flip = g_steal_pointer (&other_update->custom_page_flip);
}
static void
merge_page_flip_listeners_from (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
update->page_flip_listeners =
g_list_concat (update->page_flip_listeners,
g_steal_pointer (&other_update->page_flip_listeners));
}
static void
merge_result_listeners_from (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
update->result_listeners =
g_list_concat (update->result_listeners,
g_steal_pointer (&other_update->result_listeners));
}
void
meta_kms_update_merge_from (MetaKmsUpdate *update,
MetaKmsUpdate *other_update)
{
g_return_if_fail (update->device == other_update->device);
merge_mode_sets (update, other_update);
merge_plane_assignments_from (update, other_update);
merge_crtc_color_updates_from (update, other_update);
merge_connector_updates_from (update, other_update);
merge_custom_page_flip_from (update, other_update);
merge_page_flip_listeners_from (update, other_update);
merge_result_listeners_from (update, other_update);
}
gboolean
meta_kms_update_get_needs_modeset (MetaKmsUpdate *update)
{
return update->needs_modeset || update->mode_sets;
}
MetaKmsUpdate *
meta_kms_update_new (MetaKmsDevice *device)
{
MetaKmsUpdate *update;
update = g_new0 (MetaKmsUpdate, 1);
update->device = device;
return update;
}
void
meta_kms_update_free (MetaKmsUpdate *update)
{
if (update->impl_device)
meta_kms_impl_device_unhold_fd (update->impl_device);
g_list_free_full (update->result_listeners,
(GDestroyNotify) meta_kms_result_listener_free);
g_list_free_full (update->plane_assignments,
(GDestroyNotify) meta_kms_plane_assignment_free);
g_list_free_full (update->mode_sets,
(GDestroyNotify) meta_kms_mode_set_free);
g_list_free_full (update->page_flip_listeners,
(GDestroyNotify) meta_kms_page_flip_listener_unref);
g_list_free_full (update->connector_updates, g_free);
g_list_free_full (update->crtc_color_updates,
(GDestroyNotify) meta_kms_crtc_color_updates_free);
g_clear_pointer (&update->custom_page_flip, meta_kms_custom_page_flip_free);
g_free (update);
}
void
meta_kms_update_realize (MetaKmsUpdate *update,
MetaKmsImplDevice *impl_device)
{
update->impl_device = impl_device;
meta_kms_impl_device_hold_fd (impl_device);
}