diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
index b33d8e96d..f36607f29 100644
--- a/include/libweston/libweston.h
+++ b/include/libweston/libweston.h
@@ -438,6 +438,7 @@ struct weston_output {
struct wl_event_source *idle_repaint_source;
struct wl_signal frame_signal;
+ struct wl_signal post_latch_signal;
struct wl_signal destroy_signal; /**< sent when disabled */
struct weston_coord_global move;
struct timespec frame_time; /* presentation timestamp */
@@ -1493,6 +1494,13 @@ struct weston_compositor {
uint32_t placeholder_color;
bool no_xwm_decorations;
+
+ /**
+ * When set the compositor has latched content updates for the
+ * upcoming repaint, and no more updates may be applied until after
+ * that repaint occurs.
+ */
+ bool latched;
};
struct weston_solid_buffer_values {
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 2ebcbea13..417e5e7d0 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -3615,6 +3615,21 @@ output_assign_planes(struct weston_output *output)
}
}
+/* The "latch" point is the last possible instant before a repaint. After
+ * the latch, no more content updates can be applied by the compositor
+ * until after the scheduled repaint completes.
+ */
+static void
+weston_output_latch(struct weston_output *output)
+{
+ struct weston_compositor *compositor = output->compositor;
+
+ assert(!compositor->latched);
+ compositor->latched = true;
+
+ wl_signal_emit(&output->post_latch_signal, output);
+}
+
static int
weston_output_repaint(struct weston_output *output)
{
@@ -3628,6 +3643,8 @@ weston_output_repaint(struct weston_output *output)
uint32_t frame_time_msec;
enum weston_hdcp_protection highest_requested = WESTON_HDCP_DISABLE;
+ weston_output_latch(output);
+
TL_POINT(ec, TLP_CORE_REPAINT_BEGIN, TLP_OUTPUT(output), TLP_END);
/* Rebuild the surface list and update surface transforms up front. */
@@ -3696,6 +3713,7 @@ weston_output_repaint(struct weston_output *output)
output_accumulate_damage(output);
r = output->repaint(output);
+ ec->latched = false;
output->repaint_needed = false;
if (r == 0) {
@@ -8115,6 +8133,7 @@ weston_output_enable(struct weston_output *output)
output->original_scale = output->current_scale;
wl_signal_init(&output->frame_signal);
+ wl_signal_init(&output->post_latch_signal);
wl_signal_init(&output->destroy_signal);
weston_output_transform_scale_init(output, output->transform,
diff --git a/libweston/surface-state.c b/libweston/surface-state.c
index 19ac44ce0..0f7fc404d 100644
--- a/libweston/surface-state.c
+++ b/libweston/surface-state.c
@@ -272,6 +272,8 @@ weston_surface_apply_state(struct weston_surface *surface,
pixman_region32_t opaque;
enum weston_surface_status status = state->status;
+ assert(!surface->compositor->latched);
+
surface->flow_id = state->flow_id;
state->flow_id = 0;
diff --git a/protocol/weston-test.xml b/protocol/weston-test.xml
index 717e95dcf..21bd03051 100644
--- a/protocol/weston-test.xml
+++ b/protocol/weston-test.xml
@@ -99,6 +99,8 @@
+
diff --git a/tests/weston-test.c b/tests/weston-test.c
index 092f3e500..61167b1d3 100644
--- a/tests/weston-test.c
+++ b/tests/weston-test.c
@@ -83,6 +83,7 @@ struct weston_test_output {
struct weston_test *test;
struct weston_output *output;
struct wl_listener repaint_listener;
+ struct wl_listener post_latch_listener;
struct wl_list link;
};
@@ -138,6 +139,20 @@ output_repaint_listener(struct wl_listener *listener, void *data)
}
}
+static void
+output_post_latch_listener(struct wl_listener *listener, void *data)
+{
+ struct weston_test_output *to =
+ container_of(listener, struct weston_test_output,
+ post_latch_listener);
+ struct weston_head *head;
+
+ wl_list_for_each(head, &to->output->head_list, output_link) {
+ maybe_breakpoint(to->test, WESTON_TEST_BREAKPOINT_POST_LATCH,
+ head);
+ }
+}
+
static void
output_created_listener(struct wl_listener *listener, void *data)
{
@@ -149,8 +164,13 @@ output_created_listener(struct wl_listener *listener, void *data)
to->test = test;
to->output = output;
+
to->repaint_listener.notify = output_repaint_listener;
wl_signal_add(&output->frame_signal, &to->repaint_listener);
+
+ to->post_latch_listener.notify = output_post_latch_listener;
+ wl_signal_add(&output->post_latch_signal, &to->post_latch_listener);
+
wl_list_insert(&test->output_list, &to->link);
}
@@ -168,6 +188,7 @@ output_destroyed_listener(struct wl_listener *listener, void *data)
continue;
wl_list_remove(&to->repaint_listener.link);
+ wl_list_remove(&to->post_latch_listener.link);
wl_list_remove(&to->link);
free(to);
}
@@ -883,6 +904,7 @@ out_free:
wl_list_for_each_safe(to, tmp, &test->output_list, link) {
wl_list_remove(&to->repaint_listener.link);
+ wl_list_remove(&to->post_latch_listener.link);
wl_list_remove(&to->link);
free(to);
}