This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch wl/browser-all
in repository enlightenment.

View the commit online.

commit 7ed15ec54dfa911eb807f364b3bdd31290c28353
Author: Cedric BAIL <[email protected]>
AuthorDate: Sun Aug 16 21:01:02 2026 -0600

    e_comp_wl - implement zwp_idle_inhibit_manager_v1
    
    "Do not blank the screen while this is playing." A browser takes an
    inhibitor for the surface showing video and drops it when playback stops.
    Without the protocol E blanks over the top of a film.
    
    Implementing it turned up the reason the existing inhibit path could not
    have carried it. e_comp_wl_notidle() runs on every input event and armed
    a fresh idle timer whether or not anything had asked it not to, so an
    inhibit survived only as long as nobody touched the machine - it worked
    in exactly the case that did not need it and lapsed the moment anyone
    moved the mouse. The timer is no longer re-armed while something is
    holding the screen awake.
    
    Inhibitors are counted rather than folded into saver_inhibit. That flag
    is a boolean two unrelated callers already share - a drag in progress
    and, on XWayland, a selection owner changing - and they are not paired
    with each other, so refcounting it would have gone wrong the first time
    they overlapped.
    
    The spec says an inhibitor applies only while its surface is visible.
    That is deliberately not implemented: E would have to watch iconify, desk
    switches and occlusion for every inhibited surface, and the failure mode
    of ignoring it is a screen that stays awake when it could have slept
    rather than one that sleeps during a film. Browsers drop the inhibitor
    themselves when a tab is hidden.
    
    wlcs has no test for this protocol, and the object has no events and no
    readable state, so an inhibitor that works and one that is accepted and
    quietly dropped look identical from outside. wl_test grows a back door
    that reports whether the screen will actually stay on - not whether an
    inhibitor exists, which would answer yes in both cases - and a way to
    switch the screensaver on, because the test compositor comes up with it
    disabled and every assertion would otherwise pass whatever E did.
    
    The harness needed one more thing to see any of this. _pointer_motion
    already reproduces two of the things E's ecore mouse handler does,
    because the evas half alone left pointer-constraints and relative-pointer
    untestable. The idle timer hangs off that same half and was the third,
    so the harness could move the pointer all day and E never counted it as
    activity. Verified by reverting the notidle change and watching the test
    fail on exactly that assertion.
    
    wl-test.xml goes to version 4. Both new requests are appended: opcodes
    are positional, and inserting one renumbers every request after it.
---
 src/bin/e_comp_wl.c                   |  52 +++++++
 src/bin/e_comp_wl.h                   |   7 +
 src/bin/e_comp_wl_extensions.c        |  70 +++++++++
 src/bin/generated/meson.build         |   1 +
 src/modules/wl_test/e_mod_main.c      |  28 +++-
 src/protocol/wl-test.xml              |  40 +++++-
 src/tests/wayland/globals.expected    |   1 +
 src/tests/wayland/meson.build         |   2 +
 src/tests/wayland/test_idle_inhibit.c | 258 ++++++++++++++++++++++++++++++++++
 9 files changed, 457 insertions(+), 2 deletions(-)

diff --git a/src/bin/e_comp_wl.c b/src/bin/e_comp_wl.c
index c204cf9bf..89e9da747 100644
--- a/src/bin/e_comp_wl.c
+++ b/src/bin/e_comp_wl.c
@@ -5213,6 +5213,12 @@ e_comp_wl_grab_client_mouse_button(const Ecore_Event_Mouse_Button *ev)
 }
 
 static Eina_Bool saver_inhibit = EINA_FALSE;
+/* Live zwp_idle_inhibitor_v1 objects. Counted separately from saver_inhibit
+ * rather than folded into it: that flag is a boolean two unrelated callers
+ * already share - a drag in progress and, on XWayland, a selection owner
+ * changing - and neither of them is paired with the other, so refcounting it
+ * would go wrong the first time they overlapped. */
+static int saver_idle_inhibitors = 0;
 static Eina_Bool saver_on = EINA_FALSE;
 static Ecore_Timer *screensaver_eval_timer = NULL;
 static Ecore_Timer *screensaver_idle_timer = NULL;
