Wayland: Add support for the idle-inhibit protocol

Closes #955.
diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h
index 157a482..dfff8d3 100644
--- a/include/GLFW/glfw3.h
+++ b/include/GLFW/glfw3.h
@@ -2358,7 +2358,8 @@
  *  icons, the window will inherit the one defined in the application's
  *  desktop file, so this function emits @ref GLFW_PLATFORM_ERROR.
  *
- *  @remark @wayland Screensaver inhibition is currently unimplemented.
+ *  @remark @wayland Screensaver inhibition requires the idle-inhibit protocol
+ *  to be implemented in the user's compositor.
  *
  *  @thread_safety This function must only be called from the main thread.
  *
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b14512c..01c0105 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -47,6 +47,10 @@
         PROTOCOL
         "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
         BASENAME pointer-constraints-unstable-v1)
+    ecm_add_wayland_client_protocol(glfw_SOURCES
+        PROTOCOL
+        ${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
+        BASENAME idle-inhibit-unstable-v1)
 elseif (_GLFW_MIR)
     set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h
                      posix_time.h posix_thread.h xkb_unicode.h egl_context.h
diff --git a/src/wl_init.c b/src/wl_init.c
index d6832aa..ec25f20 100644
--- a/src/wl_init.c
+++ b/src/wl_init.c
@@ -501,6 +501,13 @@
                              &zwp_pointer_constraints_v1_interface,
                              1);
     }
+    else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0)
+    {
+        _glfw.wl.idleInhibitManager =
+            wl_registry_bind(registry, name,
+                             &zwp_idle_inhibit_manager_v1_interface,
+                             1);
+    }
 }
 
 static void registryHandleGlobalRemove(void *data,
@@ -786,6 +793,8 @@
         zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);
     if (_glfw.wl.pointerConstraints)
         zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
+    if (_glfw.wl.idleInhibitManager)
+        zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
     if (_glfw.wl.registry)
         wl_registry_destroy(_glfw.wl.registry);
     if (_glfw.wl.display)
diff --git a/src/wl_platform.h b/src/wl_platform.h
index 96c0a99..516c84b 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -54,6 +54,7 @@
 
 #include "wayland-relative-pointer-unstable-v1-client-protocol.h"
 #include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
+#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
 
 #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
 #define _glfw_dlclose(handle) dlclose(handle)
@@ -138,6 +139,9 @@
         struct zwp_relative_pointer_v1*    relativePointer;
         struct zwp_locked_pointer_v1*      lockedPointer;
     } pointerLock;
+
+    struct zwp_idle_inhibitor_v1*          idleInhibitor;
+
 } _GLFWwindowWayland;
 
 // Wayland-specific global data
@@ -154,6 +158,7 @@
     struct wl_keyboard*         keyboard;
     struct zwp_relative_pointer_manager_v1* relativePointerManager;
     struct zwp_pointer_constraints_v1*      pointerConstraints;
+    struct zwp_idle_inhibit_manager_v1*     idleInhibitManager;
 
     int                         compositorVersion;
 
diff --git a/src/wl_window.c b/src/wl_window.c
index b2ae7aa..9759ba2 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -189,6 +189,24 @@
     wl_region_destroy(region);
 }
 
+static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
+{
+    if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
+    {
+        window->wl.idleInhibitor =
+            zwp_idle_inhibit_manager_v1_create_inhibitor(
+                _glfw.wl.idleInhibitManager, window->wl.surface);
+        if (!window->wl.idleInhibitor)
+            _glfwInputError(GLFW_PLATFORM_ERROR,
+                            "Wayland: Idle inhibitor creation failed");
+    }
+    else if (!enable && window->wl.idleInhibitor)
+    {
+        zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
+        window->wl.idleInhibitor = NULL;
+    }
+}
+
 static GLFWbool createSurface(_GLFWwindow* window,
                               const _GLFWwndconfig* wndconfig)
 {
@@ -239,14 +257,17 @@
             WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
             0,
             window->monitor->wl.output);
+        setIdleInhibitor(window, GLFW_TRUE);
     }
     else if (window->wl.maximized)
     {
         wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
+        setIdleInhibitor(window, GLFW_FALSE);
     }
     else
     {
         wl_shell_surface_set_toplevel(window->wl.shellSurface);
+        setIdleInhibitor(window, GLFW_FALSE);
     }
 
     wl_surface_commit(window->wl.surface);
@@ -452,6 +473,9 @@
         _glfwInputWindowFocus(window, GLFW_FALSE);
     }
 
+    if (window->wl.idleInhibitor)
+        zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
+
     if (window->context.destroy)
         window->context.destroy(window);
 
@@ -637,10 +661,12 @@
             WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
             refreshRate * 1000, // Convert Hz to mHz.
             monitor->wl.output);
+        setIdleInhibitor(window, GLFW_TRUE);
     }
     else
     {
         wl_shell_surface_set_toplevel(window->wl.shellSurface);
+        setIdleInhibitor(window, GLFW_FALSE);
     }
     _glfwInputWindowMonitor(window, monitor);
 }