Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libliftoff for openSUSE:Factory 
checked in at 2024-05-28 17:29:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libliftoff (Old)
 and      /work/SRC/openSUSE:Factory/.libliftoff.new.24587 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libliftoff"

Tue May 28 17:29:39 2024 rev:4 rq:1177281 version:0.5.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libliftoff/libliftoff.changes    2023-03-07 
16:50:31.153642952 +0100
+++ /work/SRC/openSUSE:Factory/.libliftoff.new.24587/libliftoff.changes 
2024-05-28 17:30:37.875985557 +0200
@@ -1,0 +2,7 @@
+Tue May 28 07:58:02 UTC 2024 - llyyr <[email protected]>
+
+- Update to 0.5.0:
+  * See full changelog at 
+    https://gitlab.freedesktop.org/emersion/libliftoff/-/tags/v0.5.0
+
+-------------------------------------------------------------------

Old:
----
  libliftoff-v0.4.1.tar.gz

New:
----
  libliftoff-v0.5.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libliftoff.spec ++++++
--- /var/tmp/diff_new_pack.druoWL/_old  2024-05-28 17:30:38.956025051 +0200
+++ /var/tmp/diff_new_pack.druoWL/_new  2024-05-28 17:30:38.960025198 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package libliftoff
 #
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %define libname libliftoff0
 
 Name:           libliftoff
-Version:        0.4.1
+Version:        0.5.0
 Release:        0
 Summary:        KMS plane library
 Group:          Development/Libraries/C and C++

++++++ libliftoff-v0.4.1.tar.gz -> libliftoff-v0.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/.builds/alpine.yml 
new/libliftoff-v0.5.0/.builds/alpine.yml
--- old/libliftoff-v0.4.1/.builds/alpine.yml    2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/.builds/alpine.yml    2024-05-28 00:22:40.000000000 
+0200
@@ -1,4 +1,4 @@
-image: alpine/edge
+image: alpine/latest
 packages:
   - gcc
   - clang
@@ -10,11 +10,13 @@
   - https://gitlab.freedesktop.org/emersion/libliftoff.git
 artifacts:
   - coveragereport.tar.gz
+environment:
+  UBSAN_OPTIONS: halt_on_error=1
 tasks:
   - setup: |
       cd libliftoff
-      CC=gcc meson build-gcc/ --fatal-meson-warnings -Db_coverage=true
-      CC=clang meson build-clang/ --fatal-meson-warnings 
-Db_sanitize=address,undefined -Db_lundef=false
+      CC=gcc meson setup build-gcc/ --fatal-meson-warnings -Db_coverage=true
+      CC=clang meson setup build-clang/ --fatal-meson-warnings 
-Db_sanitize=address,undefined -Db_lundef=false
   - build-gcc: |
       cd libliftoff
       ninja -C build-gcc/
@@ -29,8 +31,6 @@
       ninja -C build-clang/ test
   - coverage: |
       cd libliftoff
-      echo "exclude-directories = test/" >>gcovr.cfg
-      echo "exclude-directories = example/" >>gcovr.cfg
       ninja -C build-gcc/ coverage-html
       cd build-gcc/meson-logs/
       tar -czf ~/coveragereport.tar.gz coveragereport
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/.builds/freebsd.yml 
new/libliftoff-v0.5.0/.builds/freebsd.yml
--- old/libliftoff-v0.4.1/.builds/freebsd.yml   2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/.builds/freebsd.yml   2024-05-28 00:22:40.000000000 
+0200
@@ -8,7 +8,7 @@
 tasks:
 - setup: |
     cd libliftoff
-    meson build/ --fatal-meson-warnings
+    meson setup build/ --fatal-meson-warnings
 - build: |
     cd libliftoff
     ninja -C build/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/README.md 
new/libliftoff-v0.5.0/README.md
--- old/libliftoff-v0.4.1/README.md     2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/README.md     2024-05-28 00:22:40.000000000 +0200
@@ -20,7 +20,7 @@
 
 Depends on libdrm. Requires universal planes and atomic.
 
-    meson build/
+    meson setup build/
     ninja -C build/
 
 ## Usage
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/alloc.c 
new/libliftoff-v0.5.0/alloc.c
--- old/libliftoff-v0.4.1/alloc.c       2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/alloc.c       2024-05-28 00:22:40.000000000 +0200
@@ -1,9 +1,11 @@
+#define _POSIX_C_SOURCE 200112L
 #include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 #include "log.h"
 #include "private.h"
@@ -76,6 +78,9 @@
        struct liftoff_layer **best;
        int best_score;
 
+       struct timespec started_at;
+       int64_t timeout_ns;
+
        /* per-output */
        bool has_composition_layer;
        size_t non_composition_layers_len;
@@ -96,6 +101,29 @@
        char log_prefix[64];
 };
 
+static const int64_t NSEC_PER_SEC = 1000 * 1000 * 1000;
+
+static int64_t
+timespec_to_nsec(struct timespec ts)
+{
+       return (int64_t)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
+}
+
+static const int64_t DEFAULT_ALLOC_TIMEOUT_NSEC = 1000 * 1000; // 1ms
+
+static bool
+check_deadline(struct timespec start, int64_t timeout_ns)
+{
+       struct timespec now;
+
+       if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
+               liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime");
+               return false;
+       }
+
+       return timespec_to_nsec(now) - timeout_ns < timespec_to_nsec(start);
+}
+
 static void
 plane_step_init_next(struct alloc_step *step, struct alloc_step *prev,
                     struct liftoff_layer *layer)
@@ -126,7 +154,7 @@
 
        zpos_prop = NULL;
        if (layer != NULL) {
-               zpos_prop = layer_get_property(layer, "zpos");
+               zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
        }
        if (zpos_prop != NULL && plane->type != DRM_PLANE_TYPE_PRIMARY) {
                step->last_layer_zpos = zpos_prop->value;
@@ -176,7 +204,7 @@
        struct liftoff_layer *other_layer;
        struct liftoff_layer_property *zpos_prop, *other_zpos_prop;
 
-       zpos_prop = layer_get_property(layer, "zpos");
+       zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
        if (zpos_prop == NULL) {
                return false;
        }
@@ -186,7 +214,8 @@
                        continue;
                }
 
-               other_zpos_prop = layer_get_property(other_layer, "zpos");
+               other_zpos_prop = layer_get_core_property(other_layer,
+                                                         LIFTOFF_PROP_ZPOS);
                if (other_zpos_prop == NULL) {
                        continue;
                }
@@ -209,7 +238,7 @@
        struct liftoff_layer *other_layer;
        struct liftoff_layer_property *zpos_prop, *other_zpos_prop;
 
-       zpos_prop = layer_get_property(layer, "zpos");
+       zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
        if (zpos_prop == NULL) {
                return false;
        }
@@ -229,7 +258,8 @@
                        continue;
                }
 
-               other_zpos_prop = layer_get_property(other_layer, "zpos");
+               other_zpos_prop = layer_get_core_property(other_layer,
+                                                         LIFTOFF_PROP_ZPOS);
                if (other_zpos_prop == NULL) {
                        continue;
                }
@@ -292,7 +322,7 @@
                return false;
        }
 