@@ -5320,6 +5326,13 @@ _e_comp_wl_screensaver_off()
    return ECORE_CALLBACK_RENEW;
 }
 
+/* Is anything currently asking the screen to stay awake? */
+static Eina_Bool
+_e_comp_wl_saver_inhibited(void)
+{
+   return saver_inhibit || (saver_idle_inhibitors > 0);
+}
+
 E_API void
 e_comp_wl_notidle(void)
 {
@@ -5333,12 +5346,51 @@ e_comp_wl_notidle(void)
           (0.3, _e_comp_wl_screensaver_eval_cb, NULL);
      }
    E_FREE_FUNC(screensaver_idle_timer, ecore_timer_del);
+
+   /* Every input event lands here, and until now every one of them armed a
+    * fresh idle timer whether or not something had asked us not to. An
+    * inhibit therefore survived only for as long as the user kept their hands
+    * off - which is to say it worked in exactly the case where nothing needed
+    * it, and lapsed the moment anyone touched the machine. */
+   if (_e_comp_wl_saver_inhibited()) return;
+
    timeout = e_screensaver_timeout_get(EINA_TRUE);
    if (timeout > 0)
      screensaver_idle_timer = ecore_timer_add
        (timeout, _e_comp_wl_screensaver_idle_cb, NULL);
 }
 
