Wayland: Add support for xdg-decoration

This allows compositors which prefer to draw the decorations around
clients to do so, rather than letting GLFW draw its own decorations.
The appearance is thus entirely subject to the compositor used, but
should generally be better than the current solid colour decorations we
have, which we continue to use when the compositor doesn’t support this
protocol or tells us to draw the decorations ourselves.

This new protocol has been tested against wlroots’s rootston compositor.

Fixes #1257.
diff --git a/.travis.yml b/.travis.yml
index f7ce408..804b864 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -65,7 +65,7 @@
           sudo dpkg -i extra-cmake-modules_5.38.0a-0ubuntu1_amd64.deb;
           git clone git://anongit.freedesktop.org/wayland/wayland-protocols;
           pushd wayland-protocols;
-          git checkout 1.12 && ./autogen.sh --prefix=/usr && make && sudo make install;
+          git checkout 1.15 && ./autogen.sh --prefix=/usr && make && sudo make install;
           popd;
       fi
     - cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DGLFW_USE_WAYLAND=${USE_WAYLAND} ..
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a0517db..d46d364 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -262,7 +262,7 @@
 
     find_package(Wayland REQUIRED Client Cursor Egl)
     find_package(WaylandScanner REQUIRED)
-    find_package(WaylandProtocols 1.12 REQUIRED)
+    find_package(WaylandProtocols 1.15 REQUIRED)
 
     list(APPEND glfw_PKG_DEPS "wayland-egl")
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 22ce68f..d440006 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -37,6 +37,10 @@
         BASENAME xdg-shell)
     ecm_add_wayland_client_protocol(glfw_SOURCES
         PROTOCOL
+        "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
+        BASENAME xdg-decoration)
+    ecm_add_wayland_client_protocol(glfw_SOURCES
+        PROTOCOL
         "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/viewporter/viewporter.xml"
         BASENAME viewporter)
     ecm_add_wayland_client_protocol(glfw_SOURCES
diff --git a/src/wl_init.c b/src/wl_init.c
index c14a099..06531fb 100644
--- a/src/wl_init.c
+++ b/src/wl_init.c
@@ -758,6 +758,13 @@
             wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
         xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
     }
+    else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0)
+    {
+        _glfw.wl.decorationManager =
+            wl_registry_bind(registry, name,
+                             &zxdg_decoration_manager_v1_interface,
+                             1);
+    }
     else if (strcmp(interface, "wp_viewporter") == 0)
     {
         _glfw.wl.viewporter =
@@ -1138,6 +1145,8 @@
         wl_shell_destroy(_glfw.wl.shell);
     if (_glfw.wl.viewporter)
         wp_viewporter_destroy(_glfw.wl.viewporter);
+    if (_glfw.wl.decorationManager)
+        zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager);
     if (_glfw.wl.wmBase)
         xdg_wm_base_destroy(_glfw.wl.wmBase);
     if (_glfw.wl.pointer)
diff --git a/src/wl_platform.h b/src/wl_platform.h
index 1089424..2cf6496 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -57,6 +57,7 @@
 #include "osmesa_context.h"
 
 #include "wayland-xdg-shell-client-protocol.h"
+#include "wayland-xdg-decoration-client-protocol.h"
 #include "wayland-viewporter-client-protocol.h"
 #include "wayland-relative-pointer-unstable-v1-client-protocol.h"
 #include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
@@ -185,6 +186,7 @@
     struct {
         struct xdg_surface*     surface;
         struct xdg_toplevel*    toplevel;
+        struct zxdg_toplevel_decoration_v1* decoration;
     } xdg;
 
     _GLFWcursor*                currentCursor;
@@ -210,6 +212,7 @@
     GLFWbool                    justCreated;
 
     struct {
+        GLFWbool                           serverSide;
         struct wl_buffer*                  buffer;
         _GLFWdecorationWayland             top, left, right, bottom;
         int                                focus;
@@ -231,6 +234,7 @@
     struct wl_pointer*          pointer;
     struct wl_keyboard*         keyboard;
     struct xdg_wm_base*         wmBase;
+    struct zxdg_decoration_manager_v1*      decorationManager;
     struct wp_viewporter*       viewporter;
     struct zwp_relative_pointer_manager_v1* relativePointerManager;
     struct zwp_pointer_constraints_v1*      pointerConstraints;
diff --git a/src/wl_window.c b/src/wl_window.c
index ef45bae..c1d24f2 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -274,7 +274,7 @@
     const GLFWimage image = { 1, 1, data };
     GLFWbool opaque = (data[3] == 255);
 
-    if (!_glfw.wl.viewporter)
+    if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide)
         return;
 
     if (!window->wl.decorations.buffer)