-       zpos_prop = layer_get_property(layer, "zpos");
+       zpos_prop = layer_get_core_property(layer, LIFTOFF_PROP_ZPOS);
        if (zpos_prop != NULL) {
                if ((int)zpos_prop->value > step->last_layer_zpos &&
                    has_allocated_layer_over(output, step, layer)) {
@@ -387,6 +417,31 @@
        return true;
 }
 
+static bool
+check_plane_output_compatible(struct liftoff_plane *plane, struct 
liftoff_output *output)
+{
+       return (plane->possible_crtcs & (1 << output->crtc_index)) != 0;
+}
+
+static int
+count_remaining_compatible_planes(struct liftoff_output *output,
+                                 struct alloc_step *step)
+{
+       struct liftoff_list *link;
+       struct liftoff_plane *plane;
+       int remaining = 0;
+
+       for (link = step->plane_link; link != &output->device->planes; link = 
link->next) {
+               plane = liftoff_container_of(link, plane, link);
+               if (plane->layer == NULL &&
+                   check_plane_output_compatible(plane, output)) {
+                       remaining++;
+               }
+       }
+
+       return remaining;
+}
+
 static int
 output_choose_layers(struct liftoff_output *output, struct alloc_result 
*result,
                     struct alloc_step *step)
@@ -395,7 +450,7 @@
        struct liftoff_plane *plane;
        struct liftoff_layer *layer;
        int cursor, ret;
-       size_t remaining_planes;
+       int remaining_planes;
        struct alloc_step next_step = {0};
 
        device = output->device;
@@ -416,21 +471,16 @@
 
        plane = liftoff_container_of(step->plane_link, plane, link);
 
-       remaining_planes = result->planes_len - step->plane_idx;
-       if (result->best_score >= step->score + (int)remaining_planes) {
+       remaining_planes = count_remaining_compatible_planes(output, step);
+       if (result->best_score >= step->score + remaining_planes) {
                /* Even if we find a layer for all remaining planes, we won't
                 * find a better allocation. Give up. */
-               /* TODO: change remaining_planes to only count those whose
-                * possible CRTC match and which aren't allocated */
                return 0;
        }
 
        cursor = drmModeAtomicGetCursor(result->req);
 
-       if (plane->layer != NULL) {
-               goto skip;
-       }
-       if ((plane->possible_crtcs & (1 << output->crtc_index)) == 0) {
+       if (plane->layer != NULL || !check_plane_output_compatible(plane, 
output)) {
                goto skip;
        }
 
@@ -449,6 +499,12 @@
                        continue;
                }
 
+               if (!check_deadline(result->started_at, result->timeout_ns)) {
+                       liftoff_log(LIFTOFF_DEBUG, "%s Deadline exceeded",
+                                   step->log_prefix);
+                       break;
+               }
+
                /* Try to use this layer for the current plane */
                ret = plane_apply(plane, layer, result->req);
                if (ret == -EINVAL) {
@@ -542,10 +598,37 @@
 }
 
 static bool
-layer_needs_realloc(struct liftoff_layer *layer)
+layer_intersection_changed(struct liftoff_layer *this,
+                          struct liftoff_output *output)
+{
+       struct liftoff_layer *other;
+       struct liftoff_rect this_cur, this_prev, other_cur, other_prev;
+
+       layer_get_rect(this, &this_cur);
+       layer_get_prev_rect(this, &this_prev);
+       liftoff_list_for_each(other, &output->layers, link) {
+               if (this == other) {
+                       continue;
+               }
+
+               layer_get_rect(other, &other_cur);
+               layer_get_prev_rect(other, &other_prev);
+
+               if (rect_intersects(&this_cur, &other_cur) !=
+                   rect_intersects(&this_prev, &other_prev)) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool
+layer_needs_realloc(struct liftoff_layer *layer, struct liftoff_output *output)
 {
-       size_t i;
        struct liftoff_layer_property *prop;
+       bool check_crtc_intersect = false;
+       size_t i;
 
        if (layer->changed) {
                liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
@@ -563,7 +646,7 @@
                 * If FB_ID changes from non-zero to non-zero and the FB
                 * attributes didn't change, we can try to re-use the previous
                 * allocation. */
-               if (strcmp(prop->name, "FB_ID") == 0) {
+               if (prop->core_index == LIFTOFF_PROP_FB_ID) {
                        if (prop->value == 0 && prop->prev_value == 0) {
                                continue;
                        }
@@ -593,7 +676,7 @@
                /* If the layer was or becomes completely transparent or
                 * completely opaque, we might be able to find a better
                 * allocation. Otherwise, we can keep the current one. */
-               if (strcmp(prop->name, "alpha") == 0) {
+               if (prop->core_index == LIFTOFF_PROP_ALPHA) {
                        if (prop->value == 0 || prop->prev_value == 0 ||
                            prop->value == 0xFFFF || prop->prev_value == 
0xFFFF) {
                                liftoff_log(LIFTOFF_DEBUG, "Cannot re-use 
previous allocation: "
@@ -610,16 +693,123 @@
                        continue;
                }
 
-               /* TODO: if CRTC_{X,Y,W,H} changed but intersection with other
-                * layers hasn't changed, don't realloc */
+               /* If CRTC_* changed, check for intersection later */
+               if (strcmp(prop->name, "CRTC_X") == 0 ||
+                   strcmp(prop->name, "CRTC_Y") == 0 ||
+                   strcmp(prop->name, "CRTC_W") == 0 ||
+                   strcmp(prop->name, "CRTC_H") == 0) {
+                       check_crtc_intersect = true;
+                       continue;
+               }
+
                liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
                            "property \"%s\" changed", prop->name);
                return true;
        }
 
+       if (check_crtc_intersect &&
+           layer_intersection_changed(layer, output)) {
+               liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
+                           "intersection with other layer(s) changed");
+               return true;
+       }
+
        return false;
 }
 
+static bool
+layer_is_higher_priority(struct liftoff_layer *this, struct liftoff_layer 
*other)
+{
+       struct liftoff_layer_property *this_zpos, *other_zpos;
+       bool this_visible, other_visible, intersects;
+
+       // The composition layer should be highest priority.
+       if (this->output->composition_layer == this) {
+               return true;
+       } else if (this->output->composition_layer == other) {
+               return false;
+       }
+
+       // Invisible layers are given lowest priority. Pass-thru if both have
+       // same visibility
+       this_visible = layer_is_visible(this);
+       other_visible = layer_is_visible(other);
+       if (this_visible != other_visible) {
+               return this_visible;
+       }
+
+       // A layer's overall priority is determined by a combination of it's
+       // current_priority, it's zpos, and whether it intersects with others.
+       //
+       // Consider two layers. If they do not intersect, the layer with higher
+       // priority is given overall priority. However if both layers have
+       // identical priority, then the layer with higher zpos is given overall
+       // priority.
+       //
+       // If the layers intersect, their zpos determines the overall priority.
+       // If their zpos are identical, then simply fallback to looking at
+       // current_priority. Otherwise, the layer with higher zpos is given
+       // overall priority, since the top layer needs to be offloaded in order
+       // to offload the bottom layer.
+
+       this_zpos = layer_get_core_property(this, LIFTOFF_PROP_ZPOS);
+       other_zpos = layer_get_core_property(other, LIFTOFF_PROP_ZPOS);
+       intersects = layer_intersects(this, other);
+
+       if (this_zpos != NULL && other_zpos != NULL) {
+               if (intersects) {
+                       return this_zpos->value == other_zpos->value ?
+                              this->current_priority > other->current_priority 
:
+                              this_zpos->value > other_zpos->value;
+               } else {
+                       return this->current_priority == 
other->current_priority ?
+                              this_zpos->value > other_zpos->value :
+                              this->current_priority > other->current_priority;
+               }
+       } else if (this_zpos == NULL && other_zpos == NULL) {
+               return this->current_priority > other->current_priority;
+       } else {
+               // Either this or other zpos is null
+               return this_zpos != NULL;
+       }
+}
+
+static bool
+update_layers_order(struct liftoff_output *output)
+{
+       struct liftoff_list *search, *max, *cur, *head;
+       struct liftoff_layer *this_layer, *other_layer;
+       bool order_changed = false;
+
+       head = &output->layers;
+       cur = head;
+
+       // Run a insertion sort to order layers by priority.
+       while (cur->next != head) {
+               cur = cur->next;
+
+               max = cur;
+               search = cur;
+               while (search->next != head) {
+                       search = search->next;
+                       this_layer = liftoff_container_of(search, this_layer, 
link);
+                       other_layer = liftoff_container_of(max, other_layer, 
link);
+                       if (layer_is_higher_priority(this_layer, other_layer)) {
+                               max = search;
+                       }
+               }
+
+               if (cur != max) {
+                       liftoff_list_swap(cur, max);
+                       // max is now where iterator cur was, relocate to 
continue
+                       cur = max;
+                       order_changed = true;
+               }
+       }
+
+       return order_changed;
+}
+
 static int
 reuse_previous_alloc(struct liftoff_output *output, drmModeAtomicReq *req,
                     uint32_t flags)
@@ -627,9 +817,12 @@
        struct liftoff_device *device;
        struct liftoff_layer *layer;
        int cursor, ret;
+       bool layer_order_changed;
 
        device = output->device;
 
+       layer_order_changed = update_layers_order(output);
+
        if (output->layers_changed) {
                liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
                            "a layer has been added or removed");
@@ -637,11 +830,17 @@
        }
 
        liftoff_list_for_each(layer, &output->layers, link) {
-               if (layer_needs_realloc(layer)) {
+               if (layer_needs_realloc(layer, output)) {
                        return -EINVAL;
                }
        }
 
+       if (layer_order_changed) {
+               liftoff_log(LIFTOFF_DEBUG, "Cannot re-use previous allocation: "
+                           "layer priority order changed.");
+               return -EINVAL;
+       }
+
        cursor = drmModeAtomicGetCursor(req);
 
        ret = apply_current(device, req);
@@ -698,7 +897,8 @@
         * re-created a completely different one which happens to have the same
         * FB ID. */
        liftoff_list_for_each(layer, &output->layers, link) {
-               memset(&layer->fb_info, 0, sizeof(layer->fb_info));
+               layer->fb_info = (drmModeFB2){0};
+
                layer_cache_fb_info(layer);
                /* TODO: propagate error? */
        }
@@ -709,8 +909,8 @@
 {
        if (output->alloc_reused_counter == 0) {
                liftoff_log(LIFTOFF_DEBUG,
-                           "Reusing previous plane allocation on output %p",
-                           (void *)output);
+                           "Reusing previous plane allocation on output 
%"PRIu32,
+                           output->crtc_id);
        }
        output->alloc_reused_counter++;
 }
@@ -718,14 +918,14 @@
 static void
 log_no_reuse(struct liftoff_output *output)
 {
-       liftoff_log(LIFTOFF_DEBUG, "Computing plane allocation on output %p",
-                   (void *)output);
+       liftoff_log(LIFTOFF_DEBUG, "Computing plane allocation on output 
%"PRIu32,
+                   output->crtc_id);
 
        if (output->alloc_reused_counter != 0) {
                liftoff_log(LIFTOFF_DEBUG,
                            "Stopped reusing previous plane allocation on "
-                           "output %p (had reused it %d times)",
-                           (void *)output, output->alloc_reused_counter);
+                           "output %"PRIu32" (had reused it %d times)",
+                           output->crtc_id, output->alloc_reused_counter);
                output->alloc_reused_counter = 0;
        }
 }
@@ -749,15 +949,22 @@
 
 int
 liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req,
-                    uint32_t flags)
+                    uint32_t flags,
+                    const struct liftoff_output_apply_options *options)
 {
        struct liftoff_device *device;
        struct liftoff_plane *plane;
        struct liftoff_layer *layer;
        struct alloc_result result = {0};
        struct alloc_step step = {0};
+       const struct liftoff_output_apply_options default_options = {0};
        size_t i, candidate_planes;
        int ret;
+       bool found_layer;
+
+       if (options == NULL) {
+               options = &default_options;
+       }
 
        device = output->device;
 
@@ -767,6 +974,7 @@
        ret = reuse_previous_alloc(output, req, flags);
        if (ret == 0) {
                log_reuse(output);
+               mark_layers_clean(output);
                return 0;
        }
        log_no_reuse(output);
@@ -808,20 +1016,30 @@
        result.flags = flags;
        result.planes_len = liftoff_list_length(&device->planes);
 
-       step.alloc = malloc(result.planes_len * sizeof(*step.alloc));
-       result.best = malloc(result.planes_len * sizeof(*result.best));
+       step.alloc = malloc(result.planes_len * sizeof(step.alloc[0]));
+       result.best = malloc(result.planes_len * sizeof(result.best[0]));
        if (step.alloc == NULL || result.best == NULL) {
                liftoff_log_errno(LIFTOFF_ERROR, "malloc");
                return -ENOMEM;
        }
 
+       if (clock_gettime(CLOCK_MONOTONIC, &result.started_at) != 0) {
+               liftoff_log_errno(LIFTOFF_ERROR, "clock_gettime");
+               return -errno;
+       }
+
+       result.timeout_ns = options->timeout_ns;
+       if (result.timeout_ns == 0) {
+               result.timeout_ns = DEFAULT_ALLOC_TIMEOUT_NSEC;
+       }
+
        /* For each plane, try to find a layer. Don't do it the other
         * way around (ie. for each layer, try to find a plane) because
         * some drivers want user-space to enable the primary plane
         * before any other plane. */
 
        result.best_score = -1;
-       memset(result.best, 0, result.planes_len * sizeof(*result.best));
+       memset(result.best, 0, result.planes_len * sizeof(result.best[0]));
        result.has_composition_layer = output->composition_layer != NULL;
        result.non_composition_layers_len =
                non_composition_layers_length(output);
@@ -838,12 +1056,14 @@
        }
 
        liftoff_log(LIFTOFF_DEBUG,
-                   "Found plane allocation for output %p (score: %d, candidate 
planes: %zu, tests: %d):",
-                   (void *)output, result.best_score, candidate_planes,
+                   "Found plane allocation for output %"PRIu32" "
+                   "(score: %d, candidate planes: %zu, tests: %d):",
+                   output->crtc_id, result.best_score, candidate_planes,
                    device->test_commit_counter);
 
        /* Apply the best allocation */
        i = 0;
+       found_layer = false;
        liftoff_list_for_each(plane, &device->planes, link) {
                layer = result.best[i];
                i++;
@@ -858,8 +1078,10 @@
                assert(layer->plane == NULL);
                plane->layer = layer;
                layer->plane = plane;
+
+               found_layer = true;
        }
-       if (i == 0) {
+       if (!found_layer) {
                liftoff_log(LIFTOFF_DEBUG, "  (No layer has a plane)");
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/device.c 
new/libliftoff-v0.5.0/device.c
--- old/libliftoff-v0.4.1/device.c      2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/device.c      2024-05-28 00:22:40.000000000 +0200
@@ -122,3 +122,36 @@
 
        return ret;
 }
+
+ssize_t
+core_property_index(const char *name)
+{
+       if (strcmp(name, "FB_ID") == 0) {
+               return LIFTOFF_PROP_FB_ID;
+       } else if (strcmp(name, "CRTC_ID") == 0) {
+               return LIFTOFF_PROP_CRTC_ID;
+       } else if (strcmp(name, "CRTC_X") == 0) {
+               return LIFTOFF_PROP_CRTC_X;
+       } else if (strcmp(name, "CRTC_Y") == 0) {
+               return LIFTOFF_PROP_CRTC_Y;
+       } else if (strcmp(name, "CRTC_W") == 0) {
+               return LIFTOFF_PROP_CRTC_W;
+       } else if (strcmp(name, "CRTC_H") == 0) {
+               return LIFTOFF_PROP_CRTC_H;
+       } else if (strcmp(name, "SRC_X") == 0) {
+               return LIFTOFF_PROP_SRC_X;
+       } else if (strcmp(name, "SRC_Y") == 0) {
+               return LIFTOFF_PROP_SRC_Y;
+       } else if (strcmp(name, "SRC_W") == 0) {
+               return LIFTOFF_PROP_SRC_W;
+       } else if (strcmp(name, "SRC_H") == 0) {
+               return LIFTOFF_PROP_SRC_H;
+       } else if (strcmp(name, "zpos") == 0) {
+               return LIFTOFF_PROP_ZPOS;
+       } else if (strcmp(name, "alpha") == 0) {
+               return LIFTOFF_PROP_ALPHA;
+       } else if (strcmp(name, "rotation") == 0) {
+               return LIFTOFF_PROP_ROTATION;
+       }
+       return -1;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/example/compositor.c 
new/libliftoff-v0.5.0/example/compositor.c
--- old/libliftoff-v0.4.1/example/compositor.c  2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/example/compositor.c  2024-05-28 00:22:40.000000000 
+0200
@@ -195,7 +195,7 @@
 
        flags = DRM_MODE_ATOMIC_NONBLOCK;
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, flags);
+       ret = liftoff_output_apply(output, req, flags, NULL);
        if (ret != 0) {
                perror("liftoff_output_apply");
                return 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/example/dynamic.c 
new/libliftoff-v0.5.0/example/dynamic.c
--- old/libliftoff-v0.4.1/example/dynamic.c     2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/example/dynamic.c     2024-05-28 00:22:40.000000000 
+0200
@@ -114,7 +114,7 @@
 
        flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, flags);
+       ret = liftoff_output_apply(output, req, flags, NULL);
        if (ret != 0) {
                perror("liftoff_output_apply");
                return false;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/example/multi-output.c 
new/libliftoff-v0.5.0/example/multi-output.c
--- old/libliftoff-v0.4.1/example/multi-output.c        2023-03-05 
19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/example/multi-output.c        2024-05-28 
00:22:40.000000000 +0200
@@ -157,7 +157,7 @@
        flags = DRM_MODE_ATOMIC_NONBLOCK;
        req = drmModeAtomicAlloc();
        for (i = 0; i < outputs_len; i++) {
-               ret = liftoff_output_apply(outputs[i], req, flags);
+               ret = liftoff_output_apply(outputs[i], req, flags, NULL);
                if (ret != 0) {
                        perror("liftoff_output_apply");
                        return 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/example/simple.c 
new/libliftoff-v0.5.0/example/simple.c
--- old/libliftoff-v0.4.1/example/simple.c      2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/example/simple.c      2024-05-28 00:22:40.000000000 
+0200
@@ -130,7 +130,7 @@
 
        flags = DRM_MODE_ATOMIC_NONBLOCK;
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, flags);
+       ret = liftoff_output_apply(output, req, flags, NULL);
        if (ret != 0) {
                perror("liftoff_output_apply");
                return 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/include/libliftoff.h 
new/libliftoff-v0.5.0/include/libliftoff.h
--- old/libliftoff-v0.4.1/include/libliftoff.h  2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/include/libliftoff.h  2024-05-28 00:22:40.000000000 
+0200
@@ -63,18 +63,28 @@
 liftoff_plane_get_id(struct liftoff_plane *plane);
 
 /**
+ * Options for liftoff_output_apply().
+ */
+struct liftoff_output_apply_options {
+       /* Timeout in nanoseconds. If zero, a default timeout is used. */
+       int64_t timeout_ns;
+};
+
+/**
  * Build a layer to plane mapping and append the plane configuration to req.
  *
  * Callers are expected to commit req afterwards and can figure out which
  * layers need composition via liftoff_layer_needs_composition().
  *
- * flags is the atomic commit flags the caller intends to use.
+ * flags is the atomic commit flags the caller intends to use. If options is
+ * NULL, defaults are used.
  *
  * Zero is returned on success, negative errno on error.
  */
 int
 liftoff_output_apply(struct liftoff_output *output, drmModeAtomicReq *req,
-                    uint32_t flags);
+                    uint32_t flags,
+                    const struct liftoff_output_apply_options *options);
 
 /**
  * Make the device manage a CRTC's planes.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/include/list.h 
new/libliftoff-v0.5.0/include/list.h
--- old/libliftoff-v0.4.1/include/list.h        2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/include/list.h        2024-05-28 00:22:40.000000000 
+0200
@@ -18,6 +18,9 @@
 void
 liftoff_list_remove(struct liftoff_list *elm);
 
+void
+liftoff_list_swap(struct liftoff_list *this, struct liftoff_list *other);
+
 size_t
 liftoff_list_length(const struct liftoff_list *list);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/include/private.h 
new/libliftoff-v0.5.0/include/private.h
--- old/libliftoff-v0.4.1/include/private.h     2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/include/private.h     2024-05-28 00:22:40.000000000 
+0200
@@ -2,6 +2,7 @@
 #define PRIVATE_H
 
 #include <libliftoff.h>
+#include <sys/types.h>
 #include "list.h"
 #include "log.h"
 
@@ -9,6 +10,28 @@
  * given number of page-flips */
 #define LIFTOFF_PRIORITY_PERIOD 60
 
+/**
+ * List of well-known KMS properties.
+ *
+ * Keep core_property_index() in sync.
+ */
+enum liftoff_core_property {
+       LIFTOFF_PROP_FB_ID,
+       LIFTOFF_PROP_CRTC_ID,
+       LIFTOFF_PROP_CRTC_X,
+       LIFTOFF_PROP_CRTC_Y,
+       LIFTOFF_PROP_CRTC_W,
+       LIFTOFF_PROP_CRTC_H,
+       LIFTOFF_PROP_SRC_X,
+       LIFTOFF_PROP_SRC_Y,
+       LIFTOFF_PROP_SRC_W,
+       LIFTOFF_PROP_SRC_H,
+       LIFTOFF_PROP_ZPOS,
+       LIFTOFF_PROP_ALPHA,
+       LIFTOFF_PROP_ROTATION,
+       LIFTOFF_PROP_LAST, /* keep last */
+};
+
 struct liftoff_device {
        int drm_fd;
 
@@ -45,6 +68,7 @@
 
        struct liftoff_layer_property *props;
        size_t props_len;
+       ssize_t core_props[LIFTOFF_PROP_LAST]; /* indices into the props array 
*/
 
        bool force_composition; /* FB needs to be composited */
 
@@ -62,6 +86,7 @@
 struct liftoff_layer_property {
        char name[DRM_PROP_NAME_LEN];
        uint64_t value, prev_value;
+       ssize_t core_index;
 };
 
 struct liftoff_plane {
@@ -69,12 +94,12 @@
        uint32_t possible_crtcs;
        uint32_t type;
        int zpos; /* greater values mean closer to the eye */
-       /* TODO: formats */
        struct liftoff_list link; /* liftoff_device.planes */
 
        drmModePropertyRes **props;
        size_t props_len;
        drmModePropertyBlobRes *in_formats_blob;
+       const drmModePropertyRes *core_props[LIFTOFF_PROP_LAST];
 
        struct liftoff_layer *layer;
 };
@@ -91,9 +116,18 @@
 struct liftoff_layer_property *
 layer_get_property(struct liftoff_layer *layer, const char *name);
 
+struct liftoff_layer_property *
+layer_get_core_property(struct liftoff_layer *layer, enum 
liftoff_core_property prop);
+
 void
 layer_get_rect(struct liftoff_layer *layer, struct liftoff_rect *rect);
 
+void
+layer_get_prev_rect(struct liftoff_layer *layer, struct liftoff_rect *rect);
+
+bool
+rect_intersects(struct liftoff_rect *a, struct liftoff_rect *b);
+
 bool
 layer_intersects(struct liftoff_layer *a, struct liftoff_layer *b);
 
@@ -129,4 +163,7 @@
 void
 output_log_layers(struct liftoff_output *output);
 
+ssize_t
+core_property_index(const char *name);
+
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/layer.c 
new/libliftoff-v0.5.0/layer.c
--- old/libliftoff-v0.4.1/layer.c       2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/layer.c       2024-05-28 00:22:40.000000000 +0200
@@ -10,6 +10,7 @@
 liftoff_layer_create(struct liftoff_output *output)
 {
        struct liftoff_layer *layer;
+       size_t i;
 
        layer = calloc(1, sizeof(*layer));
        if (layer == NULL) {
@@ -17,13 +18,16 @@
                return NULL;
        }
        layer->output = output;
-       layer->candidate_planes = calloc(sizeof(layer->candidate_planes[0]),
-                                        output->device->planes_cap);
+       layer->candidate_planes = calloc(output->device->planes_cap,
+                                        sizeof(layer->candidate_planes[0]));
        if (layer->candidate_planes == NULL) {
                liftoff_log_errno(LIFTOFF_ERROR, "calloc");
                free(layer);
                return NULL;
        }
+       for (i = 0; i < LIFTOFF_PROP_LAST; i++) {
+               layer->core_props[i] = -1;
+       }
        liftoff_list_insert(output->layers.prev, &layer->link);
        output->layers_changed = true;
        return layer;
@@ -50,10 +54,28 @@
 }
 
 struct liftoff_layer_property *
+layer_get_core_property(struct liftoff_layer *layer, enum 
liftoff_core_property prop)
+{
+       ssize_t i;
+
+       i = layer->core_props[prop];
+       if (i < 0) {
+               return NULL;
+       }
+       return &layer->props[i];
+}
+
+struct liftoff_layer_property *
 layer_get_property(struct liftoff_layer *layer, const char *name)
 {
+       ssize_t core_prop_idx;
        size_t i;
 
+       core_prop_idx = core_property_index(name);
+       if (core_prop_idx >= 0) {
+               return layer_get_core_property(layer, core_prop_idx);
+       }
+
        for (i = 0; i < layer->props_len; i++) {
                if (strcmp(layer->props[i].name, name) == 0) {
                        return &layer->props[i];
@@ -68,6 +90,7 @@
 {
        struct liftoff_layer_property *props;
        struct liftoff_layer_property *prop;
+       size_t i;
 
        if (strcmp(name, "CRTC_ID") == 0) {
                liftoff_log(LIFTOFF_ERROR,
@@ -77,8 +100,8 @@
 
        prop = layer_get_property(layer, name);
        if (prop == NULL) {
-               props = realloc(layer->props, (layer->props_len + 1)
-                               * sizeof(struct liftoff_layer_property));
+               props = realloc(layer->props,
+                               (layer->props_len + 1) * sizeof(props[0]));
                if (props == NULL) {
                        liftoff_log_errno(LIFTOFF_ERROR, "realloc");
                        return -ENOMEM;
@@ -86,16 +109,22 @@
                layer->props = props;
                layer->props_len++;
 
-               prop = &layer->props[layer->props_len - 1];
-               memset(prop, 0, sizeof(*prop));
+               i = layer->props_len - 1;
+               prop = &layer->props[i];
+               *prop = (struct liftoff_layer_property){0};
                strncpy(prop->name, name, sizeof(prop->name) - 1);
+               prop->core_index = core_property_index(name);
 
                layer->changed = true;
+
+               if (prop->core_index >= 0) {
+                       layer->core_props[prop->core_index] = (ssize_t)i;
+               }
        }
 
        prop->value = value;
 
-       if (strcmp(name, "FB_ID") == 0 && layer->force_composition) {
+       if (prop->core_index == LIFTOFF_PROP_FB_ID && layer->force_composition) 
{
                layer->force_composition = false;
                layer->changed = true;
        }
@@ -113,11 +142,18 @@
                return;
        }
 
+       if (prop->core_index >= 0) {
+               layer->core_props[prop->core_index] = -1;
+       }
+
        last = &layer->props[layer->props_len - 1];
        if (prop != last) {
                *prop = *last;
+               if (last->core_index >= 0) {
+                       layer->core_props[last->core_index] = prop - 
layer->props;
+               }
        }
-       memset(last, 0, sizeof(*last));
+       *last = (struct liftoff_layer_property){0};
        layer->props_len--;
 
        layer->changed = true;
@@ -156,10 +192,10 @@
 {
        struct liftoff_layer_property *x_prop, *y_prop, *w_prop, *h_prop;
 
-       x_prop = layer_get_property(layer, "CRTC_X");
-       y_prop = layer_get_property(layer, "CRTC_Y");
-       w_prop = layer_get_property(layer, "CRTC_W");
-       h_prop = layer_get_property(layer, "CRTC_H");
+       x_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_X);
+       y_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_Y);
+       w_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_W);
+       h_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_H);
 
        rect->x = x_prop != NULL ? x_prop->value : 0;
        rect->y = y_prop != NULL ? y_prop->value : 0;
@@ -167,6 +203,29 @@
        rect->height = h_prop != NULL ? h_prop->value : 0;
 }
 
+void
+layer_get_prev_rect(struct liftoff_layer *layer, struct liftoff_rect *rect)
+{
+       struct liftoff_layer_property *x_prop, *y_prop, *w_prop, *h_prop;
+
+       x_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_X);
+       y_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_Y);
+       w_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_W);
+       h_prop = layer_get_core_property(layer, LIFTOFF_PROP_CRTC_H);
+
+       rect->x = x_prop != NULL ? x_prop->prev_value : 0;
+       rect->y = y_prop != NULL ? y_prop->prev_value : 0;
+       rect->width = w_prop != NULL ? w_prop->prev_value : 0;
+       rect->height = h_prop != NULL ? h_prop -> prev_value : 0;
+}
+
+bool
+rect_intersects(struct liftoff_rect *ra, struct liftoff_rect *rb)
+{
+       return ra->x < rb->x + rb->width && ra->y < rb->y + rb->height &&
+              ra->x + ra->width > rb->x && ra->y + ra->height > rb->y;
+}
+
 bool
 layer_intersects(struct liftoff_layer *a, struct liftoff_layer *b)
 {
@@ -179,8 +238,7 @@
        layer_get_rect(a, &ra);
        layer_get_rect(b, &rb);
 
-       return ra.x < rb.x + rb.width && ra.y < rb.y + rb.height &&
-              ra.x + ra.width > rb.x && ra.y + ra.height > rb.y;
+       return rect_intersects(&ra, &rb);
 }
 
 void
@@ -214,7 +272,7 @@
        struct liftoff_layer_property *prop;
 
        /* TODO: also bump priority when updating other properties */
-       prop = layer_get_property(layer, "FB_ID");
+       prop = layer_get_core_property(layer, LIFTOFF_PROP_FB_ID);
        if (prop != NULL && prop->prev_value != prop->value) {
                layer->pending_priority++;
        }
@@ -231,7 +289,7 @@
 {
        struct liftoff_layer_property *fb_id_prop;
 
-       fb_id_prop = layer_get_property(layer, "FB_ID");
+       fb_id_prop = layer_get_core_property(layer, LIFTOFF_PROP_FB_ID);
        return fb_id_prop != NULL && fb_id_prop->value != 0;
 }
 
@@ -240,7 +298,7 @@
 {
        struct liftoff_layer_property *alpha_prop;
 
-       alpha_prop = layer_get_property(layer, "alpha");
+       alpha_prop = layer_get_core_property(layer, LIFTOFF_PROP_ALPHA);
        if (alpha_prop != NULL && alpha_prop->value == 0) {
                return false; /* fully transparent */
        }
@@ -260,9 +318,9 @@
        size_t i, j, num_planes;
        int ret;
 
-       fb_id_prop = layer_get_property(layer, "FB_ID");
+       fb_id_prop = layer_get_core_property(layer, LIFTOFF_PROP_FB_ID);
        if (fb_id_prop == NULL || fb_id_prop->value == 0) {
-               memset(&layer->fb_info, 0, sizeof(layer->fb_info));
+               layer->fb_info = (drmModeFB2){0};
                return 0;
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/list.c new/libliftoff-v0.5.0/list.c
--- old/libliftoff-v0.4.1/list.c        2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/list.c        2024-05-28 00:22:40.000000000 +0200
@@ -25,6 +25,19 @@
        elm->prev = NULL;
 }
 
+void
+liftoff_list_swap(struct liftoff_list *this, struct liftoff_list *other)
+{
+       struct liftoff_list tmp;
+
+       liftoff_list_insert(other, &tmp);
+       liftoff_list_remove(other);
+       liftoff_list_insert(this, other);
+       liftoff_list_remove(this);
+       liftoff_list_insert(&tmp, this);
+       liftoff_list_remove(&tmp);
+}
+
 size_t
 liftoff_list_length(const struct liftoff_list *list)
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/meson.build 
new/libliftoff-v0.5.0/meson.build
--- old/libliftoff-v0.4.1/meson.build   2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/meson.build   2024-05-28 00:22:40.000000000 +0200
@@ -1,7 +1,7 @@
 project(
        'libliftoff',
        'c',
-       version: '0.4.1',
+       version: '0.5.0',
        license: 'MIT',
        meson_version: '>=0.52.0',
        default_options: [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/output.c 
new/libliftoff-v0.5.0/output.c
--- old/libliftoff-v0.4.1/output.c      2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/output.c      2024-05-28 00:22:40.000000000 +0200
@@ -104,6 +104,9 @@
                                                   " (composition layer)" : "");
                }
 
+               liftoff_log(LIFTOFF_DEBUG, "    Priority = %"PRIi32,
+                           layer->current_priority);
+
                for (i = 0; i < layer->props_len; i++) {
                        char *name = layer->props[i].name;
                        uint64_t value = layer->props[i].value;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/plane.c 
new/libliftoff-v0.5.0/plane.c
--- old/libliftoff-v0.4.1/plane.c       2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/plane.c       2024-05-28 00:22:40.000000000 +0200
@@ -43,6 +43,7 @@
        drmModePropertyRes *prop;
        uint64_t value;
        bool has_type = false, has_zpos = false;
+       ssize_t core_prop_idx;
 
        liftoff_list_for_each(plane, &device->planes, link) {
                if (plane->id == id) {
@@ -105,6 +106,11 @@
                                return NULL;
                        }
                }
+
+               core_prop_idx = core_property_index(prop->name);
+               if (core_prop_idx >= 0) {
+                       plane->core_props[core_prop_idx] = prop;
+               }
        }
        drmModeFreeObjectProperties(drm_props);
 
@@ -173,12 +179,16 @@
 }
 
 static const drmModePropertyRes *
-plane_get_property(struct liftoff_plane *plane, const char *name)
+plane_get_property(struct liftoff_plane *plane,
+                  const struct liftoff_layer_property *layer_prop)
 {
        size_t i;
 
+       if (layer_prop->core_index >= 0)
+               return plane->core_props[layer_prop->core_index];
+
        for (i = 0; i < plane->props_len; i++) {
-               if (strcmp(plane->props[i]->name, name) == 0) {
+               if (strcmp(plane->props[i]->name, layer_prop->name) == 0) {
                        return plane->props[i];
                }
        }
@@ -276,16 +286,16 @@
 }
 
 static int
-set_plane_prop_str(struct liftoff_plane *plane, drmModeAtomicReq *req,
-                  const char *name, uint64_t value)
+set_plane_core_prop(struct liftoff_plane *plane, drmModeAtomicReq *req,
+                   enum liftoff_core_property core_prop, uint64_t value)
 {
        const drmModePropertyRes *prop;
 
-       prop = plane_get_property(plane, name);
+       prop = plane->core_props[core_prop];
        if (prop == NULL) {
                liftoff_log(LIFTOFF_DEBUG,
-                           "plane %"PRIu32" is missing the %s property",
-                           plane->id, name);
+                           "plane %"PRIu32" is missing core property %d",
+                           plane->id, core_prop);
                return -EINVAL;
        }
 
@@ -355,33 +365,34 @@
        cursor = drmModeAtomicGetCursor(req);
 
        if (layer == NULL) {
-               ret = set_plane_prop_str(plane, req, "FB_ID", 0);
+               ret = set_plane_core_prop(plane, req, LIFTOFF_PROP_FB_ID, 0);
                if (ret != 0) {
                        return ret;
                }
-               return set_plane_prop_str(plane, req, "CRTC_ID", 0);
+               return set_plane_core_prop(plane, req, LIFTOFF_PROP_CRTC_ID, 0);
        }
 
-       ret = set_plane_prop_str(plane, req, "CRTC_ID", layer->output->crtc_id);
+       ret = set_plane_core_prop(plane, req, LIFTOFF_PROP_CRTC_ID,
+                                 layer->output->crtc_id);
        if (ret != 0) {
                return ret;
        }
 
        for (i = 0; i < layer->props_len; i++) {
                layer_prop = &layer->props[i];
-               if (strcmp(layer_prop->name, "zpos") == 0) {
+               if (layer_prop->core_index == LIFTOFF_PROP_ZPOS) {
                        /* We don't yet support setting the zpos property. We
                         * only use it (read-only) during plane allocation. */
                        continue;
                }
 
-               plane_prop = plane_get_property(plane, layer_prop->name);
+               plane_prop = plane_get_property(plane, layer_prop);
                if (plane_prop == NULL) {
-                       if (strcmp(layer_prop->name, "alpha") == 0 &&
+                       if (layer_prop->core_index == LIFTOFF_PROP_ALPHA &&
                            layer_prop->value == 0xFFFF) {
                                continue; /* Layer is completely opaque */
                        }
-                       if (strcmp(layer_prop->name, "rotation") == 0 &&
+                       if (layer_prop->core_index == LIFTOFF_PROP_ROTATION &&
                            layer_prop->value == DRM_MODE_ROTATE_0) {
                                continue; /* Layer isn't rotated */
                        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/bench.c 
new/libliftoff-v0.5.0/test/bench.c
--- old/libliftoff-v0.4.1/test/bench.c  2023-03-05 19:36:15.000000000 +0100
+++ new/libliftoff-v0.5.0/test/bench.c  2024-05-28 00:22:40.000000000 +0200
@@ -1,6 +1,7 @@
 #define _POSIX_C_SOURCE 200112L
 #include <assert.h>
 #include <libliftoff.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
@@ -67,6 +68,7 @@
        }
 
        liftoff_log_set_priority(LIFTOFF_SILENT);
+       liftoff_mock_verbose = false;
 
        for (i = 0; i < planes_len; i++) {
                plane_type = i == 0 ? DRM_PLANE_TYPE_PRIMARY :
@@ -103,7 +105,9 @@
        clock_gettime(CLOCK_MONOTONIC, &start);
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, 0);
+       ret = liftoff_output_apply(output, req, 0, &(struct 
liftoff_output_apply_options){
+               .timeout_ns = INT64_MAX,
+       });
        assert(ret == 0);
        drmModeAtomicFree(req);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/libdrm_mock.c 
new/libliftoff-v0.5.0/test/libdrm_mock.c
--- old/libliftoff-v0.4.1/test/libdrm_mock.c    2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/libdrm_mock.c    2024-05-28 00:22:40.000000000 
+0200
@@ -20,6 +20,7 @@
 uint32_t liftoff_mock_drm_crtc_id = DRM_MODE_OBJECT_CRTC & OBJ_TYPE_MASK;
 size_t liftoff_mock_commit_count = 0;
 bool liftoff_mock_require_primary_plane = false;
+bool liftoff_mock_verbose = true;
 
 struct liftoff_mock_plane {
        uint32_t id;
@@ -347,6 +348,20 @@
 }
 
 static void
+debug(const char *fmt, ...)
+{
+       va_list args;
+
+       if (!liftoff_mock_verbose) {
+               return;
+       }
+
+       va_start(args, fmt);
+       vfprintf(stderr, fmt, args);
+       va_end(args);
+}
+
+static void
 apply_atomic_req(drmModeAtomicReq *req)
 {
        int i;
@@ -359,9 +374,8 @@
                plane = liftoff_mock_drm_get_plane(prop->obj_id);
                prop_index = get_prop_index(prop->prop_id);
                plane->prop_values[prop_index] = prop->value;
-               fprintf(stderr, "libdrm_mock: plane %"PRIu32": "
-                       "setting %s = %"PRIu64"\n", plane->id,
-                       plane_props[prop_index].name, prop->value);
+               debug("libdrm_mock: plane %"PRIu32": setting %s = %"PRIu64"\n",
+                     plane->id, plane_props[prop_index].name, prop->value);
        }
 }
 
@@ -401,23 +415,23 @@
                has_crtc = crtc_id != 0;
 
                if (has_fb != has_crtc) {
-                       fprintf(stderr, "libdrm_mock: plane %u: both FB_ID and "
-                               "CRTC_ID must be set or unset together "
-                               "(FB_ID = %"PRIu64", CRTC_ID = %"PRIu64")\n",
-                               plane->id, fb_id, crtc_id);
+                       debug("libdrm_mock: plane %u: both FB_ID and CRTC_ID "
+                             "must be set or unset together "
+                             "(FB_ID = %"PRIu64", CRTC_ID = %"PRIu64")\n",
+                             plane->id, fb_id, crtc_id);
                        return -EINVAL;
                }
 
                if (has_fb) {
                        if (crtc_id != liftoff_mock_drm_crtc_id) {
-                               fprintf(stderr, "libdrm_mock: plane %u: "
-                                       "invalid CRTC_ID\n", plane->id);
+                               debug("libdrm_mock: plane %u: invalid 
CRTC_ID\n",
+                                     plane->id);
                                return -EINVAL;
                        }
                        layer = mock_fb_get_layer(fb_id);
                        if (layer == NULL) {
-                               fprintf(stderr, "libdrm_mock: plane %u: "
-                                       "invalid FB_ID\n", plane->id);
+                               debug("libdrm_mock: plane %u: invalid FB_ID\n",
+                                     plane->id);
                                return -EINVAL;
                        }
                        found = false;
@@ -428,9 +442,9 @@
                                }
                        }
                        if (!found) {
-                               fprintf(stderr, "libdrm_mock: plane %u: "
-                                       "layer %p is not compatible\n",
-                                       plane->id, (void *)layer);
+                               debug("libdrm_mock: plane %u: "
+                                     "layer %p is not compatible\n",
+                                     plane->id, (void *)layer);
                                return -EINVAL;
                        }
 
@@ -443,8 +457,8 @@
 
        if (liftoff_mock_require_primary_plane && any_plane_enabled &&
            !primary_plane_enabled) {
-               fprintf(stderr, "libdrm_mock: cannot light up CRTC without "
-                       "enabling the primary plane\n");
+               debug("libdrm_mock: cannot light up CRTC without enabling the "
+                     "primary plane\n");
                return -EINVAL;
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/libdrm_mock.h 
new/libliftoff-v0.5.0/test/libdrm_mock.h
--- old/libliftoff-v0.4.1/test/libdrm_mock.h    2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/libdrm_mock.h    2024-05-28 00:22:40.000000000 
+0200
@@ -8,6 +8,7 @@
 
 extern uint32_t liftoff_mock_drm_crtc_id;
 extern size_t liftoff_mock_commit_count;
+extern bool liftoff_mock_verbose;
 
 /**
  * Some drivers require the primary plane to be enabled in order to light up a
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/meson.build 
new/libliftoff-v0.5.0/test/meson.build
--- old/libliftoff-v0.4.1/test/meson.build      2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/meson.build      2024-05-28 00:22:40.000000000 
+0200
@@ -63,13 +63,15 @@
                'change-alpha',
                'set-alpha-from-opaque',
                'set-alpha-from-transparent',
+               'change-position-same-intersection',
+               'change-position-different-intersection',
                'unset-alpha-to-opaque',
                'unset-alpha-to-transparent',
                'change-in-fence-fd',
                'change-fb-damage-clips',
        ],
        'priority': [
-               #'basic',
+               'basic',
        ],
        'prop': [
                'default-alpha',
@@ -79,6 +81,7 @@
                'unmatched',
                'unset',
                'in-formats',
+               'fb-damage-clips',
                'range',
                'signed-range',
                'enum',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_alloc.c 
new/libliftoff-v0.5.0/test/test_alloc.c
--- old/libliftoff-v0.4.1/test/test_alloc.c     2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/test_alloc.c     2024-05-28 00:22:40.000000000 
+0200
@@ -803,7 +803,7 @@
        }
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, 0);
+       ret = liftoff_output_apply(output, req, 0, NULL);
        assert(ret == 0);
        ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
        assert(ret == 0);
@@ -875,7 +875,7 @@
        liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, 0);
+       ret = liftoff_output_apply(output, req, 0, NULL);
        assert(ret == 0);
        ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
        assert(ret == 0);
@@ -913,7 +913,7 @@
        liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, 0);
+       ret = liftoff_output_apply(output, req, 0, NULL);
        assert(ret == 0);
        ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
        assert(ret == 0);
@@ -959,7 +959,7 @@
        liftoff_mock_plane_add_compatible_layer(mock_plane, layer_with_fb);
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, 0);
+       ret = liftoff_output_apply(output, req, 0, NULL);
        assert(ret == 0);
        ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
        assert(ret == 0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_candidate.c 
new/libliftoff-v0.5.0/test/test_candidate.c
--- old/libliftoff-v0.4.1/test/test_candidate.c 2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/test_candidate.c 2024-05-28 00:22:40.000000000 
+0200
@@ -60,7 +60,7 @@
        liftoff_layer_set_property(layer, "COLOR_RANGE", 0);
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, 0);
+       ret = liftoff_output_apply(output, req, 0, NULL);
        assert(ret == 0);
        ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
        assert(ret == 0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_dynamic.c 
new/libliftoff-v0.5.0/test/test_dynamic.c
--- old/libliftoff-v0.4.1/test/test_dynamic.c   2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/test_dynamic.c   2024-05-28 00:22:40.000000000 
+0200
@@ -50,7 +50,7 @@
        assert(ctx->commit_count == 0);
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(ctx->output, req, 0);
+       ret = liftoff_output_apply(ctx->output, req, 0, NULL);
        assert(ret == 0);
        ret = drmModeAtomicCommit(ctx->drm_fd, req, 0, NULL);
        assert(ret == 0);
@@ -72,7 +72,7 @@
        int ret;
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(ctx->output, req, 0);
+       ret = liftoff_output_apply(ctx->output, req, 0, NULL);
        assert(ret == 0);
        if (want_reuse_prev_alloc) {
                /* The library should perform only one TEST_ONLY commit with the
@@ -295,6 +295,33 @@
 }
 
 static void
+run_change_position_same_intersection(struct context *ctx)
+{
+
+       first_commit(ctx);
+       assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer);
+
+       liftoff_layer_set_property(ctx->other_layer, "CRTC_X", 1);
+
+       second_commit(ctx, true);
+       assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer);
+}
+
+static void
+run_change_position_different_intersection(struct context *ctx)
+{
+
+       first_commit(ctx);
+       assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer);
+
+       liftoff_layer_set_property(ctx->other_layer, "CRTC_X", 2000);
+       liftoff_layer_set_property(ctx->other_layer, "CRTC_Y", 2000);
+
+       second_commit(ctx, false);
+       assert(liftoff_mock_plane_get_layer(ctx->mock_plane) == ctx->layer);
+}
+
+static void
 run_change_in_fence_fd(struct context *ctx)
 {
        liftoff_layer_set_property(ctx->layer, "IN_FENCE_FD", 42);
@@ -334,6 +361,8 @@
        { .name = "change-alpha", .run = run_change_alpha },
        { .name = "set-alpha-from-opaque", .run = run_set_alpha_from_opaque },
        { .name = "set-alpha-from-transparent", .run = 
run_set_alpha_from_transparent },
+       { .name = "change-position-same-intersection", .run = 
run_change_position_same_intersection },
+       { .name = "change-position-different-intersection", .run = 
run_change_position_different_intersection },
        { .name = "unset-alpha-to-opaque", .run = run_unset_alpha_to_opaque },
        { .name = "unset-alpha-to-transparent", .run = 
run_unset_alpha_to_transparent },
        { .name = "change-in-fence-fd", .run = run_change_in_fence_fd },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_priority.c 
new/libliftoff-v0.5.0/test/test_priority.c
--- old/libliftoff-v0.4.1/test/test_priority.c  2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/test_priority.c  2024-05-28 00:22:40.000000000 
+0200
@@ -76,7 +76,7 @@
 
                        liftoff_layer_set_property(layer, "FB_ID", fbs[j % 2]);
 
-                       ret = liftoff_output_apply(output, req, 0);
+                       ret = liftoff_output_apply(output, req, 0, NULL);
                        assert(ret == 0);
                        ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
                        assert(ret == 0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libliftoff-v0.4.1/test/test_prop.c 
new/libliftoff-v0.5.0/test/test_prop.c
--- old/libliftoff-v0.4.1/test/test_prop.c      2023-03-05 19:36:15.000000000 
+0100
+++ new/libliftoff-v0.5.0/test/test_prop.c      2024-05-28 00:22:40.000000000 
+0200
@@ -34,7 +34,7 @@
        int ret;
 
        req = drmModeAtomicAlloc();
-       ret = liftoff_output_apply(output, req, 0);
+       ret = liftoff_output_apply(output, req, 0, NULL);
        assert(ret == 0);
        ret = drmModeAtomicCommit(drm_fd, req, 0, NULL);
        assert(ret == 0);
@@ -277,6 +277,38 @@
        return 0;
 }
 
+static int
+test_ignored_prop(const char *name)
+{
+       struct liftoff_mock_plane *mock_plane;
+       int drm_fd;
+       struct liftoff_device *device;
+       struct liftoff_output *output;
+       struct liftoff_layer *layer;
+
+       mock_plane = liftoff_mock_drm_create_plane(DRM_PLANE_TYPE_PRIMARY);
+
+       drm_fd = liftoff_mock_drm_open();
+       device = liftoff_device_create(drm_fd);
+       assert(device != NULL);
+
+       liftoff_device_register_all_planes(device);
+
+       output = liftoff_output_create(device, liftoff_mock_drm_crtc_id);
+       layer = add_layer(output, 0, 0, 1920, 1080);
+       liftoff_layer_set_property(layer, name, 0);
+
+       liftoff_mock_plane_add_compatible_layer(mock_plane, layer);
+
+       commit(drm_fd, output);
+       assert(liftoff_mock_plane_get_layer(mock_plane) == layer);
+
+       liftoff_device_destroy(device);
+       close(drm_fd);
+
+       return 0;
+}
+
 struct single_format_modifier_blob {
        struct drm_format_modifier_blob base;
        uint32_t formats[1];
@@ -497,6 +529,8 @@
                return test_unset_prop();
        } else if (strcmp(test_name, "in-formats") == 0) {
                return test_in_formats();
+       } else if (strcmp(test_name, "fb-damage-clips") == 0) {
+               return test_ignored_prop("FB_DAMAGE_CLIPS");
        } else if (strcmp(test_name, "range") == 0) {
                return test_range();
        } else if (strcmp(test_name, "signed-range") == 0) {

Reply via email to