+/* zwp_idle_inhibit_manager_v1, one call per live inhibitor. */
+E_API void
+e_comp_wl_idle_inhibit_add(void)
+{
+   saver_idle_inhibitors++;
+   if (saver_idle_inhibitors == 1)
+     E_FREE_FUNC(screensaver_idle_timer, ecore_timer_del);
+}
+
+E_API void
+e_comp_wl_idle_inhibit_del(void)
+{
+   if (saver_idle_inhibitors <= 0) return;
+   saver_idle_inhibitors--;
+   /* The last one going away is as good as activity: start the clock again
+    * rather than leaving the screen awake until something else happens. */
+   if (saver_idle_inhibitors == 0) e_comp_wl_notidle();
+}
+
+E_API Eina_Bool
+e_comp_wl_idle_inhibited_get(void)
+{
+   /* Not "has anyone asked" but "will the screen actually stay on". An
+    * inhibit with an idle timer still ticking underneath it is not an
+    * inhibit, and telling the two apart is the entire point of reporting
+    * this - asking whether an inhibitor exists would answer yes in both
+    * cases. */
+   if (!_e_comp_wl_saver_inhibited()) return EINA_FALSE;
+   return !screensaver_idle_timer;
+}
+
 E_API void
 e_comp_wl_screensaver_activate(void)
 {
diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h
index a4d5083c4..e959505b2 100644
--- a/src/bin/e_comp_wl.h
+++ b/src/bin/e_comp_wl.h
@@ -186,6 +186,10 @@ typedef struct E_Comp_Wl_Extension_Data
      {
         struct wl_global *global;
      } wp_fractional_scale_manager_v1;
+   struct
+     {
+        struct wl_global *global;
+     } zwp_idle_inhibit_manager_v1;
    struct
      {
         struct wl_global *global;
@@ -571,6 +575,9 @@ E_API void e_comp_wl_extension_action_route_pid_allowed_set(uint32_t pid, Eina_B
 E_API const void *e_comp_wl_extension_action_route_interface_get(int *version);
 
 E_API void e_comp_wl_notidle(void);
+E_API void e_comp_wl_idle_inhibit_add(void);
+E_API void e_comp_wl_idle_inhibit_del(void);
+E_API Eina_Bool e_comp_wl_idle_inhibited_get(void);
 E_API void e_comp_wl_screensaver_activate(void);
 E_API void e_comp_wl_screensaver_inhibit(Eina_Bool inhibit);
 
diff --git a/src/bin/e_comp_wl_extensions.c b/src/bin/e_comp_wl_extensions.c
index f8f687ed8..58ee0ae47 100644
--- a/src/bin/e_comp_wl_extensions.c
+++ b/src/bin/e_comp_wl_extensions.c
@@ -11,6 +11,7 @@
 #include "viewporter-server-protocol.h"
 #include "xdg-output-unstable-v1-server-protocol.h"
 #include "fractional-scale-v1-server-protocol.h"
+#include "idle-inhibit-unstable-v1-server-protocol.h"
 
 /* mutter uses 32, seems reasonable */
 #define HANDLE_LEN 32
@@ -1569,6 +1570,73 @@ static const struct zxdg_output_manager_v1_interface _e_zxdg_output_manager_v1_i
    _e_zxdg_output_manager_v1_cb_xdg_output_get,
 };
 
+
+/* zwp_idle_inhibit_manager_v1.
+ *
+ * "Do not blank the screen while this is playing." A browser creates an
+ * inhibitor for the surface showing video and destroys it when playback stops,
+ * and without the protocol E blanks over the top of a film.
+ *
+ * The inhibitor carries no state of its own: it exists or it does not, and E
+ * only needs to know how many exist. The counting lives in e_comp_wl.c beside
+ * the idle timer it suppresses.
+ *
+ * The spec says an inhibitor applies only while its surface is visible. That
+ * is deliberately not implemented here: E would have to watch iconify, desk
+ * switches and occlusion for every inhibited surface, and the failure mode of
+ * ignoring it is a screen that stays awake when it could have slept, rather
+ * than one that sleeps during a film. Browsers drop the inhibitor themselves
+ * when a tab is hidden or playback ends, so the case mostly does not arise.
+ * Gating on visibility is the obvious next step if it ever does. */
+
+static void
+_e_zwp_idle_inhibitor_v1_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static const struct zwp_idle_inhibitor_v1_interface _e_zwp_idle_inhibitor_v1_interface =
+{
+   _e_zwp_idle_inhibitor_v1_cb_destroy,
+};
+
+/* called by wl_resource_destroy, including when the client simply goes away */
+static void
+_e_zwp_idle_inhibitor_v1_res_destroy(struct wl_resource *resource EINA_UNUSED)
+{
+   e_comp_wl_idle_inhibit_del();
+}
+
+static void
+_e_zwp_idle_inhibit_manager_v1_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_resource_destroy(resource);
+}
+
+static void
+_e_zwp_idle_inhibit_manager_v1_cb_inhibitor_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface EINA_UNUSED)
+{
+   struct wl_resource *res;
+
+   res = wl_resource_create(client, &zwp_idle_inhibitor_v1_interface,
+                            wl_resource_get_version(resource), id);
+   if (!res)
+     {
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(res, &_e_zwp_idle_inhibitor_v1_interface,
+                                  NULL, _e_zwp_idle_inhibitor_v1_res_destroy);
+   e_comp_wl_idle_inhibit_add();
+}
+
+static const struct zwp_idle_inhibit_manager_v1_interface _e_zwp_idle_inhibit_manager_v1_interface =
+{
+   _e_zwp_idle_inhibit_manager_v1_cb_destroy,
+   _e_zwp_idle_inhibit_manager_v1_cb_inhibitor_create,
+};
+
 #define GLOBAL_BIND_CB(NAME, IFACE, ...) \
 static void \
 _e_comp_wl_##NAME##_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id) \
@@ -1595,6 +1663,7 @@ GLOBAL_BIND_CB(zwp_pointer_constraints_v1, zwp_pointer_constraints_v1_interface)
 GLOBAL_BIND_CB(xdg_activation_v1, xdg_activation_v1_interface)
 GLOBAL_BIND_CB(zxdg_output_manager_v1, zxdg_output_manager_v1_interface)
 GLOBAL_BIND_CB(wp_fractional_scale_manager_v1, wp_fractional_scale_manager_v1_interface)
+GLOBAL_BIND_CB(zwp_idle_inhibit_manager_v1, zwp_idle_inhibit_manager_v1_interface)
 GLOBAL_BIND_CB(action_route, action_route_interface,
      e_binding_key_list_cb = _action_route_key_list_cb;
      key_bindings = eina_hash_string_superfast_new(NULL);
@@ -1697,6 +1766,7 @@ e_comp_wl_extensions_init(void)
    GLOBAL_CREATE_OR_RETURN(wp_viewporter, wp_viewporter_interface, 1);
    GLOBAL_CREATE_OR_RETURN(zxdg_output_manager_v1, zxdg_output_manager_v1_interface, 3);
    GLOBAL_CREATE_OR_RETURN(wp_fractional_scale_manager_v1, wp_fractional_scale_manager_v1_interface, 1);
+   GLOBAL_CREATE_OR_RETURN(zwp_idle_inhibit_manager_v1, zwp_idle_inhibit_manager_v1_interface, 1);
 
    ecore_event_handler_add(ECORE_WL2_EVENT_SYNC_DONE, _dmabuf_add, NULL);
    ecore_event_handler_add(E_EVENT_CLIENT_ZONE_SET,
diff --git a/src/bin/generated/meson.build b/src/bin/generated/meson.build
index 0cbe69904..9999fe9a6 100644
--- a/src/bin/generated/meson.build
+++ b/src/bin/generated/meson.build
@@ -11,6 +11,7 @@ protos = [
   '@0@/unstable/xdg-output/xdg-output-unstable-v1.xml'.format(dir_wayland_protocols),
   '@0@/unstable/primary-selection/primary-selection-unstable-v1.xml'.format(dir_wayland_protocols),
   '@0@/staging/fractional-scale/fractional-scale-v1.xml'.format(dir_wayland_protocols),
+  '@0@/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'.format(dir_wayland_protocols),
 ]
 
 proto_c = []
diff --git a/src/modules/wl_test/e_mod_main.c b/src/modules/wl_test/e_mod_main.c
index 790e04f04..be9cbdee7 100644
--- a/src/modules/wl_test/e_mod_main.c
+++ b/src/modules/wl_test/e_mod_main.c
@@ -142,6 +142,22 @@ _wl_test_cb_zone_add(struct wl_client *client EINA_UNUSED, struct wl_resource *r
      ERR("wl_test: could not create wl_output for zone %d", num);
 }
 
+static void
+_wl_test_cb_screensaver_enable(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t enable, uint32_t timeout)
+{
+   e_config->screensaver_enable = !!enable;
+   e_config->screensaver_timeout = (int)timeout;
+   /* Re-arm from the new configuration, the way changing it in the settings
+    * dialog would. */
+   e_comp_wl_notidle();
+}
+
+static void
+_wl_test_cb_get_idle_inhibit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+   wl_test_send_idle_inhibit(resource, !!e_comp_wl_idle_inhibited_get());
+}
+
 static void
 _wl_test_cb_output_scale_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t index, int32_t scale)
 {
@@ -230,6 +246,13 @@ _pointer_motion(int x, int y)
           return;
      }
 
+   /* A third thing hangs off that same ecore half: the screensaver's idle
+    * timer, re-armed from _e_comp_wl_cb_mouse_move. Without this the harness
+    * can move the pointer all day and E never counts it as activity, so
+    * anything about idling - inhibitors above all - reads as working whether
+    * or not it does. */
+   e_comp_canvas_notidle();
+
    evas_event_feed_mouse_move(e_comp->evas, x, y, 0, NULL);
 }
 