@@ -321,6 +321,22 @@
     destroyDecoration(&window->wl.decorations.bottom);
 }
 
+static void xdgDecorationHandleConfigure(void* data,
+                                         struct zxdg_toplevel_decoration_v1* decoration,
+                                         uint32_t mode)
+{
+    _GLFWwindow* window = data;
+
+    window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+
+    if (!window->wl.decorations.serverSide)
+        createDecorations(window);
+}
+
+static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = {
+    xdgDecorationHandleConfigure,
+};
+
 // Makes the surface considered as XRGB instead of ARGB.
 static void setOpaqueRegion(_GLFWwindow* window)
 {
@@ -493,9 +509,6 @@
     if (!window->wl.transparent)
         setOpaqueRegion(window);
 
-    if (window->decorated && !window->monitor)
-        createDecorations(window);
-
     return GLFW_TRUE;
 }
 
@@ -517,7 +530,8 @@
             monitor->wl.output);
     }
     setIdleInhibitor(window, GLFW_TRUE);
-    destroyDecorations(window);
+    if (!window->wl.decorations.serverSide)
+        destroyDecorations(window);
 }
 
 static GLFWbool createShellSurface(_GLFWwindow* window)
@@ -553,11 +567,13 @@
     {
         wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
         setIdleInhibitor(window, GLFW_FALSE);
+        createDecorations(window);
     }
     else
     {
         wl_shell_surface_set_toplevel(window->wl.shellSurface);
         setIdleInhibitor(window, GLFW_FALSE);
+        createDecorations(window);
     }
 
     wl_surface_commit(window->wl.surface);
@@ -646,6 +662,27 @@
     xdgSurfaceHandleConfigure
 };
 
+static void setXdgDecorations(_GLFWwindow* window)
+{
+    if (_glfw.wl.decorationManager)
+    {
+        window->wl.xdg.decoration =
+            zxdg_decoration_manager_v1_get_toplevel_decoration(
+                _glfw.wl.decorationManager, window->wl.xdg.toplevel);
+        zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration,
+                                                 &xdgDecorationListener,
+                                                 window);
+        zxdg_toplevel_decoration_v1_set_mode(
+            window->wl.xdg.decoration,
+            ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+    }
+    else
+    {
+        window->wl.decorations.serverSide = GLFW_FALSE;
+        createDecorations(window);
+    }
+}
+
 static GLFWbool createXdgSurface(_GLFWwindow* window)
 {
     window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,
@@ -693,10 +730,12 @@
     {
         xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
         setIdleInhibitor(window, GLFW_FALSE);
+        setXdgDecorations(window);
     }
     else
     {
         setIdleInhibitor(window, GLFW_FALSE);
+        setXdgDecorations(window);
     }
 
     wl_surface_commit(window->wl.surface);
@@ -946,6 +985,9 @@
         window->context.destroy(window);
 
     destroyDecorations(window);
+    if (window->wl.xdg.decoration)
+        zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
+
     if (window->wl.decorations.buffer)
         wl_buffer_destroy(window->wl.decorations.buffer);
 
@@ -1061,7 +1103,7 @@
                                      int* left, int* top,
                                      int* right, int* bottom)
 {
-    if (window->decorated && !window->monitor)
+    if (window->decorated && !window->monitor && !window->wl.decorations.serverSide)
     {
         if (top)
             *top = _GLFW_DECORATION_TOP;
@@ -1190,7 +1232,7 @@
         else if (window->wl.shellSurface)
             wl_shell_surface_set_toplevel(window->wl.shellSurface);
         setIdleInhibitor(window, GLFW_FALSE);
-        if (window->decorated)
+        if (!_glfw.wl.decorationManager)
             createDecorations(window);
     }
     _glfwInputWindowMonitor(window, monitor);