@@ -368,6 +391,9 @@ static const struct wl_test_interface _wl_test_implementation =
    /* version 3, appended in the order the protocol declares them */
    _wl_test_cb_get_surface_buffer_info,
    _wl_test_cb_output_scale_set,
+   /* version 4 */
+   _wl_test_cb_get_idle_inhibit,
+   _wl_test_cb_screensaver_enable,
 };
 
 static void
@@ -407,7 +433,7 @@ e_modapi_init(E_Module *m)
    wl_display_add_client_created_listener(e_comp_wl->wl.disp,
                                           &_client_created_listener);
 
-   _wl_test_global = wl_global_create(e_comp_wl->wl.disp, &wl_test_interface, 3,
+   _wl_test_global = wl_global_create(e_comp_wl->wl.disp, &wl_test_interface, 4,
                                      NULL, _wl_test_cb_bind);
    if (!_wl_test_global)
      {
diff --git a/src/protocol/wl-test.xml b/src/protocol/wl-test.xml
index 36c52206b..2d05b5553 100644
--- a/src/protocol/wl-test.xml
+++ b/src/protocol/wl-test.xml
@@ -23,7 +23,7 @@
     DEALINGS IN THE SOFTWARE.
   </copyright>
 
-  <interface name="wl_test" version="3">
+  <interface name="wl_test" version="4">
     <description summary="private interface for the compositor test suite">
       A back door into the compositor for its own test suite. It exposes
       internal state and synthesises input, so it is deliberately NOT built
@@ -266,5 +266,43 @@
       <arg name="index" type="uint" summary="0-based, in wl_output creation order"/>
       <arg name="scale" type="int"/>
     </request>
+
+    <request name="get_idle_inhibit" since="4">
+      <description summary="is anything holding the screensaver off?">
+        zwp_idle_inhibit_manager_v1 has no events and no state a client can
+        read back, so from the outside an inhibitor that works and one that is
+        silently dropped look identical.
+
+        Reports whether the screen will actually stay on, not merely whether
+        an inhibitor exists: an inhibit with the idle timer still ticking
+        underneath it is not an inhibit, and a test that asked the weaker
+        question would pass in both cases.
+
+        Appended at the end of the interface, like everything before it:
+        opcodes are positional, so inserting a request in the middle renumbers
+        every one after it and silently rebinds the listener slots of every
+        test already written against this protocol.
+      </description>
+    </request>
+
+    <request name="screensaver_enable" since="4">
+      <description summary="turn the screensaver on and give it a timeout">
+        The test compositor comes up with the screensaver disabled, so no idle
+        timer is ever armed and anything that goes wrong with arming one is
+        invisible. A test that wants to prove an inhibit actually holds the
+        screen has to switch the screensaver on first, or it proves only that
+        nothing was going to blank anyway.
+
+        Use a long timeout: the point is that a timer exists, not that it
+        fires.
+      </description>
+      <arg name="enable" type="uint"/>
+      <arg name="timeout" type="uint" summary="seconds"/>
+    </request>
+
+    <event name="idle_inhibit" since="4">
+      <description summary="reply to get_idle_inhibit"/>
+      <arg name="inhibited" type="uint" summary="nonzero if the screen will not blank"/>
+    </event>
   </interface>
 </protocol>
diff --git a/src/tests/wayland/globals.expected b/src/tests/wayland/globals.expected
index 40ccb62e2..7bfc6230f 100644
--- a/src/tests/wayland/globals.expected
+++ b/src/tests/wayland/globals.expected
@@ -12,6 +12,7 @@ wp_viewporter	1
 xdg_activation_v1	1
 xdg_wm_base	6
 zwp_e_session_recovery	1
+zwp_idle_inhibit_manager_v1	1
 zwp_pointer_constraints_v1	1
 zwp_primary_selection_device_manager_v1	1
 zwp_relative_pointer_manager_v1	1
diff --git a/src/tests/wayland/meson.build b/src/tests/wayland/meson.build
index bf117ccd5..8cd8ead51 100644
--- a/src/tests/wayland/meson.build
+++ b/src/tests/wayland/meson.build
@@ -32,6 +32,7 @@ foreach p: [
   '@0@/stable/xdg-shell/xdg-shell.xml'.format(dir_wayland_protocols),
   '@0@/staging/xdg-activation/xdg-activation-v1.xml'.format(dir_wayland_protocols),
   '@0@/staging/fractional-scale/fractional-scale-v1.xml'.format(dir_wayland_protocols),
+  '@0@/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'.format(dir_wayland_protocols),
 ]
   test_proto_src += gen_scanner_client.process(p)
   test_proto_src += gen_scanner_impl.process(p)
@@ -58,6 +59,7 @@ wl_protocol_tests = [
   ['activation', 'test_activation.c'],
   ['buffer-scale', 'test_buffer_scale.c'],
   ['fractional-scale', 'test_fractional_scale.c'],
+  ['idle-inhibit', 'test_idle_inhibit.c'],
 ]
 
 foreach t: wl_protocol_tests
diff --git a/src/tests/wayland/test_idle_inhibit.c b/src/tests/wayland/test_idle_inhibit.c
new file mode 100644
index 000000000..3b0ea80a4
--- /dev/null
+++ b/src/tests/wayland/test_idle_inhibit.c
@@ -0,0 +1,258 @@
+/* Does an idle inhibitor actually stop the screen blanking?
+ *
+ * zwp_idle_inhibit_manager_v1 has no events and no state a client can read
+ * back. An inhibitor that works and one the compositor accepts and quietly
+ * drops look exactly the same from outside, and wlcs has no test for this
+ * protocol at all, so nothing anywhere would notice the difference. The wl_test
+ * back door reports what the compositor actually believes, which is the only
+ * way to tell the two apart.
+ *
+ * The case that matters is not "an inhibitor exists" but "an inhibitor exists
+ * and then somebody touches the mouse". Every input event runs through
+ * e_comp_wl_notidle(), which used to arm a fresh idle timer whether or not
+ * anything had asked it not to - so an inhibit held only while the machine was
+ * untouched, which is to say it worked in the one case where nothing needed it
+ * and lapsed the moment anyone moved. That is the third check below, and it is
+ * the whole reason this test exists.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <wayland-client.h>
+#include "wl-test-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
+#include "idle-inhibit-unstable-v1-client-protocol.h"
+
+#define W 200
+#define H 150
+
+#define FAIL(fmt, ...) \
+  do { fprintf(stderr, "test-idle-inhibit: " fmt "\n", ##__VA_ARGS__); return 1; } while (0)
+
+static struct wl_compositor *compositor;
+static struct wl_shm *shm;
+static struct xdg_wm_base *wm_base;
+static struct wl_test *tester;
+static struct zwp_idle_inhibit_manager_v1 *idle_manager;
+static struct wl_registry *reg;
+static struct wl_display *disp;
+static int sync_done, configured;
+static uint32_t tester_version;
+
+static int inhibited, inhibit_valid;
+
+static void
+_idle_inhibit(void *d, struct wl_test *t, uint32_t v)
+{
+   (void)d; (void)t;
+   inhibited = (int)v;
+   inhibit_valid = 1;
+}
+
+static void _wm_ping(void *d, struct xdg_wm_base *b, uint32_t serial)
+{ (void)d; xdg_wm_base_pong(b, serial); }
+static const struct xdg_wm_base_listener _wm_listener = { _wm_ping };
+
+static void _xdg_conf(void *d, struct xdg_surface *s, uint32_t serial)
+{ (void)d; xdg_surface_ack_configure(s, serial); configured = 1; }
+static const struct xdg_surface_listener _xdg_listener = { _xdg_conf };
+
+static void _top_conf(void *d, struct xdg_toplevel *t, int32_t w, int32_t h, struct wl_array *st)
+{ (void)d; (void)t; (void)w; (void)h; (void)st; }
+static void _top_close(void *d, struct xdg_toplevel *t) { (void)d; (void)t; }
+static const struct xdg_toplevel_listener _top_listener = { _top_conf, _top_close };
+
+static void _sync_done(void *d, struct wl_test *t) { (void)d; (void)t; sync_done = 1; }
+static void _client_serial(void *d, struct wl_test *t, uint32_t s) { (void)d; (void)t; (void)s; }
+static void _surface_info(void *d, struct wl_test *t, struct wl_surface *s, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t vis, uint32_t foc)
+{ (void)d; (void)t; (void)s; (void)x; (void)y; (void)w; (void)h; (void)vis; (void)foc; }
+static void _surface_unknown(void *d, struct wl_test *t, struct wl_surface *s)
+{ (void)d; (void)t; (void)s; }
+static void _surface_buffer_info(void *d, struct wl_test *t, struct wl_surface *s, int32_t bs, int32_t bt, int32_t os)
+{ (void)d; (void)t; (void)s; (void)bs; (void)bt; (void)os; }
+static const struct wl_test_listener _tester_listener =
+{
+   _client_serial, _surface_info, _surface_unknown, _sync_done,
+   _surface_buffer_info, _idle_inhibit
+};
+
+static void
+_global(void *data, struct wl_registry *r, uint32_t id, const char *iface, uint32_t ver)
+{
+   (void)data; (void)r;
+   if (!strcmp(iface, "wl_compositor"))
+     compositor = wl_registry_bind(reg, id, &wl_compositor_interface, 4);
+   else if (!strcmp(iface, "wl_shm"))
+     shm = wl_registry_bind(reg, id, &wl_shm_interface, 1);
+   else if (!strcmp(iface, "xdg_wm_base"))
+     {
+        wm_base = wl_registry_bind(reg, id, &xdg_wm_base_interface, 1);
+        xdg_wm_base_add_listener(wm_base, &_wm_listener, NULL);
+     }
+   else if (!strcmp(iface, "zwp_idle_inhibit_manager_v1"))
+     idle_manager = wl_registry_bind(reg, id,
+                                     &zwp_idle_inhibit_manager_v1_interface, 1);
+   else if (!strcmp(iface, "wl_test"))
+     {
+        tester_version = ver;
+        if (ver >= 4) tester = wl_registry_bind(reg, id, &wl_test_interface, 4);
+     }
+}
+
+static void _global_rm(void *d, struct wl_registry *r, uint32_t id) { (void)d; (void)r; (void)id; }
+static const struct wl_registry_listener _reg_listener = { _global, _global_rm };
+
+static int
+csync(void)
+{
+   sync_done = 0;
+   wl_test_sync(tester);
+   while (!sync_done)
+     if (wl_display_dispatch(disp) < 0) return -1;
+   return wl_display_roundtrip(disp) < 0 ? -1 : 0;
+}
+
+/* Ask the compositor whether anything is holding the screen awake. */
+static int
+ask_inhibited(void)
+{
+   inhibit_valid = 0;
+   wl_test_get_idle_inhibit(tester);
+   if (wl_display_roundtrip(disp) < 0) return -1;
+   if (!inhibit_valid) return -1;
+   return inhibited;
+}
+
+static struct wl_buffer *
+make_buffer(void)
+{
+   int fd, stride = W * 4, size = stride * H;
+   void *map;
+   struct wl_shm_pool *pool;
+   struct wl_buffer *buf;
+
+   fd = shm_open("/e-test-idle-shm", O_RDWR | O_CREAT | O_EXCL, 0600);
+   if (fd < 0) return NULL;
+   shm_unlink("/e-test-idle-shm");
+   if (ftruncate(fd, size) < 0) { close(fd); return NULL; }
+   map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+   if (map == MAP_FAILED) { close(fd); return NULL; }
+   memset(map, 0xff, size);
+   pool = wl_shm_create_pool(shm, fd, size);
+   buf = wl_shm_pool_create_buffer(pool, 0, W, H, stride, WL_SHM_FORMAT_ARGB8888);
+   wl_shm_pool_destroy(pool);
+   close(fd);
+   return buf;
+}
+
+int
+main(void)
+{
+   struct wl_surface *surface;
+   struct xdg_surface *xdg_surface;
+   struct xdg_toplevel *toplevel;
+   struct zwp_idle_inhibitor_v1 *inhibitor, *second;
+   struct wl_buffer *buffer;
+   int r;
+
+   disp = wl_display_connect(NULL);
+   if (!disp) FAIL("cannot connect to WAYLAND_DISPLAY=%s", getenv("WAYLAND_DISPLAY") ?: "(unset)");
+
+   reg = wl_display_get_registry(disp);
+   wl_registry_add_listener(reg, &_reg_listener, NULL);
+   if (wl_display_roundtrip(disp) < 0) FAIL("registry roundtrip failed");
+
+   if (!compositor || !shm || !wm_base) FAIL("missing core globals");
+   if (!tester)
+     FAIL("wl_test is version %u; this test needs 4 for get_idle_inhibit",
+          tester_version);
+   if (!idle_manager)
+     FAIL("zwp_idle_inhibit_manager_v1 is not advertised, so a browser cannot "
+          "stop the screen blanking over a video");
+   wl_test_add_listener(tester, &_tester_listener, NULL);
+
+   surface = wl_compositor_create_surface(compositor);
+   xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface);
+   xdg_surface_add_listener(xdg_surface, &_xdg_listener, NULL);
+   toplevel = xdg_surface_get_toplevel(xdg_surface);
+   xdg_toplevel_add_listener(toplevel, &_top_listener, NULL);
+   xdg_toplevel_set_title(toplevel, "idle-inhibit");
+   wl_surface_commit(surface);
+   while (!configured)
+     if (wl_display_dispatch(disp) < 0) FAIL("dispatch failed awaiting configure");
+
+   buffer = make_buffer();
+   if (!buffer) FAIL("could not create an shm buffer");
+   wl_surface_attach(surface, buffer, 0, 0);
+   wl_surface_damage(surface, 0, 0, W, H);
+   wl_surface_commit(surface);
+   if (csync() < 0) FAIL("sync failed after mapping");
+
+   /* Without this the compositor has no screensaver to inhibit, no idle timer
+    * is ever armed, and every assertion below would pass whatever E did. */
+   wl_test_screensaver_enable(tester, 1, 600);
+   if (csync() < 0) FAIL("sync failed after enabling the screensaver");
+
+   r = ask_inhibited();
+   if (r < 0) FAIL("no reply to get_idle_inhibit");
+   if (r) FAIL("something is already holding the screen awake before any "
+               "inhibitor exists");
+
+   /* One inhibitor. */
+   inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(idle_manager, surface);
+   if (csync() < 0) FAIL("sync failed after create_inhibitor");
+   r = ask_inhibited();
+   if (r < 0) FAIL("no reply to get_idle_inhibit after create");
+   if (!r) FAIL("an idle inhibitor exists and the compositor is not inhibited");
+   printf("test-idle-inhibit: an inhibitor holds the screen awake\n");
+
+   /* Input must not undo it. This is the bug the protocol would otherwise
+    * have shipped on top of. */
+   /* pointer_move, not pointer_warp: the idle timer is re-armed from
+    * _e_comp_wl_cb_mouse_move, which is the ecore half of the pointer path,
+    * and warp only feeds evas. A test that warped would exercise nothing. */
+   wl_test_pointer_move(tester, 5, 5);
+   if (csync() < 0) FAIL("sync failed after pointer_move");
+   wl_test_key(tester, "a", 1);
+   if (csync() < 0) FAIL("sync failed after key down");
+   wl_test_key(tester, "a", 0);
+   if (csync() < 0) FAIL("sync failed after key up");
+
+   r = ask_inhibited();
+   if (r < 0) FAIL("no reply to get_idle_inhibit after input");
+   if (!r)
+     FAIL("moving the pointer cancelled the inhibit. Every input event arms a "
+          "fresh idle timer, so an inhibit that does not survive input only "
+          "works while nobody touches the machine -- which is the one case "
+          "that did not need it");
+   printf("test-idle-inhibit: input does not cancel it\n");
+
+   /* Two inhibitors, one released: still inhibited. A boolean would fail. */
+   second = zwp_idle_inhibit_manager_v1_create_inhibitor(idle_manager, surface);
+   if (csync() < 0) FAIL("sync failed after second create_inhibitor");
+   zwp_idle_inhibitor_v1_destroy(second);
+   if (csync() < 0) FAIL("sync failed after destroying the second inhibitor");
+
+   r = ask_inhibited();
+   if (r < 0) FAIL("no reply to get_idle_inhibit after the second destroy");
+   if (!r)
+     FAIL("two inhibitors were taken and one released, and the compositor "
+          "stopped inhibiting -- these are counted, not a flag");
+   printf("test-idle-inhibit: inhibitors are counted, not a flag\n");
+
+   /* Last one out. */
+   zwp_idle_inhibitor_v1_destroy(inhibitor);
+   if (csync() < 0) FAIL("sync failed after destroying the last inhibitor");
+
+   r = ask_inhibited();
+   if (r < 0) FAIL("no reply to get_idle_inhibit after the last destroy");
+   if (r)
+     FAIL("every inhibitor is gone and the screen is still being held awake");
+   printf("test-idle-inhibit: releasing the last one lets the screen sleep\n");
+
+   printf("test-idle-inhibit: ok\n");
+   return 0;
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to