[wayland] Update from origin

Because msandy@google.com asked, and because upstream fixed an implicit
conversion issue.

Bug: 58162
Change-Id: I4846a4819867a61fe3c222335d9fe48e69df4c5f
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/wayland/+/430731
Reviewed-by: Shai Barack <shayba@google.com>
Reviewed-by: Matt Sandy <msandy@google.com>
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d92e5e9
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,24 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = tab
+indent_size = 8
+max_line_length = 80
+
+[*.xml]
+indent_style = tab
+indent_size = 2
+tab_width = 8
+
+[*.py]
+indent_style = space
+indent_size = 4
+
+[*.yml]
+indent_style = space
+indent_size = 2
+max_line_length = off
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2489665..b2e174f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,18 +1,50 @@
-image: debian:stretch
+.templates_sha: &template_sha bd8010dd0123d3f0dda4ef691078566af2842613 # see https://docs.gitlab.com/ee/ci/yaml/#includefile
+
+
+include:
+  # Debian container builder template
+  - project: 'freedesktop/ci-templates'
+    ref: *template_sha
+    file: '/templates/debian.yml'
+
 
 stages:
+  - prep
   - build
 
-before_script:
-  - echo 'path-exclude=/usr/share/doc/*' > /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
-  - echo 'path-exclude=/usr/share/man/*' >> /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
-  - echo '#!/bin/sh' > /usr/sbin/policy-rc.d
-  - echo 'exit 101' >> /usr/sbin/policy-rc.d
-  - chmod +x /usr/sbin/policy-rc.d
-  - apt-get update
-  - apt-get -y --no-install-recommends install build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev doxygen graphviz xmlto xsltproc docbook-xsl
 
-build-native:
+variables:
+  DEBIAN_PACKAGES: 'build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev doxygen graphviz xmlto xsltproc docbook-xsl python3-pip python3-setuptools ninja-build'
+  DEBIAN_EXEC: 'pip3 install meson==0.52.1'
+  # these tags should be updated each time the list of packages is updated
+  # changing these will force rebuilding the associated image
+  # Note: these tags have no meaning and are not tied to a particular
+  # wayland version
+  DEBIAN_TAG: '2020-06-05.1'
+  FDO_UPSTREAM_REPO: wayland/wayland
+
+
+.debian.buster:
+  variables:
+    FDO_DISTRIBUTION_PACKAGES: $DEBIAN_PACKAGES
+    FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
+    FDO_DISTRIBUTION_VERSION: 'buster'
+    FDO_DISTRIBUTION_EXEC: $DEBIAN_EXEC
+
+
+debian:buster@container-prep:
+  extends:
+    - .debian.buster
+    - .fdo.container-build@debian
+  stage: prep
+  variables:
+    GIT_STRATEGY: none
+
+
+build-native-autotools:
+  extends:
+    - .debian.buster
+    - .fdo.distribution-image@debian
   stage: build
   script:
   - export BUILD_ID="wayland-$CI_JOB_NAME_$CI_COMMIT_SHA-$CI_JOB_ID"
@@ -34,3 +66,25 @@
     - build-*/wayland*/_build/sub/*.log
     - build-*/*.log
     - prefix-*
+
+
+build-native-meson:
+  extends:
+    - .debian.buster
+    - .fdo.distribution-image@debian
+  stage: build
+  script:
+  - export BUILD_ID="wayland-$CI_JOB_NAME_$CI_COMMIT_SHA-$CI_JOB_ID"
+  - export PREFIX="$(pwd)/prefix-$BUILD_ID"
+  - export BUILDDIR="$(pwd)/build-$BUILD_ID"
+  - mkdir "$BUILDDIR" "$PREFIX"
+  - cd "$BUILDDIR"
+  - meson --prefix="$PREFIX" -Dicon_directory=/usr/share/X11/icons ..
+  - ninja -k0 test
+  - ninja clean
+  artifacts:
+    name: wayland-meson-$CI_COMMIT_SHA-$CI_JOB_ID
+    when: always
+    paths:
+    - build-meson/meson-logs
+    - prefix-*
diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md
new file mode 100644
index 0000000..b78a615
--- /dev/null
+++ b/.gitlab/issue_templates/Bug.md
@@ -0,0 +1,8 @@
+<!--
+This repository is for the Wayland protocol description and the libwayland IPC helper
+library only. Issues with Wayland during day-to-day usage are almost
+certainly a bug in your compositor and **not** a bug with in this
+repository.
+
+Please remove this comment before filing your bug.
+-->
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 686ed63..dcc9f56 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,8 +4,46 @@
 Sending patches
 ---------------
 
-Patches should be sent to **wayland-devel@lists.freedesktop.org**, using
-`git send-email`. See [git documentation] for help.
+Patches should be sent via
+[GitLab merge requests](https://docs.gitlab.com/ce/gitlab-basics/add-merge-request.html).
+Wayland is
+[hosted on freedesktop.org's GitLab](https://gitlab.freedesktop.org/wayland/wayland/):
+in order to submit code, you should create an account on this GitLab instance,
+fork the core Wayland repository, push your changes to a branch in your new
+repository, and then submit these patches for review through a merge request.
+
+Wayland formerly accepted patches via `git-send-email`, sent to
+**wayland-devel@lists.freedesktop.org**; these were
+[tracked using Patchwork](https://patchwork.freedesktop.org/project/wayland/).
+Some old patches continue to be sent this way, and we may accept small new
+patches sent to the list, but please send all new patches through GitLab merge
+requests.
+
+
+Formatting and separating commits
+---------------------------------
+
+Unlike many projects using GitHub and GitLab, Wayland has a
+[linear, 'recipe' style history](http://www.bitsnbites.eu/git-history-work-log-vs-recipe/).
+This means that every commit should be small, digestible, stand-alone, and
+functional. Rather than a purely chronological commit history like this:
+
+    connection: plug a fd leak
+    plug another fd leak
+    connection: init fds to -1
+    close all fds
+    refactor checks into a new function
+    don't close fds we handed out
+
+we aim to have a clean history which only reflects the final state, broken up
+into functional groupings:
+
+    connection: Refactor out closure allocation
+    connection: Clear fds we shouldn't close to -1
+    connection: Make wl_closure_destroy() close fds of undispatched closures
+
+This ensures that the final patch series only contains the final state,
+without the changes and missteps taken along the development process.
 
 The first line of a commit message should contain a prefix indicating
 what part is affected by the patch followed by one sentence that
@@ -45,7 +83,7 @@
 
 When you re-send patches, revised or not, it would be very good to document the
 changes compared to the previous revision in the commit message and/or the
-cover letter. If you have already received Reviewed-by or Acked-by tags, you
+merge request. If you have already received Reviewed-by or Acked-by tags, you
 should evaluate whether they still apply and include them in the respective
 commit messages. Otherwise the tags may be lost, reviewers miss the credit they
 deserve, and the patches may cause redundant review effort.
@@ -54,78 +92,37 @@
 Tracking patches and following up
 ---------------------------------
 
-[Wayland Patchwork](http://patchwork.freedesktop.org/project/wayland/list/) is
-used for tracking patches to Wayland. Xwayland patches are tracked with the
-[Xorg project](https://patchwork.freedesktop.org/project/Xorg/list/)
-instead. Weston uses
-[GitLab merge requests](https://gitlab.freedesktop.org/wayland/weston/merge_requests)
-for code review, and does not use mailing list review at all.
+Once submitted to GitLab, your patches will be reviewed by the Wayland
+development team on GitLab. Review may be entirely positive and result in your
+code landing instantly, in which case, great! You're done. However, we may ask
+you to make some revisions: fixing some bugs we've noticed, working to a
+slightly different design, or adding documentation and tests.
 
-Libinput patches, even though they use the same mailing list as
-Wayland, are not tracked in the Wayland Patchwork.
+If you do get asked to revise the patches, please bear in mind the notes above.
+You should use `git rebase -i` to make revisions, so that your patches follow
+the clear linear split documented above. Following that split makes it easier
+for reviewers to understand your work, and to verify that the code you're
+submitting is correct.
 
-The following applies only to Wayland.
+A common request is to split single large patch into multiple patches. This can
+happen, for example, if when adding a new feature you notice a bug elsewhere
+which you need to fix to progress. Separating these changes into separate
+commits will allow us to verify and land the bugfix quickly, pushing part of
+your work for the good of everyone, whilst revision and discussion continues on
+the larger feature part. It also allows us to direct you towards reviewers who
+best understand the different areas you are working on.
 
-If a patch is not found in Patchwork, there is a high possibility for it to be
-forgotten. Patches attached to bug reports or not arriving to the mailing list
-because of e.g. subscription issues will not be in Patchwork because Patchwork
-only collects patches sent to the list.
+When you have made any requested changes, please rebase the commits, verify
+that they still individually look good, then force-push your new branch to
+GitLab. This will update the merge request and notify everyone subscribed to
+your merge request, so they can review it again.
 
-When you send a revised version of a patch, it would be very nice to mark your
-old patch as superseded (or rejected, if that is applicable). You can change
-the status of your own patches by registering to Patchwork - ownership is
-identified by email address you use to register. Updating your patch status
-appropriately will help maintainer work.
-
-The following patch states are found in Patchwork:
-
-- **New**:
-    Patches under discussion or not yet processed.
-
-- **Under review**:
-    Mostly unused state.
-
-- **Accepted**:
-    The patch is merged in the master branch upstream, as is or slightly
-    modified.
-
-- **Rejected**:
-    The idea or approach is rejected and cannot be fixed by revising
-    the patch.
-
-- **RFC**:
-    Request for comments, not meant to be merged as is.
-
-- **Not applicable**:
-    The email was not actually a patch, or the patch is not for Wayland.
-    Libinput patches are usually automatically ignored by Wayland
-    Patchwork, but if they get through, they will be marked as Not
-    applicable.
-
-- **Changes requested**:
-    Reviewers determined that changes to the patch are needed. The
-    submitter is expected to send a revised version. (You should
-    not wait for your patch to be set to this state before revising,
-    though.)
-
-- **Awaiting upstream**:
-    Mostly unused as the patch is waiting for upstream actions but
-    is not shown in the default list, which means it is easy to
-    overlook.
-
-- **Superseded**:
-    A revised version of the patch has been submitted.
-
-- **Deferred**:
-    Used mostly during freeze periods before releases, to temporarily
-    hide patches that cannot be merged during a freeze.
-
-Note, that in the default listing, only patches in *New* or *Under review* are
-shown.
-
-There is also a command line interface to Patchwork called `pwclient`, see
-http://patchwork.freedesktop.org/project/wayland/
-for links where to get it and the sample `.pwclientrc` for Wayland.
+There are also
+[many GitLab CLI clients](https://about.gitlab.com/applications/#cli-clients),
+if you prefer to avoid the web interface. It may be difficult to follow review
+comments without using the web interface though, so we do recommend using this
+to go through the review process, even if you use other clients to track the
+list of available patches.
 
 
 Coding style
diff --git a/Makefile.am b/Makefile.am
index 697c517..b9438b7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,6 +24,7 @@
 
 bin_PROGRAMS = wayland-scanner
 wayland_scanner_SOURCES = src/scanner.c
+wayland_scanner_CPPFLAGS = $(AM_CPPFLAGS) -include config.h
 wayland_scanner_CFLAGS = $(EXPAT_CFLAGS) $(LIBXML_CFLAGS) $(AM_CFLAGS)
 wayland_scanner_LDADD = $(EXPAT_LIBS) $(LIBXML_LIBS) libwayland-util.la
 pkgconfig_DATA += src/wayland-scanner.pc
@@ -56,7 +57,8 @@
 	src/connection.c			\
 	src/wayland-os.c			\
 	src/wayland-os.h			\
-	src/wayland-private.h
+	src/wayland-private.h			\
+	src/wayland-server-private.h
 
 include_HEADERS =				\
 	src/wayland-util.h			\
@@ -71,7 +73,7 @@
 	protocol/wayland-client-protocol.h
 
 libwayland_server_la_CFLAGS = $(FFI_CFLAGS) $(AM_CFLAGS) -pthread
-libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la -lrt -lm
+libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la $(RT_LIBS) -lm
 libwayland_server_la_LDFLAGS = -version-info 1:0:1
 libwayland_server_la_SOURCES =			\
 	src/wayland-server.c			\
@@ -83,7 +85,7 @@
 	protocol/wayland-protocol.c
 
 libwayland_client_la_CFLAGS = $(FFI_CFLAGS) $(AM_CFLAGS) -pthread
-libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la -lrt -lm
+libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la $(RT_LIBS) -lm
 libwayland_client_la_LDFLAGS = -version-info 3:0:3
 libwayland_client_la_SOURCES =			\
 	src/wayland-client.c
@@ -96,22 +98,22 @@
 
 protocol/%-protocol.c : $(top_srcdir)/protocol/%.xml
 if USE_HOST_SCANNER
-	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) code $< $@
+	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s code $< $@
 else
-	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) public-code $< $@
+	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s public-code $< $@
 endif
 
 protocol/%-server-protocol.h : $(top_srcdir)/protocol/%.xml
-	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) server-header $< $@
+	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s server-header $< $@
 
 protocol/%-client-protocol.h : $(top_srcdir)/protocol/%.xml
-	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) client-header $< $@
+	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s client-header $< $@
 
 protocol/%-server-protocol-core.h : $(top_srcdir)/protocol/%.xml
-	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) server-header -c < $< > $@
+	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s server-header -c < $< > $@
 
 protocol/%-client-protocol-core.h : $(top_srcdir)/protocol/%.xml
-	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) client-header -c < $< > $@
+	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s client-header -c < $< > $@
 
 BUILT_SOURCES =					\
 	$(nodist_libwayland_server_la_SOURCES)	\
@@ -174,6 +176,7 @@
 	sanity-test				\
 	socket-test				\
 	queue-test				\
+	proxy-test				\
 	signal-test				\
 	newsignal-test				\
 	resources-test				\
@@ -210,12 +213,15 @@
 	exec-fd-leak-checker			\
 	fixed-benchmark
 
-noinst_LTLIBRARIES += libtest-runner.la
+noinst_LTLIBRARIES +=				\
+	libtest-runner.la			\
+	libtest-helpers.la
+
+libtest_helpers_la_SOURCES = tests/test-helpers.c
 
 libtest_runner_la_SOURCES =			\
 	tests/test-runner.c			\
 	tests/test-runner.h			\
-	tests/test-helpers.c			\
 	tests/test-compositor.h			\
 	tests/test-compositor.c
 libtest_runner_la_LIBADD =			\
@@ -223,13 +229,14 @@
 	libwayland-util.la			\
 	libwayland-client.la			\
 	libwayland-server.la			\
-	-lrt -ldl $(FFI_LIBS)
-
+	libtest-helpers.la			\
+	$(RT_LIBS) $(DL_LIBS) $(FFI_LIBS)
 
 array_test_SOURCES = tests/array-test.c
 array_test_LDADD = libtest-runner.la
 client_test_SOURCES = tests/client-test.c
 client_test_LDADD = libtest-runner.la
+display_test_CFLAGS = -pthread
 display_test_SOURCES = tests/display-test.c
 display_test_LDADD = libtest-runner.la
 nodist_display_test_SOURCES =				\
@@ -254,6 +261,8 @@
 socket_test_LDADD = libtest-runner.la
 queue_test_SOURCES = tests/queue-test.c
 queue_test_LDADD = libtest-runner.la
+proxy_test_SOURCES = tests/proxy-test.c
+proxy_test_LDADD = libtest-runner.la
 signal_test_SOURCES = tests/signal-test.c
 signal_test_LDADD = libtest-runner.la
 # wayland-server.c is needed here to access wl_priv_* functions
@@ -270,7 +279,6 @@
 headers_test_SOURCES = tests/headers-test.c \
 		       tests/headers-protocol-test.c \
 		       tests/headers-protocol-core-test.c
-headers_test_LDADD = libtest-runner.la
 nodist_headers_test_SOURCES =			\
 	protocol/wayland-server-protocol-core.h	\
 	protocol/wayland-client-protocol-core.h
@@ -280,16 +288,23 @@
 endif
 
 fixed_benchmark_SOURCES = tests/fixed-benchmark.c
-fixed_benchmark_LDADD = libtest-runner.la
+fixed_benchmark_LDADD = $(RT_LIBS)
 
 os_wrappers_test_SOURCES = tests/os-wrappers-test.c
 os_wrappers_test_LDADD = libtest-runner.la
 
 exec_fd_leak_checker_SOURCES = tests/exec-fd-leak-checker.c
-exec_fd_leak_checker_LDADD = libtest-runner.la
+exec_fd_leak_checker_LDADD = libtest-helpers.la
 
 EXTRA_DIST += tests/scanner-test.sh			\
 	protocol/tests.xml				\
+	tests/data/bad-identifier-arg.xml		\
+	tests/data/bad-identifier-entry.xml		\
+	tests/data/bad-identifier-enum.xml		\
+	tests/data/bad-identifier-event.xml		\
+	tests/data/bad-identifier-interface.xml		\
+	tests/data/bad-identifier-protocol.xml		\
+	tests/data/bad-identifier-request.xml		\
 	tests/data/example.xml				\
 	tests/data/example-client.h			\
 	tests/data/example-server.h			\
@@ -301,7 +316,22 @@
 	tests/data/small-code-core.c			\
 	tests/data/small-client-core.h			\
 	tests/data/small-server-core.h			\
-	tests/data/small-private-code.c
+	tests/data/small-private-code.c			\
+	meson.build					\
+	meson_options.txt				\
+	cursor/meson.build				\
+	doc/meson.build					\
+	doc/doxygen/mainpage.dox			\
+	doc/doxygen/meson.build				\
+	doc/doxygen/gen-doxygen.py			\
+	doc/doxygen/xml/meson.build			\
+	doc/doxygen/xml/Client/meson.build		\
+	doc/doxygen/xml/Server/meson.build		\
+	doc/publican/meson.build			\
+	doc/publican/sources/meson.build		\
+	egl/meson.build					\
+	src/meson.build					\
+	tests/meson.build
 
 tests/scanner-test.sh: $(top_builddir)/wayland-scanner
 
diff --git a/README b/README
index 3ae9f0f..ad06dce 100644
--- a/README
+++ b/README
@@ -26,9 +26,8 @@
 
     $ git clone https://gitlab.freedesktop.org/wayland/wayland
     $ cd wayland
-    $ ./autogen.sh --prefix=PREFIX
-    $ make
-    $ make install
+    $ meson build/ --prefix=PREFIX
+    $ ninja -C build/ install
 
 where PREFIX is where you want to install the libraries.  See
 https://wayland.freedesktop.org for more complete build instructions
diff --git a/TODO b/TODO
index 8cb8d34..88fa5cc 100644
--- a/TODO
+++ b/TODO
@@ -102,9 +102,6 @@
 
  - Investigate DirectFB on Wayland (or is that Wayland on DirectFB?)
 
- - SDL port, bnf has work in progress here:
-   http://cgit.freedesktop.org/~bnf/sdl-wayland/
-
 
 Ideas
 
diff --git a/config.h b/config.h
index 8b240bb..d006b69 100644
--- a/config.h
+++ b/config.h
@@ -13,6 +13,9 @@
 /* libxml-2.0 is available */
 /* #undef HAVE_LIBXML */
 
+/* Define to 1 if you have the `memfd_create' function. */
+#define HAVE_MEMFD_CREATE 1
+
 /* Define to 1 if you have the <memory.h> header file. */
 #define HAVE_MEMORY_H 1
 
@@ -37,6 +40,9 @@
 /* Define to 1 if you have the <string.h> header file. */
 #define HAVE_STRING_H 1
 
+/* Define to 1 if you have the `strndup' function. */
+#define HAVE_STRNDUP 1
+
 /* Define to 1 if you have the <sys/prctl.h> header file. */
 #define HAVE_SYS_PRCTL_H 1
 
@@ -62,7 +68,7 @@
 #define PACKAGE_NAME "wayland"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "wayland 1.16.90"
+#define PACKAGE_STRING "wayland 1.18.90"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "wayland"
@@ -71,10 +77,10 @@
 #define PACKAGE_URL "https://wayland.freedesktop.org/"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.16.90"
+#define PACKAGE_VERSION "1.18.90"
 
 /* Define to 1 if you have the ANSI C header files. */
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "1.16.90"
+#define VERSION "1.18.90"
diff --git a/configure.ac b/configure.ac
index 18fb649..ace83b2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,8 +1,8 @@
 AC_PREREQ([2.64])
 
 m4_define([wayland_major_version],  [1])
-m4_define([wayland_minor_version], [16])
-m4_define([wayland_micro_version],  [90])
+m4_define([wayland_minor_version], [18])
+m4_define([wayland_micro_version], [90])
 m4_define([wayland_version],
           [wayland_major_version.wayland_minor_version.wayland_micro_version])
 
@@ -63,7 +63,13 @@
 AC_SUBST(GCC_CFLAGS)
 
 AC_CHECK_HEADERS([sys/prctl.h])
-AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate prctl])
+AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate prctl memfd_create strndup])
+
+# *BSD don't have libdl, but they have its functions in libc
+WESTON_SEARCH_LIBS([DL], [dl], [dlsym])
+
+# OpenBSD doesn't have librt, but it has its functions in libc
+WESTON_SEARCH_LIBS([RT], [rt], [clock_gettime])
 
 AC_ARG_ENABLE([libraries],
 	      [AC_HELP_STRING([--disable-libraries],
@@ -123,19 +129,6 @@
 AC_PATH_PROG(XSLTPROC, xsltproc)
 AM_CONDITIONAL([HAVE_XSLTPROC], [test "x$XSLTPROC" != "x"])
 
-AC_MSG_CHECKING([for docbook manpages stylesheet])
-MANPAGES_STYLESHEET=http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
-AC_PATH_PROGS_FEATURE_CHECK([XSLTPROC_TMP], [xsltproc],
-			    AS_IF([`"$ac_path_XSLTPROC_TMP" --nonet "$MANPAGES_STYLESHEET" > /dev/null 2>&1`],
-				  [HAVE_MANPAGES_STYLESHEET=yes]))
-if test "x$HAVE_MANPAGES_STYLESHEET" = "xyes"; then
-	AM_CONDITIONAL([HAVE_MANPAGES_STYLESHEET], true)
-	AC_SUBST(MANPAGES_STYLESHEET)
-	AC_MSG_RESULT([yes])
-else
-	AM_CONDITIONAL([HAVE_MANPAGES_STYLESHEET], false)
-	AC_MSG_RESULT([no])
-fi
 
 AM_CONDITIONAL(BUILD_DOCS, [test x$enable_documentation = xyes])
 if test "x$enable_documentation" = "xyes"; then
@@ -171,6 +164,19 @@
 	                   [AC_MSG_RESULT([yes])],
 	                   [AC_MSG_RESULT([yes])])
 
+	AC_MSG_CHECKING([for docbook stylesheets])
+	DOCS_STYLESHEET=http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
+	AC_PATH_PROGS_FEATURE_CHECK([XSLTPROC_TMP], [xsltproc],
+				    AS_IF([`"$ac_path_XSLTPROC_TMP" --nonet "$DOCS_STYLESHEET" > /dev/null 2>&1`],
+					  [HAVE_DOCS_STYLESHEET=yes]))
+	if test "x$HAVE_DOCS_STYLESHEET" != "xyes"; then
+		AC_MSG_RESULT([no])
+		AC_MSG_ERROR([Documentation build requested but docbook-xsl stylesheets are not found. Install the docbook-xsl package or disable the documentation using --disable-documentation])
+	fi
+
+	AC_MSG_RESULT([yes])
+	AC_SUBST(DOCS_STYLESHEET)
+
 	AC_CONFIG_FILES([
 	doc/doxygen/wayland.doxygen
 	])
@@ -184,7 +190,6 @@
 		 doc/Makefile
 		 doc/publican/Makefile
 		 doc/doxygen/Makefile
-		 doc/man/Makefile
 		 egl/wayland-egl.pc
 		 egl/wayland-egl-backend.pc
 		 src/wayland-server-uninstalled.pc
diff --git a/cursor/meson.build b/cursor/meson.build
new file mode 100644
index 0000000..ae85ed9
--- /dev/null
+++ b/cursor/meson.build
@@ -0,0 +1,27 @@
+icondir = get_option('icon_directory')
+if icondir == ''
+	icondir = join_paths(get_option('prefix'), get_option('datadir'), 'icons')
+endif
+
+wayland_cursor = library(
+	'wayland-cursor',
+	sources: [
+		'wayland-cursor.c',
+		'os-compatibility.c',
+		'xcursor.c',
+	],
+	version: '0.0.0',
+	dependencies: [ wayland_client_dep ],
+	c_args: [ '-DICONDIR="@0@"'.format(icondir) ],
+	install: true,
+)
+
+install_headers('wayland-cursor.h')
+
+pkgconfig.generate(
+	name: 'Wayland Cursor',
+	description: 'Wayland cursor helper library',
+	version: meson.project_version(),
+	libraries: wayland_cursor,
+	filebase: 'wayland-cursor',
+)
diff --git a/cursor/os-compatibility.c b/cursor/os-compatibility.c
index e972d21..9eac229 100644
--- a/cursor/os-compatibility.c
+++ b/cursor/os-compatibility.c
@@ -25,6 +25,8 @@
 
 #define _GNU_SOURCE
 
+#include "config.h"
+
 #include <sys/types.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -32,7 +34,10 @@
 #include <string.h>
 #include <stdlib.h>
 
-#include "config.h"
+#ifdef HAVE_MEMFD_CREATE
+#include <sys/mman.h>
+#endif
+
 #include "os-compatibility.h"
 
 #ifndef HAVE_MKOSTEMP
@@ -99,6 +104,13 @@
  * given size. If disk space is insufficent, errno is set to ENOSPC.
  * If posix_fallocate() is not supported, program may receive
  * SIGBUS on accessing mmap()'ed file contents instead.
+ *
+ * If the C library implements memfd_create(), it is used to create the
+ * file purely in memory, without any backing file name on the file
+ * system, and then sealing off the possibility of shrinking it.  This
+ * can then be checked before accessing mmap()'ed file contents, to
+ * make sure SIGBUS can't happen.  It also avoids requiring
+ * XDG_RUNTIME_DIR.
  */
 int
 os_create_anonymous_file(off_t size)
@@ -107,42 +119,65 @@
 	const char *path;
 	char *name;
 	int fd;
-	int ret;
 
-	path = getenv("XDG_RUNTIME_DIR");
-	if (!path) {
-		errno = ENOENT;
-		return -1;
-	}
-
-	name = malloc(strlen(path) + sizeof(template));
-	if (!name)
-		return -1;
-
-	strcpy(name, path);
-	strcat(name, template);
-
-	fd = create_tmpfile_cloexec(name);
-
-	free(name);
-
-	if (fd < 0)
-		return -1;
-
-#ifdef HAVE_POSIX_FALLOCATE
-	ret = posix_fallocate(fd, 0, size);
-	if (ret != 0) {
-		close(fd);
-		errno = ret;
-		return -1;
-	}
-#else
-	ret = ftruncate(fd, size);
-	if (ret < 0) {
-		close(fd);
-		return -1;
-	}
+#ifdef HAVE_MEMFD_CREATE
+	fd = memfd_create("wayland-cursor", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+	if (fd >= 0) {
+		/* We can add this seal before calling posix_fallocate(), as
+		 * the file is currently zero-sized anyway.
+		 *
+		 * There is also no need to check for the return value, we
+		 * couldn't do anything with it anyway.
+		 */
+		fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
+	} else
 #endif
+	{
+		path = getenv("XDG_RUNTIME_DIR");
+		if (!path) {
+			errno = ENOENT;
+			return -1;
+		}
+
+		name = malloc(strlen(path) + sizeof(template));
+		if (!name)
+			return -1;
+
+		strcpy(name, path);
+		strcat(name, template);
+
+		fd = create_tmpfile_cloexec(name);
+
+		free(name);
+
+		if (fd < 0)
+			return -1;
+	}
+
+	if (os_resize_anonymous_file(fd, size) < 0) {
+		close(fd);
+		return -1;
+	}
 
 	return fd;
 }
+
+int
+os_resize_anonymous_file(int fd, off_t size)
+{
+#ifdef HAVE_POSIX_FALLOCATE
+	/* 
+	 * Filesystems that do support fallocate will return EINVAL or
+	 * EOPNOTSUPP. In this case we need to fall back to ftruncate
+	 */
+	errno = posix_fallocate(fd, 0, size);
+	if (errno == 0)
+		return 0;
+	else if (errno != EINVAL && errno != EOPNOTSUPP)
+		return -1;
+#endif
+	if (ftruncate(fd, size) < 0)
+		return -1;
+
+	return 0;
+}
diff --git a/cursor/os-compatibility.h b/cursor/os-compatibility.h
index d0e69ac..fdfeb78 100644
--- a/cursor/os-compatibility.h
+++ b/cursor/os-compatibility.h
@@ -31,4 +31,7 @@
 int
 os_create_anonymous_file(off_t size);
 
+int
+os_resize_anonymous_file(int fd, off_t size);
+
 #endif /* OS_COMPATIBILITY_H */
diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c
index d40c5c8..4e2dc50 100644
--- a/cursor/wayland-cursor.c
+++ b/cursor/wayland-cursor.c
@@ -83,15 +83,9 @@
 static int
 shm_pool_resize(struct shm_pool *pool, int size)
 {
-	if (ftruncate(pool->fd, size) < 0)
+	if (os_resize_anonymous_file(pool->fd, size) < 0)
 		return 0;
 
-#ifdef HAVE_POSIX_FALLOCATE
-	errno = posix_fallocate(pool->fd, 0, size);
-	if (errno != 0)
-		return 0;
-#endif
-
 	wl_shm_pool_resize(pool->pool, size);
 
 	munmap(pool->data, pool->size);
diff --git a/cursor/wayland-cursor.h b/cursor/wayland-cursor.h
index 40d3fc5..915a110 100644
--- a/cursor/wayland-cursor.h
+++ b/cursor/wayland-cursor.h
@@ -36,17 +36,36 @@
 struct wl_buffer;
 struct wl_shm;
 
+/** A still image part of a cursor
+ *
+ * Use `wl_cursor_image_get_buffer()` to get the corresponding `struct
+ * wl_buffer` to attach to your `struct wl_surface`. */
 struct wl_cursor_image {
-	uint32_t width;		/* actual width */
-	uint32_t height;	/* actual height */
-	uint32_t hotspot_x;	/* hot spot x (must be inside image) */
-	uint32_t hotspot_y;	/* hot spot y (must be inside image) */
-	uint32_t delay;		/* animation delay to next frame (ms) */
+	/** Actual width */
+	uint32_t width;
+
+	/** Actual height */
+	uint32_t height;
+
+	/** Hot spot x (must be inside image) */
+	uint32_t hotspot_x;
+
+	/** Hot spot y (must be inside image) */
+	uint32_t hotspot_y;
+
+	/** Animation delay to next frame (ms) */
+	uint32_t delay;
 };
 
+/** A cursor, as returned by `wl_cursor_theme_get_cursor()` */
 struct wl_cursor {
+	/** How many images there are in this cursor’s animation */
 	unsigned int image_count;
+
+	/** The array of still images composing this animation */
 	struct wl_cursor_image **images;
+
+	/** The name of this cursor */
 	char *name;
 };
 
diff --git a/cursor/xcursor.c b/cursor/xcursor.c
index 689c702..1f1360f 100644
--- a/cursor/xcursor.c
+++ b/cursor/xcursor.c
@@ -285,10 +285,11 @@
 
     if ((*file->read) (file, bytes, 4) != 4)
 	return XcursorFalse;
-    *u = ((bytes[0] << 0) |
-	  (bytes[1] << 8) |
-	  (bytes[2] << 16) |
-	  (bytes[3] << 24));
+
+    *u = ((XcursorUInt)(bytes[0]) << 0) |
+         ((XcursorUInt)(bytes[1]) << 8) |
+         ((XcursorUInt)(bytes[2]) << 16) |
+         ((XcursorUInt)(bytes[3]) << 24);
     return XcursorTrue;
 }
 
diff --git a/cursor/xcursor.h b/cursor/xcursor.h
index 62e2322..c1ca12c 100644
--- a/cursor/xcursor.h
+++ b/cursor/xcursor.h
@@ -26,8 +26,10 @@
 #ifndef XCURSOR_H
 #define XCURSOR_H
 
+#include <stdint.h>
+
 typedef int		XcursorBool;
-typedef unsigned int	XcursorUInt;
+typedef uint32_t	XcursorUInt;
 
 typedef XcursorUInt	XcursorDim;
 typedef XcursorUInt	XcursorPixel;
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 1efd3e2..0b1c4f2 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1 +1 @@
-SUBDIRS = doxygen publican man
+SUBDIRS = doxygen publican
diff --git a/doc/doxygen/.gitignore b/doc/doxygen/.gitignore
index a85e6c0..d68d6fc 100644
--- a/doc/doxygen/.gitignore
+++ b/doc/doxygen/.gitignore
@@ -1,4 +1,3 @@
 doxygen_sqlite3.db
 html/
 wayland.doxygen
-xml/
diff --git a/doc/doxygen/Makefile.am b/doc/doxygen/Makefile.am
index f8b0b3a..86fd8bf 100644
--- a/doc/doxygen/Makefile.am
+++ b/doc/doxygen/Makefile.am
@@ -3,8 +3,10 @@
 
 noinst_DATA = \
               xml/Client/index.xml \
+              xml/Cursor/index.xml \
               xml/Server/index.xml \
               html/Client/index.html \
+              html/Cursor/index.html \
               html/Server/index.html
 dist_noinst_DATA = wayland.doxygen.in
 
@@ -17,6 +19,10 @@
 	$(top_srcdir)/src/wayland-client.h	\
 	$(top_srcdir)/src/wayland-client-core.h
 
+scanned_src_files_Cursor = 				\
+	$(top_srcdir)/cursor/wayland-cursor.c	\
+	$(top_srcdir)/cursor/wayland-cursor.h
+
 scanned_src_files_Server = 				\
 	$(scanned_src_files_shared)			\
 	$(top_srcdir)/src/event-loop.c		\
@@ -42,6 +48,9 @@
 	$(top_builddir)/protocol/wayland-client-protocol.h \
 	$(extra_doxygen)
 
+extra_doxygen_Cursor = \
+	$(extra_doxygen)
+
 diagramsdir := $(srcdir)/dot
 diagramssrc := $(wildcard $(diagramsdir)/*.gv)
 diagrams := $(patsubst $(diagramsdir)/%,xml/%,$(diagramssrc:.gv=.png))
@@ -53,20 +62,21 @@
 dist_man3_MANS = $(shell test -d man && find man/man3 -name "wl_*.3" -printf "man/man3/%P\n")
 
 # Listing various directories that might need to be created.
-alldirs := xml xml/Client xml/Server man/man3 html/Client html/Server
+alldirsrel := xml xml/Client xml/Server xml/Cursor man/man3 html/Client html/Server html/Cursor
+alldirs := $(patsubst %,$(CURDIR)/%,$(alldirsrel))
 
 $(diagrams): $(diagramssrc)
 
 $(diagram_maps):  $(diagramssrc)
 
-xml/%/index.xml: $(top_srcdir)/src/scanner.c $(scanned_src_files_%) wayland.doxygen $(diagrams) $(diagram_maps) | xml/%
+xml/%/index.xml: $(top_srcdir)/src/scanner.c $(scanned_src_files_%) wayland.doxygen $(diagrams) $(diagram_maps) | $(CURDIR)/xml/%
 	$(AM_V_GEN)(cat wayland.doxygen; \
           echo "GENERATE_XML=YES"; \
           echo "XML_OUTPUT=xml/$*"; \
           echo "INPUT= $(scanned_src_files_$*)"; \
           ) | $(DOXYGEN) -
 
-html/%/index.html: $(scanned_src_files_%) wayland.doxygen $(diagrams) $(diagram_maps) | html/%
+html/%/index.html: $(scanned_src_files_%) wayland.doxygen $(diagrams) $(diagram_maps) | $(CURDIR)/html/%
 	$(AM_V_GEN)(cat wayland.doxygen; \
           echo "PROJECT_NAME=\"Wayland $* API\""; \
           echo "GENERATE_HTML=YES"; \
@@ -74,7 +84,7 @@
           echo "INPUT= $(scanned_src_files_$*) $(extra_doxygen_$*)"; \
           ) | $(DOXYGEN) -
 
-man/man3/wl_display.3: $(top_srcdir)/src/scanner.c $(scanned_src_files_man) wayland.doxygen | man/man3
+man/man3/wl_display.3: $(top_srcdir)/src/scanner.c $(scanned_src_files_man) wayland.doxygen | $(CURDIR)/man/man3
 	$(AM_V_GEN)(cat wayland.doxygen; \
           echo "GENERATE_MAN=YES"; \
           echo "MAN_OUTPUT=man"; \
@@ -82,10 +92,10 @@
           echo "INPUT= $(scanned_src_files_man)"; \
           ) | $(DOXYGEN) -
 
-xml/%.png: $(diagramsdir)/%.gv | xml
+xml/%.png: $(diagramsdir)/%.gv | $(CURDIR)/xml
 	$(AM_V_GEN)$(DOT) -Tpng -o$@ $<
 
-xml/%.map: $(diagramsdir)/%.gv | xml
+xml/%.map: $(diagramsdir)/%.gv | $(CURDIR)/xml
 	$(AM_V_GEN)$(DOT) -Tcmapx_np -o$@ $<
 
 # general rule to create one of the listed directories.
diff --git a/doc/doxygen/gen-doxygen.py b/doc/doxygen/gen-doxygen.py
new file mode 100755
index 0000000..1bb07e5
--- /dev/null
+++ b/doc/doxygen/gen-doxygen.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+
+import argparse
+import datetime
+import errno
+import os
+import subprocess
+import sys
+
+# Custom configuration for each documentation format
+doxygen_templates = {
+    'xml': [
+        'GENERATE_XML=YES\n',
+        'XML_OUTPUT={format}/{section}\n',
+        'INPUT= {files}\n',
+    ],
+    'html': [
+        'GENERATE_HTML=YES\n',
+        'HTML_OUTPUT={format}/{section}\n',
+        'PROJECT_NAME=\"Wayland {section} API\"\n',
+        'INPUT= {files}\n',
+    ],
+    'man': [
+        'GENERATE_MAN=YES\n',
+        'MAN_OUTPUT={format}\n',
+        'MAN_SUBDIR=.\n',
+        'JAVADOC_AUTOBRIEF=NO\n',
+        'INPUT= {files}\n',
+    ],
+}
+
+def load_doxygen_file(doxyfile):
+    with open(doxyfile, 'r') as f:
+        res = f.readlines()
+    return res
+
+def get_template(outformat):
+    for (k,v) in doxygen_templates.items():
+        if outformat.startswith(k):
+            return v
+
+def gen_doxygen_file(data, outformat, section, files):
+    for l in get_template(outformat):
+        data.append(l.format(format=outformat, section=section, files=' '.join(files)))
+    return data
+
+parser = argparse.ArgumentParser(description='Generate docs with Doxygen')
+parser.add_argument('doxygen_file',
+                    help='The doxygen file to use')
+parser.add_argument('files',
+                    help='The list of files to parse',
+                    metavar='FILES',
+                    nargs='+')
+parser.add_argument('--builddir',
+                    help='The build directory',
+                    metavar='DIR',
+                    default='.')
+parser.add_argument('--section',
+                    help='The section to build',
+                    metavar='NAME',
+                    default='Client')
+parser.add_argument('--output-format',
+                    help='The output format: xml, html, man',
+                    metavar='FORMAT',
+                    default='xml')
+parser.add_argument('--stamp',
+                    help='Stamp file to output',
+                    metavar='STAMP_FILE',
+                    nargs='?',
+                    type=argparse.FileType('w'))
+
+args = parser.parse_args()
+
+# Merge the doxyfile with our custom templates
+conf = load_doxygen_file(args.doxygen_file)
+conf = gen_doxygen_file(conf, args.output_format, args.section, args.files)
+
+# Doxygen is not clever enough to create the directories it
+# needs beforehand
+try:
+    os.makedirs(os.path.join(args.builddir, args.output_format))
+except OSError as e:
+    if e.errno != errno.EEXIST:
+        raise e
+
+# Run Doxygen with the generated doxyfile
+cmd = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE)
+cmd.stdin.write(''.join(conf).encode('utf-8'))
+cmd.stdin.close()
+if cmd.wait() != 0:
+    sys.exit(1)
+
+# This is a bit of a hack; Doxygen will generate way more files than we
+# want to install, but there's no way to know how many at configuration
+# time. Since we want to install only the wl_* man pages anyway, we can
+# delete the other files and let Meson install the whole man3 subdirectory
+if args.output_format.startswith('man'):
+    manpath = os.path.join(args.builddir, args.output_format)
+    for filename in os.listdir(manpath):
+        full_path = os.path.join(manpath, filename)
+        if not filename.startswith('wl_'):
+            os.remove(full_path)
+
+if args.stamp:
+   args.stamp.write(str(datetime.datetime.now()))
diff --git a/doc/doxygen/mainpage.dox b/doc/doxygen/mainpage.dox
index ca1da53..352f69b 100644
--- a/doc/doxygen/mainpage.dox
+++ b/doc/doxygen/mainpage.dox
@@ -6,6 +6,7 @@
  *
  * - <a href="../Server/index.html">Server-side API</a>
  * - <a href="../Client/index.html">Client-side API</a>
+ * - <a href="../Cursor/index.html">Cursor helper library API</a>
  *
  * Further documentation about the architecture and principles of Wayland is
  * available in the
diff --git a/doc/doxygen/meson.build b/doc/doxygen/meson.build
new file mode 100644
index 0000000..f2bee14
--- /dev/null
+++ b/doc/doxygen/meson.build
@@ -0,0 +1,115 @@
+# Here be dragons
+
+dot_gv = {
+	'wayland-architecture': files('dot/wayland-architecture.gv'),
+	'x-architecture': files('dot/x-architecture.gv'),
+}
+
+# This is a workaround for Meson's custom_target() directive, which
+# currently does not support outputs pointing to a sub-directory
+# XXX: try turning these into maps, so they can be indexed with picture name
+dot_png = []
+dot_map = []
+
+doxygen_conf = configuration_data()
+doxygen_conf.set('VERSION', meson.project_version())
+doxygen_conf.set('top_builddir', meson.build_root())
+wayland_doxygen = configure_file(
+	input: 'wayland.doxygen.in',
+	output: 'wayland.doxygen',
+	configuration: doxygen_conf,
+)
+
+shared_files = files([
+	'../../src/wayland-util.h',
+])
+
+client_files = files([
+	'../../src/wayland-client.c',
+	'../../src/wayland-client.h',
+	'../../src/wayland-client-core.h',
+])
+
+server_files = files([
+	'../../src/event-loop.c',
+	'../../src/wayland-server.c',
+	'../../src/wayland-server.h',
+	'../../src/wayland-server-core.h',
+	'../../src/wayland-shm.c',
+])
+
+cursor_files = files([
+	'../../cursor/wayland-cursor.c',
+	'../../cursor/wayland-cursor.h',
+])
+
+extra_client_files = [
+	'mainpage.dox',
+	wayland_client_protocol_h,
+]
+
+extra_server_files = [
+	'mainpage.dox',
+	wayland_server_protocol_h,
+]
+
+extra_cursor_files = [
+	'mainpage.dox',
+]
+
+gen_doxygen = find_program('gen-doxygen.py')
+
+subdir('xml')
+
+formats = {
+	'html': {
+		'Client': shared_files + client_files + extra_client_files,
+		'Server': shared_files + server_files + extra_server_files,
+		'Cursor': shared_files + cursor_files + extra_cursor_files,
+	},
+}
+
+foreach f_name, sections: formats
+	foreach s_name, s_files: sections
+		t_name = '@0@-@1@-doc'.format(f_name, s_name)
+
+		# We do not really need an output file, but Meson
+		# will complain if one is not set, so we use a
+		# dummy 'stamp' file
+		custom_target(
+			t_name,
+			command: [
+				gen_doxygen,
+				# XXX pass doxygen path as argument
+				'--builddir=@OUTDIR@',
+				'--section=@0@'.format(s_name),
+				'--output-format=@0@'.format(f_name),
+				'--stamp=doc/doxygen/@0@.stamp'.format(t_name),
+				wayland_doxygen,
+				'@INPUT@',
+			],
+			input: s_files,
+			output: '@0@.stamp'.format(t_name),
+			depends: [dot_png, dot_map],
+			build_by_default: true,
+		)
+	endforeach
+endforeach
+
+man_files = shared_files + server_files + client_files + cursor_files
+custom_target(
+	'man-pages-3',
+	command: [
+		gen_doxygen,
+		'--builddir=@OUTDIR@',
+		'--output-format=man3',
+		'--stamp=doc/doxygen/man3.stamp',
+		wayland_doxygen,
+		'@INPUT@',
+	],
+	input: man_files,
+	output: 'man3',
+	build_by_default: true,
+	install: true,
+	install_dir: join_paths(get_option('prefix'), get_option('mandir')),
+)
diff --git a/doc/doxygen/xml/Client/meson.build b/doc/doxygen/xml/Client/meson.build
new file mode 100644
index 0000000..849c30d
--- /dev/null
+++ b/doc/doxygen/xml/Client/meson.build
@@ -0,0 +1,18 @@
+tgt = custom_target(
+	'xml-Client-doc',
+	command: [
+		gen_doxygen,
+		# XXX pass doxygen path as argument
+		'--builddir=@OUTDIR@',
+		'--section=Client',
+		'--output-format=xml',
+		wayland_doxygen,
+		'@INPUT@',
+	],
+	input: [ shared_files, client_files ],
+	output: [ 'combine.xslt', 'index.xml' ],
+	depends: [dot_png, dot_map]
+)
+
+doxygen_Client_combine_xslt = tgt[0]
+doxygen_Client_index_xml = tgt[1]
diff --git a/doc/doxygen/xml/Server/meson.build b/doc/doxygen/xml/Server/meson.build
new file mode 100644
index 0000000..4792c1b
--- /dev/null
+++ b/doc/doxygen/xml/Server/meson.build
@@ -0,0 +1,18 @@
+tgt = custom_target(
+	'xml-Server-doc',
+	command: [
+		gen_doxygen,
+		# XXX pass doxygen path as argument
+		'--builddir=@OUTDIR@',
+		'--section=Server',
+		'--output-format=xml',
+		wayland_doxygen,
+		'@INPUT@',
+	],
+	input: [ shared_files, server_files ],
+	output: [ 'combine.xslt', 'index.xml' ],
+	depends: [dot_png, dot_map]
+)
+
+doxygen_Server_combine_xslt = tgt[0]
+doxygen_Server_index_xml = tgt[1]
diff --git a/doc/doxygen/xml/meson.build b/doc/doxygen/xml/meson.build
new file mode 100644
index 0000000..6d55c53
--- /dev/null
+++ b/doc/doxygen/xml/meson.build
@@ -0,0 +1,22 @@
+# dot_png: list of PNG targets
+# dot_map: list of MAP targets
+foreach name, infile: dot_gv
+	dot_png += custom_target(
+		name + '.png',
+		command: [ dot, '-Tpng', '-o@OUTPUT@', '@INPUT@' ],
+		input: infile,
+		output: name + '.png',
+		install: true,
+		install_dir: join_paths(publican_install_prefix, publican_html_dir, 'images')
+	)
+
+	dot_map += custom_target(
+		name + '.map',
+		command: [ dot, '-Tcmapx_np', '-o@OUTPUT@', '@INPUT@' ],
+		input: infile,
+		output: name + '.map',
+	)
+endforeach
+
+subdir('Client')
+subdir('Server')
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
deleted file mode 100644
index 41665eb..0000000
--- a/doc/man/Makefile.am
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# This generates man-pages out of the Docbook XML files. Simply add your files
-# to the $MANPAGES array. If aliases are created, please add them to the
-# MANPAGES_ALIASES array so they get installed correctly.
-#
-
-MANPAGES = \
-	wl_display_connect.3
-MANPAGES_ALIASES = \
-	wl_display_connect_to_fd.3
-
-XML_FILES = \
-	${patsubst %.1,%.xml,${patsubst %.3,%.xml,${patsubst %.5,%.xml,${patsubst %.7,%.xml,$(MANPAGES)}}}}
-CLEANFILES =
-EXTRA_DIST = $(XML_FILES)
-
-if HAVE_XSLTPROC
-if HAVE_MANPAGES_STYLESHEET
-
-CLEANFILES += $(MANPAGES) $(MANPAGES_ALIASES)
-EXTRA_DIST += $(MANPAGES) $(MANPAGES_ALIASES)
-dist_man_MANS = $(MANPAGES) $(MANPAGES_ALIASES)
-
-XSLTPROC_FLAGS = \
-	--stringparam man.authors.section.enabled 0 \
-	--stringparam man.copyright.section.enabled 0 \
-	--stringparam funcsynopsis.style ansi \
-	--stringparam man.output.quietly 1 \
-	--nonet
-
-XSLTPROC_PROCESS_MAN = \
-	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
-	$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(MANPAGES_STYLESHEET) $< && \
-	$(SED) -i -e 's/^\.so \(.*\)\.\(.\)$$/\.so man\2\/\1\.\2/' $(MANPAGES_ALIASES)
-
-%.1: %.xml
-	$(XSLTPROC_PROCESS_MAN)
-
-%.3: %.xml
-	$(XSLTPROC_PROCESS_MAN)
-
-%.5: %.xml
-	$(XSLTPROC_PROCESS_MAN)
-
-%.7: %.xml
-	$(XSLTPROC_PROCESS_MAN)
-
-wl_display_connect_to_fd.3: wl_display_connect.3
-
-endif # HAVE_MANPAGES_STYLESHEET
-endif # HAVE_XSLTPROC
diff --git a/doc/man/wl_display_connect.xml b/doc/man/wl_display_connect.xml
deleted file mode 100644
index dab4ddb..0000000
--- a/doc/man/wl_display_connect.xml
+++ /dev/null
@@ -1,112 +0,0 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-          "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-
-<!--
-  Written 2012 by David Herrmann <dh.herrmann@googlemail.com>
-  Dedicated to the Public Domain
--->
-
-<refentry id="wl_display_connect">
-  <refentryinfo>
-    <title>wl_display_connect</title>
-    <productname>wayland-client</productname>
-    <date>September 2012</date>
-    <authorgroup>
-      <author>
-        <contrib>Developer</contrib>
-        <firstname>David</firstname>
-        <surname>Herrmann</surname>
-        <email>dh.herrmann@googlemail.com</email>
-      </author>
-    </authorgroup>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>wl_display_connect</refentrytitle>
-    <manvolnum>3</manvolnum>
-  </refmeta>
-
-  <refnamediv>
-    <refname>wl_display_connect</refname>
-    <refname>wl_display_connect_to_fd</refname>
-    <refpurpose>Connect to a Wayland socket</refpurpose>
-  </refnamediv>
-
-  <refsynopsisdiv>
-    <funcsynopsis>
-
-      <funcsynopsisinfo>#include &lt;wayland-client.h&gt;</funcsynopsisinfo>
-
-      <funcprototype>
-        <funcdef>struct wl_display *<function>wl_display_connect</function></funcdef>
-        <paramdef>const char *<parameter>name</parameter></paramdef>
-      </funcprototype>
-
-      <funcprototype>
-        <funcdef>struct wl_display *<function>wl_display_connect_to_fd</function></funcdef>
-        <paramdef>int <parameter>fd</parameter></paramdef>
-      </funcprototype>
-
-    </funcsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>Description</title>
-    <para><function>wl_display_connect</function> connects to a Wayland socket
-          that was previously opened by a Wayland server. The server socket must
-          be placed in <envar>XDG_RUNTIME_DIR</envar> when <envar>WAYLAND_DISPLAY</envar>
-	  (or <varname>name</varname>, see below) is a simple name, for this
-	  function to find it. The server socket is also allowed to exist at an
-	  arbitrary path; usage details follow. See below for compatibility issue
-	  details.</para>
-
-    <para>The <varname>name</varname> argument specifies the name of
-          the socket or <constant>NULL</constant> to use the default (which is
-          <constant>"wayland-0"</constant>). The environment variable
-          <envar>WAYLAND_DISPLAY</envar> replaces the default value.
-
-	  If <varname>name</varname> is an absolute path, then that path is used
-	  as the Wayland socket to which the connection is attempted. Note that
-	  in combination with the default-value behavior described above, this
-	  implies that setting <envar>WAYLAND_DISPLAY</envar> to an absolute
-	  path will implicitly cause <varname>name</varname> to take on that
-	  absolute path if <varname>name</varname> is <constant>NULL</constant>.
-
-          If <envar>WAYLAND_SOCKET</envar> is set, this function behaves like
-          <function>wl_display_connect_to_fd</function> with the file-descriptor
-          number taken from the environment variable.</para>
-
-    <para>Support for interpreting <envar>WAYLAND_DISPLAY</envar> as an
-          absolute path is a change in behavior compared to
-          <function>wl_display_connect</function>'s behavior in versions
-          1.14 and older of Wayland. It is no longer guaranteed in versions
-          1.15 and higher that the Wayland socket chosen is equivalent to
-          manually constructing a socket pathname by concatenating
-          <envar>XDG_RUNTIME_DIR</envar> and <envar>WAYLAND_DISPLAY</envar>.
-          Manual construction of the socket path must account for the
-          possibility that <envar>WAYLAND_DISPLAY</envar> contains an absolute
-          path.</para>
-
-    <para><function>wl_display_connect_to_fd</function> connects to a Wayland
-          socket with an explicit file-descriptor. The file-descriptor is passed
-          as argument <varname>fd</varname>.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>Return Value</title>
-    <para><function>wl_display_connect</function> and
-          <function>wl_display_connect_to_fd</function> return a new display
-          context object or NULL on failure. <varname>errno</varname> is set
-          correspondingly.</para>
-  </refsect1>
-
-  <refsect1>
-    <title>See Also</title>
-    <para>
-      <citerefentry><refentrytitle>wayland-client</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>wl_display_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>wl_display_iterate</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    </para>
-  </refsect1>
-</refentry>
diff --git a/doc/meson.build b/doc/meson.build
new file mode 100644
index 0000000..f74b6b1
--- /dev/null
+++ b/doc/meson.build
@@ -0,0 +1,37 @@
+dot = find_program('dot')
+doxygen = find_program('doxygen')
+xsltproc = find_program('xsltproc')
+xmlto = find_program('xmlto')
+
+cmd = run_command(doxygen, '--version', check: true)
+message('doxygen: ' + cmd.stdout().strip())
+vers = cmd.stdout().strip()
+if vers.version_compare('< 1.6.0')
+	error('Doxygen 1.6 or later is required for building documentation, found @0@.'.format(vers))
+endif
+
+cmd = run_command(dot, '-V', check: true)
+message('dot: ' + cmd.stderr().strip())
+vers = cmd.stderr().split('version')[1].strip().split(' ')[0]
+if vers.version_compare('< 2.26.0')
+	error('Dot (Graphviz) 2.26 or later is required for building documentation, found @0@.'.format(vers))
+endif
+
+manpage_xsl = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
+cmd = run_command(xsltproc, '--nonet', manpage_xsl)
+if cmd.returncode() != 0
+	error('The style sheet for man pages providing "@0@" was not found.'.format(manpage_xsl))
+endif
+
+publican_install_prefix = join_paths(
+	get_option('prefix'),
+	get_option('datadir'),
+	'doc',
+	meson.project_name(),
+	'Wayland', 'en-US'
+)
+
+publican_html_dir = 'html'
+
+subdir('doxygen')
+subdir('publican')
diff --git a/doc/publican/meson.build b/doc/publican/meson.build
new file mode 100644
index 0000000..898ca4f
--- /dev/null
+++ b/doc/publican/meson.build
@@ -0,0 +1,30 @@
+merge_mapcoords_xsl = files('merge-mapcoords.xsl')
+
+subdir('sources')
+
+custom_target(
+	'Wayland-docbook-html',
+	command: [
+		xmlto,
+		'--skip-validation',
+		'--stringparam', 'chunk.section.depth=0',
+		'--stringparam', 'toc.section.depth=1',
+		'--stringparam', 'html.stylesheet=css/default.css',
+		'-o', '@OUTPUT@',
+		'html',
+		'@INPUT@'
+	],
+	input: publican_processed_main,
+	output: publican_html_dir,
+	depend_files: publican_copied_sources,
+	depends: [
+		publican_processed_targets,
+		ClientAPI_xml,
+		ServerAPI_xml,
+		ProtocolSpec_xml,
+		ProtocolInterfaces_xml
+	],
+	build_by_default: true,
+	install: true,
+	install_dir: publican_install_prefix
+)
diff --git a/doc/publican/sources/Book_Info.xml b/doc/publican/sources/Book_Info.xml
index 0b7bf07..897673a 100644
--- a/doc/publican/sources/Book_Info.xml
+++ b/doc/publican/sources/Book_Info.xml
@@ -28,9 +28,9 @@
 	<imagedata fileref="images/wayland.png" format="PNG" />
       </imageobject>
       <textobject>
-        <phrase>
-          Wayland logo
-        </phrase>
+	<phrase>
+	  Wayland logo
+	</phrase>
       </textobject>
     </inlinemediaobject>
   </corpauthor>
diff --git a/doc/publican/sources/Compositors.xml b/doc/publican/sources/Compositors.xml
index eea627c..7a7bdaa 100644
--- a/doc/publican/sources/Compositors.xml
+++ b/doc/publican/sources/Compositors.xml
@@ -52,7 +52,7 @@
       run nested under the system compositor. Nesting is feasible because
       the protocol is asynchronous; roundtrips would be too expensive
       when nesting is involved. If no system compositor is present, a
-      session compositor can run directly on the hw.
+      session compositor can run directly on the hardware.
      </para>
      <para>
       X applications can continue working under a session compositor
@@ -61,8 +61,8 @@
     <para>
        Possible examples for session compositors include
       <itemizedlist>
-        <listitem>
-          <para>
+	<listitem>
+	  <para>
 	    gnome-shell
 	  </para>
 	</listitem>
diff --git a/doc/publican/sources/Foreword.xml b/doc/publican/sources/Foreword.xml
index e6875a3..46fda2b 100644
--- a/doc/publican/sources/Foreword.xml
+++ b/doc/publican/sources/Foreword.xml
@@ -22,7 +22,7 @@
   <literallayout>
 Yours,
 
-        the Wayland open-source community
-        November 2012
+	the Wayland open-source community
+	November 2012
   </literallayout>
 </preface>
diff --git a/doc/publican/sources/Introduction.xml b/doc/publican/sources/Introduction.xml
index 7516c33..276db2d 100644
--- a/doc/publican/sources/Introduction.xml
+++ b/doc/publican/sources/Introduction.xml
@@ -33,9 +33,9 @@
 	<imagedata fileref="images/x-architecture.png" format="PNG" />
       </imageobject>
       <textobject>
-        <phrase>
-          X architecture diagram
-        </phrase>
+	<phrase>
+	  X architecture diagram
+	</phrase>
       </textobject>
     </mediaobject>
     <para>
@@ -96,9 +96,9 @@
 	<imagedata fileref="images/wayland-architecture.png" format="PNG" />
       </imageobject>
       <textobject>
-        <phrase>
-          Wayland architecture diagram
-        </phrase>
+	<phrase>
+	  Wayland architecture diagram
+	</phrase>
       </textobject>
     </mediaobject>
     <para>
diff --git a/doc/publican/sources/Preface.xml b/doc/publican/sources/Preface.xml
index 61720a9..17c6ebf 100644
--- a/doc/publican/sources/Preface.xml
+++ b/doc/publican/sources/Preface.xml
@@ -15,6 +15,6 @@
   <literallayout>
 Best,
 
-        Kristian Høgsberg
+	Kristian Høgsberg
   </literallayout>
 </preface>
diff --git a/doc/publican/sources/Protocol.xml b/doc/publican/sources/Protocol.xml
index fedaaab..a472f1d 100644
--- a/doc/publican/sources/Protocol.xml
+++ b/doc/publican/sources/Protocol.xml
@@ -167,10 +167,10 @@
 	  <term>new_id</term>
 	  <listitem>
 	    <para>
-	      The 32-bit object ID.  On requests, the client
-	      decides the ID.  The only events with <type>new_id</type> are
-	      advertisements of globals, and the server will use IDs below
-	      0x10000.
+	      The 32-bit object ID.  Generally, the interface used for the new
+	      object is inferred from the xml, but in the case where it's not
+	      specified, a new_id is preceeded by a <code>string</code> specifying
+	      the interface name, and a <code>uint</code> specifying the version.
 	    </para>
 	  </listitem>
 	</varlistentry>
@@ -401,10 +401,11 @@
       set the pointer image.
     </para>
     <para>
+      Setting the pointer image to NULL causes the cursor to be hidden.
+    </para>
+    <para>
       The compositor will revert the pointer image back to a default image
-      when no surface has the pointer focus for that device.  Clients can
-      revert the pointer image back to the default image by setting a NULL
-      image.
+      when no surface has the pointer focus for that device.
     </para>
     <para>
       What if the pointer moves from one window which has set a special
@@ -412,7 +413,10 @@
       the motion event?  The new surface will be stuck with the special
       pointer image.  We can't just revert the pointer image on leaving a
       surface, since if we immediately enter a surface that sets a different
-      image, the image will flicker.  Broken app, I suppose.
+      image, the image will flicker.  If a client does not set a pointer image
+      when the pointer enters a surface, the pointer stays with the image set
+      by the last surface that changed it, possibly even hidden.  Such a client
+      is likely just broken.
     </para>
   </section>
   <section id="sect-Protocol-Output">
diff --git a/doc/publican/sources/Xwayland.xml b/doc/publican/sources/Xwayland.xml
index a39866f..e439991 100644
--- a/doc/publican/sources/Xwayland.xml
+++ b/doc/publican/sources/Xwayland.xml
@@ -78,11 +78,11 @@
     <figure>
       <title>Xwayland architecture diagram</title>
       <mediaobjectco>
-        <imageobjectco>
-          <imageobject>
-            <imagedata fileref="images/xwayland-architecture.png" format="PNG" />
-          </imageobject>
-        </imageobjectco>
+	<imageobjectco>
+	  <imageobject>
+	    <imagedata fileref="images/xwayland-architecture.png" format="PNG" />
+	  </imageobject>
+	</imageobjectco>
       </mediaobjectco>
     </figure>
     <para>
@@ -150,20 +150,20 @@
     <section id="sect-X11-Application-Support-xwm-window-identification">
       <title>Window identification</title>
       <para>
-        In Xwayland, an X11 window may have a corresponding wl_surface object
-        in Wayland. The wl_surface object is used for input and output: it is
-        referenced by input events and used to provide the X11 window content
-        to the Wayland compositor. The X11 window and the wl_surface live in
-        different protocol streams, and they need to be matched for XWM to do
-        its job.
+	In Xwayland, an X11 window may have a corresponding wl_surface object
+	in Wayland. The wl_surface object is used for input and output: it is
+	referenced by input events and used to provide the X11 window content
+	to the Wayland compositor. The X11 window and the wl_surface live in
+	different protocol streams, and they need to be matched for XWM to do
+	its job.
       </para>
       <para>
-        When Xwayland creates a wl_surface on Wayland, it will also send an X11
-        ClientMessage of type atom "WL_SURFACE_ID" to the X11 window carrying
-        the wl_surface Wayland object ID as the first 32-bit data element. This
-        is how XWM can associate a wl_surface with an X11 window. Note that
-        the request to create a wl_surface and the ID message may arrive in any
-        order in the Wayland compositor.
+	When Xwayland creates a wl_surface on Wayland, it will also send an X11
+	ClientMessage of type atom "WL_SURFACE_ID" to the X11 window carrying
+	the wl_surface Wayland object ID as the first 32-bit data element. This
+	is how XWM can associate a wl_surface with an X11 window. Note that
+	the request to create a wl_surface and the ID message may arrive in any
+	order in the Wayland compositor.
       </para>
     </section>
   </section>
diff --git a/doc/publican/sources/meson.build b/doc/publican/sources/meson.build
new file mode 100644
index 0000000..52f3a68
--- /dev/null
+++ b/doc/publican/sources/meson.build
@@ -0,0 +1,113 @@
+ProtocolSpec_xml = custom_target(
+	'ProtocolSpec.xml',
+	command: [ xsltproc, '-o', '@OUTPUT@', files('../protocol-to-docbook.xsl'), '@INPUT@' ],
+	input: wayland_protocol_xml,
+	output: 'ProtocolSpec.xml'
+)
+
+ProtocolInterfaces_xml = custom_target(
+	'ProtocolInterfaces.xml',
+	command: [ xsltproc, '-o', '@OUTPUT@', files('../protocol-interfaces-to-docbook.xsl'), '@INPUT@' ],
+	input: wayland_protocol_xml,
+	output: 'ProtocolInterfaces.xml'
+)
+
+ClientAPI_combined = custom_target(
+	'ClientAPI-combined',
+	command: [ xsltproc, '-o', '@OUTPUT@', '@INPUT@' ],
+	input: [ doxygen_Client_combine_xslt, doxygen_Client_index_xml ],
+	output: 'ClientAPI-combined.xml'
+)
+
+to_publican_xsl = files('../doxygen-to-publican.xsl')
+
+ClientAPI_xml = custom_target(
+	'ClientAPI.xml',
+	command: [ xsltproc, '-o', '@OUTPUT@', '--stringparam', 'which', 'Client', to_publican_xsl, '@INPUT@' ],
+	input: ClientAPI_combined,
+	output: 'ClientAPI.xml'
+)
+
+ServerAPI_combined = custom_target(
+	'ServerAPI-combined',
+	command: [ xsltproc, '-o', '@OUTPUT@', '@INPUT@' ],
+	input: [ doxygen_Server_combine_xslt, doxygen_Server_index_xml ],
+	output: 'ServerAPI-combined.xml'
+)
+
+ServerAPI_xml = custom_target(
+	'ServerAPI.xml',
+	command: [ xsltproc, '-o', '@OUTPUT@', '--stringparam', 'which', 'Server', to_publican_xsl, '@INPUT@' ],
+	input: ServerAPI_combined,
+	output: 'ServerAPI.xml'
+)
+
+
+publican_sources = [
+	'Wayland.ent',
+	# 'Wayland.xml', # handled specially
+	'Book_Info.xml',
+	'Author_Group.xml',
+	'Foreword.xml',
+	'Preface.xml',
+	'Revision_History.xml',
+	'Protocol.xml',
+	'Xwayland.xml',
+	'Compositors.xml',
+	'Client.xml',
+	'Server.xml'
+]
+
+publican_processed_main = configure_file(
+	input: 'Wayland.xml',
+	output: 'Wayland.xml',
+	copy: true
+)
+
+publican_copied_sources = []
+foreach src: publican_sources
+	publican_copied_sources += configure_file(
+		input: src,
+		output: src,
+		copy: true
+	)
+endforeach
+
+publican_processed_sources = [
+	'Architecture.xml',
+	'Introduction.xml'
+]
+
+publican_processed_targets = []
+foreach src: publican_processed_sources
+	publican_processed_targets += custom_target(
+		src,
+		command: [ xsltproc, '-o', '@OUTPUT@', '--stringparam', 'basedir', '.', merge_mapcoords_xsl, '@INPUT@' ],
+		input: src,
+		output: src
+	)
+endforeach
+
+publican_css_sources = files([
+	'css/brand.css',
+	'css/common.css',
+	'css/default.css',
+	'css/epub.css',
+	'css/print.css'
+])
+
+install_data(
+	publican_css_sources,
+	install_dir: join_paths(publican_install_prefix, publican_html_dir, 'css')
+)
+
+publican_img_sources = files([
+	'images/icon.svg',
+	'images/wayland.png',
+	'images/xwayland-architecture.png'
+])
+
+install_data(
+	publican_img_sources,
+	install_dir: join_paths(publican_install_prefix, publican_html_dir, 'images')
+)
diff --git a/egl/meson.build b/egl/meson.build
new file mode 100644
index 0000000..dee9b1d
--- /dev/null
+++ b/egl/meson.build
@@ -0,0 +1,43 @@
+wayland_egl = library(
+	'wayland-egl',
+	sources: [
+		'wayland-egl.c',
+		wayland_client_protocol_h
+	],
+	include_directories: src_inc,
+	version: '1.0.0',
+	install: true
+)
+
+executable('wayland-egl-abi-check', 'wayland-egl-abi-check.c')
+
+nm_path = find_program('nm').path()
+
+test(
+	'wayland-egl symbols check',
+	find_program('wayland-egl-symbols-check'),
+	env: [
+		'WAYLAND_EGL_LIB=@0@'.format(wayland_egl.full_path()),
+		'NM=@0@'.format(nm_path)
+	]
+)
+
+install_headers([
+	'wayland-egl.h',
+	'wayland-egl-core.h',
+	'wayland-egl-backend.h'
+])
+
+pkgconfig.generate(
+	name: 'wayland-egl',
+	description: 'Frontend wayland-egl library',
+	version: '18.1.0',
+	requires: 'wayland-client',
+	libraries: wayland_egl
+)
+
+pkgconfig.generate(
+	name: 'wayland-egl-backend',
+	description: 'Backend wayland-egl interface',
+	version: '3'
+)
diff --git a/m4/weston.m4 b/m4/weston.m4
new file mode 100644
index 0000000..636f9fb
--- /dev/null
+++ b/m4/weston.m4
@@ -0,0 +1,37 @@
+dnl
+dnl Copyright © 2016 Quentin “Sardem FF7” Glidic
+dnl
+dnl Permission is hereby granted, free of charge, to any person obtaining a
+dnl copy of this software and associated documentation files (the "Software"),
+dnl to deal in the Software without restriction, including without limitation
+dnl the rights to use, copy, modify, merge, publish, distribute, sublicense,
+dnl and/or sell copies of the Software, and to permit persons to whom the
+dnl Software is furnished to do so, subject to the following conditions:
+dnl
+dnl The above copyright notice and this permission notice (including the next
+dnl paragraph) shall be included in all copies or substantial portions of the
+dnl Software.
+dnl
+dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+dnl IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+dnl FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+dnl THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+dnl LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+dnl FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+dnl DEALINGS IN THE SOFTWARE.
+dnl
+
+dnl WESTON_SEARCH_LIBS(PREFIX, search-libs, function, [action-if-found], [action-if-not-found], [other-libraries])
+dnl WESTON_SEARCH_LIBS is a wrapper around AC_SEARCH_LIBS with a little difference:
+dnl action-if-found is called even if no library is required
+AC_DEFUN([WESTON_SEARCH_LIBS], [
+	weston_save_LIBS=${LIBS}
+	AC_SEARCH_LIBS([$3], [$2], [$4], [$5], [$6])
+	AS_CASE([${ac_cv_search_][$3][}],
+		['none required'], [$4],
+		[no], [],
+		[$1][_LIBS=${ac_cv_search_][$3][}]
+	)
+	AC_SUBST([$1][_LIBS])
+	LIBS=${weston_save_LIBS}
+])
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..b67b101
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,103 @@
+project(
+	'wayland', 'c', 'cpp',
+	version: '1.18.90',
+	license: 'MIT',
+	meson_version: '>= 0.52.1',
+	default_options: [
+		'warning_level=2',
+		'buildtype=debugoptimized'
+	]
+)
+
+config_h = configuration_data()
+config_h.set_quoted('PACKAGE', meson.project_name())
+config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
+
+compiler_flags = [
+	'-Wno-unused-parameter',
+	'-Wstrict-prototypes',
+	'-Wmissing-prototypes',
+	'-fvisibility=hidden',
+]
+
+cc = meson.get_compiler('c')
+add_project_arguments(
+	cc.get_supported_arguments(compiler_flags),
+	language: 'c'
+)
+
+foreach h: [ 'sys/prctl.h' ]
+	config_h.set('HAVE_' + h.underscorify().to_upper(), cc.has_header(h))
+endforeach
+
+have_funcs = [
+	'accept4',
+	'mkostemp',
+	'posix_fallocate',
+	'prctl',
+	'memfd_create',
+	'strndup',
+]
+foreach f: have_funcs
+	config_h.set('HAVE_' + f.underscorify().to_upper(), cc.has_function(f))
+endforeach
+
+if get_option('libraries')
+	ffi_dep = dependency('libffi')
+
+	decls = [
+		{ 'header': 'sys/signalfd.h', 'symbol': 'SFD_CLOEXEC' },
+		{ 'header': 'sys/timerfd.h', 'symbol': 'TFD_CLOEXEC' },
+		{ 'header': 'time.h', 'symbol': 'CLOCK_MONOTONIC' },
+	]
+
+	foreach d: decls
+		if not cc.has_header_symbol(d['header'], d['symbol'])
+			error('@0@ is needed to compile Wayland libraries'.format(d['symbol']))
+		endif
+	endforeach
+endif
+
+scanner_deps = [ dependency('expat') ]
+
+if get_option('dtd_validation')
+	scanner_deps += dependency('libxml-2.0')
+	config_h.set('HAVE_LIBXML', 1)
+endif
+
+configure_file(
+	output: 'config.h',
+	configuration: config_h,
+)
+
+pkgconfig = import('pkgconfig')
+
+wayland_protocol_xml = files('protocol/wayland.xml')
+
+root_inc = include_directories('.')
+protocol_inc = include_directories('protocol')
+src_inc = include_directories('src')
+
+subdir('src')
+
+if get_option('libraries')
+	subdir('cursor')
+	subdir('egl')
+	subdir('tests')
+	if get_option('documentation')
+		subdir('doc')
+	endif
+endif
+
+if get_option('scanner')
+	install_data([
+		'wayland-scanner.mk',
+		'protocol/wayland.xml',
+		'protocol/wayland.dtd',
+	])
+
+	install_data(
+		[ 'wayland-scanner.m4' ],
+		install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'aclocal'),
+	)
+endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..de588d1
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,20 @@
+option('libraries',
+  description: 'Compile Wayland libraries',
+  type: 'boolean',
+  value: 'true')
+option('scanner',
+  description: 'Compile wayland-scanner binary',
+  type: 'boolean',
+  value: 'true')
+option('documentation',
+  description: 'Build the documentation (requires Doxygen, dot, xmlto, xsltproc)',
+  type: 'boolean',
+  value: 'true')
+option('dtd_validation',
+  description: 'Validate the protocol DTD (requires libxml2)',
+  type: 'boolean',
+  value: 'true')
+option('icon_directory',
+  description: 'Location used to look for cursors (defaults to ${datadir}/icons if unset)',
+  type: 'string',
+  value: '')
diff --git a/protocol/generate-shm-formats.py b/protocol/generate-shm-formats.py
new file mode 100755
index 0000000..c56642e
--- /dev/null
+++ b/protocol/generate-shm-formats.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python3
+
+# This script synchronizes wayland.xml's wl_shm.format enum with drm_fourcc.h.
+# Invoke it to update wayland.xml, then manually check the changes applied.
+#
+# Requires Python 3, python-lxml, a C compiler and pkg-config.
+
+import os
+import subprocess
+import sys
+import tempfile
+# We need lxml instead of the standard library because we want
+# Element.sourceline
+from lxml import etree as ElementTree
+
+proto_dir = os.path.dirname(os.path.realpath(__file__))
+wayland_proto = proto_dir + "/wayland.xml"
+
+cc = os.getenv("CC", "cc")
+pkg_config = os.getenv("PKG_CONFIG", "pkg-config")
+
+# Find drm_fourcc.h
+version = subprocess.check_output([pkg_config, "libdrm",
+    "--modversion"]).decode().strip()
+cflags = subprocess.check_output([pkg_config, "libdrm",
+    "--cflags-only-I"]).decode().strip().split()
+libdrm_include = None
+for include_flag in cflags:
+    if not include_flag.startswith("-I"):
+        raise Exception("Expected one include dir for libdrm")
+    include_dir = include_flag[2:]
+    if include_dir.endswith("/libdrm"):
+        libdrm_include = include_dir
+        fourcc_include = libdrm_include + "/drm_fourcc.h"
+if libdrm_include == None:
+    raise Exception("Failed to find libdrm include dir")
+
+print("Using libdrm " + version, file=sys.stderr)
+
+def drm_format_to_wl(ident):
+    return ident.replace("DRM_FORMAT_", "").lower()
+
+# Collect DRM format constant names
+ident_list = []
+descriptions = {}
+prev_comment = None
+with open(fourcc_include) as input_file:
+    for l in input_file.readlines():
+        l = l.strip()
+
+        # Collect comments right before format definitions
+        if l.startswith("/*") and l.endswith("*/"):
+            prev_comment = l[2:-2]
+            continue
+        desc = prev_comment
+        prev_comment = None
+
+        # Recognize format definitions
+        parts = l.split()
+        if len(parts) < 3 or parts[0] != "#define":
+            continue
+        ident = parts[1]
+        if not ident.startswith("DRM_FORMAT_") or ident.startswith(
+                "DRM_FORMAT_MOD_"):
+            continue
+
+        ident_list.append(ident)
+
+        # Prefer in-line comments
+        if l.endswith("*/"):
+            desc = l[l.rfind("/*") + 2:-2]
+        if desc != None:
+            descriptions[drm_format_to_wl(ident)] = desc.strip()
+
+# Collect DRM format values
+idents = {}
+with tempfile.TemporaryDirectory() as work_dir:
+    c_file_name = work_dir + "/print-formats.c"
+    exe_file_name = work_dir + "/print-formats"
+
+    with open(c_file_name, "w+") as c_file:
+        c_file.write('#include <inttypes.h>\n')
+        c_file.write('#include <stdint.h>\n')
+        c_file.write('#include <stdio.h>\n')
+        c_file.write('#include <drm_fourcc.h>\n')
+        c_file.write('\n')
+        c_file.write('int main(void) {\n')
+        for ident in ident_list:
+            c_file.write('printf("0x%" PRIX64 "\\n", (uint64_t)' + ident + ');\n')
+        c_file.write('}\n')
+
+    subprocess.check_call([cc, "-Wall", "-Wextra", "-o", exe_file_name,
+        c_file_name] + cflags)
+    output = subprocess.check_output([exe_file_name]).decode().strip()
+    for i, val in enumerate(output.splitlines()):
+        idents[ident_list[i]] = val
+
+# We don't need those
+del idents["DRM_FORMAT_BIG_ENDIAN"]
+del idents["DRM_FORMAT_INVALID"]
+del idents["DRM_FORMAT_RESERVED"]
+
+# Convert from DRM constants to Wayland wl_shm.format entries
+formats = {}
+for ident, val in idents.items():
+    formats[drm_format_to_wl(ident)] = val.lower()
+# Special case for ARGB8888 and XRGB8888
+formats["argb8888"] = "0"
+formats["xrgb8888"] = "1"
+
+print("Loaded {} formats from drm_fourcc.h".format(len(formats)), file=sys.stderr)
+
+tree = ElementTree.parse("wayland.xml")
+root = tree.getroot()
+wl_shm_format = root.find("./interface[@name='wl_shm']/enum[@name='format']")
+if wl_shm_format == None:
+    raise Exception("wl_shm.format not found in wayland.xml")
+
+# Remove formats we already know about
+last_line = None
+for node in wl_shm_format:
+    if node.tag != "entry":
+        continue
+    fmt = node.attrib["name"]
+    val = node.attrib["value"]
+    if fmt not in formats:
+        raise Exception("Format present in wl_shm.formats but not in "
+            "drm_fourcc.h: " + fmt)
+    if val != formats[fmt]:
+        raise Exception("Format value in wl_shm.formats ({}) differs "
+            "from value in drm_fourcc.h ({}) for format {}"
+            .format(val, formats[fmt], fmt))
+    del formats[fmt]
+    last_line = node.sourceline
+if last_line == None:
+    raise Exception("Expected at least one existing wl_shm.format entry")
+
+print("Adding {} formats to wayland.xml...".format(len(formats)), file=sys.stderr)
+
+# Append new formats
+new_wayland_proto = wayland_proto + ".new"
+with open(new_wayland_proto, "w+") as output_file, \
+        open(wayland_proto) as input_file:
+    for i, l in enumerate(input_file.readlines()):
+        output_file.write(l)
+        if i + 1 == last_line:
+            for fmt, val in formats.items():
+                output_file.write('      <entry name="{}" value="{}"'
+                    .format(fmt, val))
+                if fmt in descriptions:
+                    output_file.write(' summary="{}"'.format(descriptions[fmt]))
+                output_file.write('/>\n')
+os.rename(new_wayland_proto, wayland_proto)
diff --git a/protocol/tests.xml b/protocol/tests.xml
index ea56ae4..22d80a1 100644
--- a/protocol/tests.xml
+++ b/protocol/tests.xml
@@ -43,8 +43,8 @@
     <!-- Version 2 additions -->
     <request name="conjoin" since="2">
       <description summary="register another fd passer with this one">
-        Tells this fd passer object about another one to send events
-        to for more complicated fd leak tests.
+	Tells this fd passer object about another one to send events
+	to for more complicated fd leak tests.
       </description>
       <arg name="passer" type="object" interface="fd_passer"/>
     </request>
diff --git a/protocol/wayland.dtd.c b/protocol/wayland.dtd.c
index 73d3d1b..85a7f68 100644
--- a/protocol/wayland.dtd.c
+++ b/protocol/wayland.dtd.c
@@ -1,4 +1,4 @@
-// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/protocol/wayland.xml b/protocol/wayland.xml
index 141038b..09e4f4c 100644
--- a/protocol/wayland.xml
+++ b/protocol/wayland.xml
@@ -91,18 +91,20 @@
       <entry name="invalid_object" value="0"
 	     summary="server couldn't find object"/>
       <entry name="invalid_method" value="1"
-	     summary="method doesn't exist on the specified interface"/>
+	     summary="method doesn't exist on the specified interface or malformed request"/>
       <entry name="no_memory" value="2"
 	     summary="server is out of memory"/>
+      <entry name="implementation" value="3"
+	     summary="implementation error in compositor"/>
     </enum>
 
     <event name="delete_id">
       <description summary="acknowledge object ID deletion">
 	This event is used internally by the object ID management
-	logic.  When a client deletes an object, the server will send
-	this event to acknowledge that it has seen the delete request.
-	When the client receives this event, it will know that it can
-	safely reuse the object ID.
+	logic. When a client deletes an object that it had created,
+	the server will send this event to acknowledge that it has
+	seen the delete request. When the client receives this event,
+	it will know that it can safely reuse the object ID.
       </description>
       <arg name="id" type="uint" summary="deleted object ID"/>
     </event>
@@ -291,10 +293,12 @@
 	formats are optional and may not be supported by the particular
 	renderer in use.
 
-	The drm format codes match the macros defined in drm_fourcc.h.
-	The formats actually supported by the compositor will be
-	reported by the format event.
+	The drm format codes match the macros defined in drm_fourcc.h, except
+	argb8888 and xrgb8888. The formats actually supported by the compositor
+	will be reported by the format event.
       </description>
+      <!-- Note to protocol writers: don't update this list manually, instead
+	   run the automated script that keeps it in sync with drm_fourcc.h. -->
       <entry name="argb8888" value="0" summary="32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian"/>
       <entry name="xrgb8888" value="1" summary="32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian"/>
       <entry name="c8" value="0x20203843" summary="8-bit color index format, [7:0] C"/>
@@ -353,6 +357,48 @@
       <entry name="yvu422" value="0x36315659" summary="3 plane YCbCr format, 2x1 subsampled Cr (1) and Cb (2) planes"/>
       <entry name="yuv444" value="0x34325559" summary="3 plane YCbCr format, non-subsampled Cb (1) and Cr (2) planes"/>
       <entry name="yvu444" value="0x34325659" summary="3 plane YCbCr format, non-subsampled Cr (1) and Cb (2) planes"/>
+      <entry name="r8" value="0x20203852" summary="[7:0] R"/>
+      <entry name="r16" value="0x20363152" summary="[15:0] R little endian"/>
+      <entry name="rg88" value="0x38384752" summary="[15:0] R:G 8:8 little endian"/>
+      <entry name="gr88" value="0x38385247" summary="[15:0] G:R 8:8 little endian"/>
+      <entry name="rg1616" value="0x32334752" summary="[31:0] R:G 16:16 little endian"/>
+      <entry name="gr1616" value="0x32335247" summary="[31:0] G:R 16:16 little endian"/>
+      <entry name="xrgb16161616f" value="0x48345258" summary="[63:0] x:R:G:B 16:16:16:16 little endian"/>
+      <entry name="xbgr16161616f" value="0x48344258" summary="[63:0] x:B:G:R 16:16:16:16 little endian"/>
+      <entry name="argb16161616f" value="0x48345241" summary="[63:0] A:R:G:B 16:16:16:16 little endian"/>
+      <entry name="abgr16161616f" value="0x48344241" summary="[63:0] A:B:G:R 16:16:16:16 little endian"/>
+      <entry name="xyuv8888" value="0x56555958" summary="[31:0] X:Y:Cb:Cr 8:8:8:8 little endian"/>
+      <entry name="vuy888" value="0x34325556" summary="[23:0] Cr:Cb:Y 8:8:8 little endian"/>
+      <entry name="vuy101010" value="0x30335556" summary="Y followed by U then V, 10:10:10. Non-linear modifier only"/>
+      <entry name="y210" value="0x30313259" summary="[63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels"/>
+      <entry name="y212" value="0x32313259" summary="[63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels"/>
+      <entry name="y216" value="0x36313259" summary="[63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels"/>
+      <entry name="y410" value="0x30313459" summary="[31:0] A:Cr:Y:Cb 2:10:10:10 little endian"/>
+      <entry name="y412" value="0x32313459" summary="[63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian"/>
+      <entry name="y416" value="0x36313459" summary="[63:0] A:Cr:Y:Cb 16:16:16:16 little endian"/>
+      <entry name="xvyu2101010" value="0x30335658" summary="[31:0] X:Cr:Y:Cb 2:10:10:10 little endian"/>
+      <entry name="xvyu12_16161616" value="0x36335658" summary="[63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian"/>
+      <entry name="xvyu16161616" value="0x38345658" summary="[63:0] X:Cr:Y:Cb 16:16:16:16 little endian"/>
+      <entry name="y0l0" value="0x304c3059" summary="[63:0]   A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0  1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian"/>
+      <entry name="x0l0" value="0x304c3058" summary="[63:0]   X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0  1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian"/>
+      <entry name="y0l2" value="0x324c3059" summary="[63:0]   A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0  1:1:10:10:10:1:1:10:10:10 little endian"/>
+      <entry name="x0l2" value="0x324c3058" summary="[63:0]   X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0  1:1:10:10:10:1:1:10:10:10 little endian"/>
+      <entry name="yuv420_8bit" value="0x38305559"/>
+      <entry name="yuv420_10bit" value="0x30315559"/>
+      <entry name="xrgb8888_a8" value="0x38415258"/>
+      <entry name="xbgr8888_a8" value="0x38414258"/>
+      <entry name="rgbx8888_a8" value="0x38415852"/>
+      <entry name="bgrx8888_a8" value="0x38415842"/>
+      <entry name="rgb888_a8" value="0x38413852"/>
+      <entry name="bgr888_a8" value="0x38413842"/>
+      <entry name="rgb565_a8" value="0x38413552"/>
+      <entry name="bgr565_a8" value="0x38413542"/>
+      <entry name="nv24" value="0x3432564e" summary="non-subsampled Cr:Cb plane"/>
+      <entry name="nv42" value="0x3234564e" summary="non-subsampled Cb:Cr plane"/>
+      <entry name="p210" value="0x30313250" summary="2x1 subsampled Cr:Cb plane, 10 bit per channel"/>
+      <entry name="p010" value="0x30313050" summary="2x2 subsampled Cr:Cb plane 10 bits per channel"/>
+      <entry name="p012" value="0x32313050" summary="2x2 subsampled Cr:Cb plane 12 bits per channel"/>
+      <entry name="p016" value="0x36313050" summary="2x2 subsampled Cr:Cb plane 16 bits per channel"/>
     </enum>
 
     <request name="create_pool">
@@ -507,6 +553,9 @@
 	this request after a NULL mime type has been set in
 	wl_data_offer.accept or no action was received through
 	wl_data_offer.action.
+
+	If wl_data_offer.finish request is received for a non drag and drop
+	operation, the invalid_finish protocol error is raised.
       </description>
     </request>
 
@@ -523,7 +572,7 @@
 
 	This request determines the final result of the drag-and-drop
 	operation. If the end result is that no action is accepted,
-	the drag source will receive wl_drag_source.cancelled.
+	the drag source will receive wl_data_source.cancelled.
 
 	The dnd_actions argument must contain only values expressed in the
 	wl_data_device_manager.dnd_actions enum, and the preferred_action
@@ -544,8 +593,10 @@
 	This request can only be made on drag-and-drop offers, a protocol error
 	will be raised otherwise.
       </description>
-      <arg name="dnd_actions" type="uint" summary="actions supported by the destination client"/>
-      <arg name="preferred_action" type="uint" summary="action preferred by the destination client"/>
+      <arg name="dnd_actions" type="uint" summary="actions supported by the destination client"
+	   enum="wl_data_device_manager.dnd_action"/>
+      <arg name="preferred_action" type="uint" summary="action preferred by the destination client"
+	   enum="wl_data_device_manager.dnd_action"/>
     </request>
 
     <event name="source_actions" since="3">
@@ -554,7 +605,8 @@
 	will be sent right after wl_data_device.enter, or anytime the source
 	side changes its offered actions through wl_data_source.set_actions.
       </description>
-      <arg name="source_actions" type="uint" summary="actions offered by the data source"/>
+      <arg name="source_actions" type="uint" summary="actions offered by the data source"
+	   enum="wl_data_device_manager.dnd_action"/>
     </event>
 
     <event name="action" since="3">
@@ -595,7 +647,8 @@
 	final wl_data_offer.set_actions and wl_data_offer.accept requests
 	must happen before the call to wl_data_offer.finish.
       </description>
-      <arg name="dnd_action" type="uint" summary="action selected by the compositor"/>
+      <arg name="dnd_action" type="uint" summary="action selected by the compositor"
+	   enum="wl_data_device_manager.dnd_action"/>
     </event>
   </interface>
 
@@ -692,7 +745,8 @@
 	wl_data_device.start_drag. Attempting to use the source other than
 	for drag-and-drop will raise a protocol error.
       </description>
-      <arg name="dnd_actions" type="uint" summary="actions supported by the data source"/>
+      <arg name="dnd_actions" type="uint" summary="actions supported by the data source"
+	   enum="wl_data_device_manager.dnd_action"/>
     </request>
 
     <event name="dnd_drop_performed" since="3">
@@ -748,7 +802,8 @@
 	Clients can trigger cursor surface changes from this point, so
 	they reflect the current action.
       </description>
-      <arg name="dnd_action" type="uint" summary="action selected by the compositor"/>
+      <arg name="dnd_action" type="uint" summary="action selected by the compositor"
+	   enum="wl_data_device_manager.dnd_action"/>
     </event>
   </interface>
 
@@ -1274,8 +1329,10 @@
 
   <interface name="wl_surface" version="4">
     <description summary="an onscreen surface">
-      A surface is a rectangular area that is displayed on the screen.
-      It has a location, size and pixel contents.
+      A surface is a rectangular area that may be displayed on zero
+      or more outputs, and shown any number of times at the compositor's
+      discretion. They can present wl_buffers, receive user input, and
+      define a local coordinate system.
 
       The size of a surface (and relative positions on it) is described
       in surface-local coordinates, which may differ from the buffer
@@ -1321,6 +1378,7 @@
       </description>
       <entry name="invalid_scale" value="0" summary="buffer scale value is invalid"/>
       <entry name="invalid_transform" value="1" summary="buffer transform value is invalid"/>
+      <entry name="invalid_size" value="2" summary="buffer size is invalid"/>
     </enum>
 
     <request name="destroy" type="destructor">
@@ -1335,8 +1393,9 @@
 
 	The new size of the surface is calculated based on the buffer
 	size transformed by the inverse buffer_transform and the
-	inverse buffer_scale. This means that the supplied buffer
-	must be an integer multiple of the buffer_scale.
+	inverse buffer_scale. This means that at commit time the supplied
+	buffer size must be an integer multiple of the buffer_scale. If
+	that's not the case, an invalid_size error is sent.
 
 	The x and y arguments specify the location of the new pending
 	buffer's upper left corner, relative to the current buffer's upper
@@ -1363,6 +1422,12 @@
 	will not receive a release event, and is not used by the
 	compositor.
 
+	If a pending wl_buffer has been committed to more than one wl_surface,
+	the delivery of wl_buffer.release events becomes undefined. A well
+	behaved client should not rely on wl_buffer.release events in this
+	case. Alternatively, a client could create multiple wl_buffer objects
+	from the same backing storage or use wp_linux_buffer_release.
+
 	Destroying the wl_buffer after wl_buffer.release does not change
 	the surface contents. However, if the client destroys the
 	wl_buffer before receiving the wl_buffer.release event, the surface
@@ -1397,9 +1462,9 @@
 	and clears pending damage. The server will clear the current
 	damage as it repaints the surface.
 
-	Alternatively, damage can be posted with wl_surface.damage_buffer
-	which uses buffer coordinates instead of surface coordinates,
-	and is probably the preferred and intuitive way of doing this.
+	Note! New clients should not use this request. Instead damage can be
+	posted with wl_surface.damage_buffer which uses buffer coordinates
+	instead of surface coordinates.
       </description>
       <arg name="x" type="int" summary="surface-local x coordinate"/>
       <arg name="y" type="int" summary="surface-local y coordinate"/>
@@ -1660,7 +1725,7 @@
     </request>
    </interface>
 
-  <interface name="wl_seat" version="6">
+  <interface name="wl_seat" version="7">
     <description summary="group of input devices">
       A seat is a group of keyboards, pointer and touch devices. This
       object is published as a global during start up, or when such a
@@ -1678,6 +1743,14 @@
       <entry name="touch" value="4" summary="the seat has touch devices"/>
     </enum>
 
+    <enum name="error">
+      <description summary="wl_seat error values">
+	These errors can be emitted in response to wl_seat requests.
+      </description>
+      <entry name="missing_capability" value="0"
+	     summary="get_pointer, get_keyboard or get_touch called on seat without the matching capability"/>
+    </enum>
+
     <event name="capabilities">
       <description summary="seat capabilities changed">
 	This is emitted whenever a seat gains or loses the pointer,
@@ -1716,7 +1789,8 @@
 	This request only takes effect if the seat has the pointer
 	capability, or has had the pointer capability in the past.
 	It is a protocol violation to issue this request on a seat that has
-	never had the pointer capability.
+	never had the pointer capability. The missing_capability error will
+	be sent in this case.
       </description>
       <arg name="id" type="new_id" interface="wl_pointer" summary="seat pointer"/>
     </request>
@@ -1729,7 +1803,8 @@
 	This request only takes effect if the seat has the keyboard
 	capability, or has had the keyboard capability in the past.
 	It is a protocol violation to issue this request on a seat that has
-	never had the keyboard capability.
+	never had the keyboard capability. The missing_capability error will
+	be sent in this case.
       </description>
       <arg name="id" type="new_id" interface="wl_keyboard" summary="seat keyboard"/>
     </request>
@@ -1742,7 +1817,8 @@
 	This request only takes effect if the seat has the touch
 	capability, or has had the touch capability in the past.
 	It is a protocol violation to issue this request on a seat that has
-	never had the touch capability.
+	never had the touch capability. The missing_capability error will
+	be sent in this case.
       </description>
       <arg name="id" type="new_id" interface="wl_touch" summary="seat touch interface"/>
     </request>
@@ -1769,7 +1845,7 @@
 
   </interface>
 
-  <interface name="wl_pointer" version="6">
+  <interface name="wl_pointer" version="7">
     <description summary="pointer input device">
       The wl_pointer interface represents one or more input devices,
       such as mice, which control the pointer location and pointer_focus
@@ -2092,7 +2168,7 @@
     </event>
   </interface>
 
-  <interface name="wl_keyboard" version="6">
+  <interface name="wl_keyboard" version="7">
     <description summary="keyboard input device">
       The wl_keyboard interface represents one or more keyboards
       associated with a seat.
@@ -2113,6 +2189,9 @@
       <description summary="keyboard mapping">
 	This event provides a file descriptor to the client which can be
 	memory-mapped to provide a keyboard mapping description.
+
+	From version 7 onwards, the fd must be mapped with MAP_PRIVATE by
+	the recipient, as MAP_SHARED may fail.
       </description>
       <arg name="format" type="uint" enum="keymap_format" summary="keymap format"/>
       <arg name="fd" type="fd" summary="keymap file descriptor"/>
@@ -2123,6 +2202,9 @@
       <description summary="enter event">
 	Notification that this seat's keyboard focus is on a certain
 	surface.
+
+	The compositor must send the wl_keyboard.modifiers event after this
+	event.
       </description>
       <arg name="serial" type="uint" summary="serial number of the enter event"/>
       <arg name="surface" type="object" interface="wl_surface" summary="surface gaining keyboard focus"/>
@@ -2136,6 +2218,9 @@
 
 	The leave notification is sent before the enter notification
 	for the new focus.
+
+	After this event client must assume that all keys, including modifiers,
+	are lifted and also it must stop key repeating if there's some going on.
       </description>
       <arg name="serial" type="uint" summary="serial number of the leave event"/>
       <arg name="surface" type="object" interface="wl_surface" summary="surface that lost keyboard focus"/>
@@ -2154,6 +2239,12 @@
 	A key was pressed or released.
 	The time argument is a timestamp with millisecond
 	granularity, with an undefined base.
+
+	The key is a platform-specific key code that can be interpreted
+	by feeding it to the keyboard mapping (see the keymap event).
+
+	If this event produces a change in modifiers, then the resulting
+	wl_keyboard.modifiers event must be sent after this event.
       </description>
       <arg name="serial" type="uint" summary="serial number of the key event"/>
       <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
@@ -2203,7 +2294,7 @@
     </event>
   </interface>
 
-  <interface name="wl_touch" version="6">
+  <interface name="wl_touch" version="7">
     <description summary="touchscreen input device">
       The wl_touch interface represents a touchscreen
       associated with a seat.
@@ -2402,6 +2493,13 @@
 
 	The physical size can be set to zero if it doesn't make sense for this
 	output (e.g. for projectors or virtual outputs).
+
+	Note: wl_output only advertises partial information about the output
+	position and identification. Some compositors, for instance those not
+	implementing a desktop-style output layout or those exposing virtual
+	outputs, might fake this information. Instead of using x and y, clients
+	should use xdg_output.logical_position. Instead of using make and model,
+	clients should use xdg_output.name and xdg_output.description.
       </description>
       <arg name="x" type="int"
 	   summary="x position within the global compositor space"/>
@@ -2446,7 +2544,20 @@
 	the output device. This is not necessarily the same as
 	the output size in the global compositor space. For instance,
 	the output may be scaled, as described in wl_output.scale,
-	or transformed, as described in wl_output.transform.
+	or transformed, as described in wl_output.transform. Clients
+	willing to retrieve the output size in the global compositor
+	space should use xdg_output.logical_size instead.
+
+	The vertical refresh rate can be set to zero if it doesn't make
+	sense for this output (e.g. for virtual outputs).
+
+	Clients should not use the refresh rate to schedule frames. Instead,
+	they should use the wl_surface.frame event or the presentation-time
+	protocol.
+
+	Note: this information is not always meaningful for all outputs. Some
+	compositors, such as those exposing virtual outputs, might fake the
+	refresh rate or the size.
       </description>
       <arg name="flags" type="uint" enum="mode" summary="bitfield of mode flags"/>
       <arg name="width" type="int" summary="width of the mode in hardware units"/>
@@ -2621,7 +2732,7 @@
       wl_surface state directly. A sub-surface is initially in the
       synchronized mode.
 
-      Sub-surfaces have also other kind of state, which is managed by
+      Sub-surfaces also have another kind of state, which is managed by
       wl_subsurface requests, as opposed to wl_surface requests. This
       state includes the sub-surface position relative to the parent
       surface (wl_subsurface.set_position), and the stacking order of
diff --git a/releasing.txt b/releasing.txt
new file mode 100644
index 0000000..1481f7c
--- /dev/null
+++ b/releasing.txt
@@ -0,0 +1,78 @@
+To make a release of Wayland, follow these steps.
+
+  0.  Verify the test suites and codebase checks pass.  All of the
+      tests should either pass or skip.
+
+      $ make check
+
+  1.  Update the first stanza of configure.ac to the intended version.
+
+      Then commit your changes:
+
+      $ export RELEASE_NUMBER="x.y.z"
+      $ export RELEASE_NAME="[alpha|beta|RC1|RC2|official|point]"
+      $ git status
+      $ git commit configure.ac -m "configure.ac: bump to version $RELEASE_NUMBER for the $RELEASE_NAME release"
+      $ git push
+
+  2.  Run the release.sh script to generate the tarballs, sign and
+      upload them, and generate a release announcement template.
+      This script can be obtained from X.org's modular package:
+
+        http://cgit.freedesktop.org/xorg/util/modular/tree/release.sh
+
+      The script supports a --dry-run option to test it without actually
+      doing a release.  If the script fails on the distcheck step due to
+      a testsuite error that can't be fixed for some reason, you can
+      skip testsuite by specifying the --dist argument.  Pass --help to
+      see other supported options.
+
+      $ release.sh .
+
+      For Wayland official and point releases, also publish the publican
+      documentation to wayland.freedesktop.org:
+
+      $ ./publish-doc
+
+  3.  Compose the release announcements.  The script will generate
+      *.x.y.z.announce files with a list of changes and tags.  Prepend
+      it with a human-readable listing of the most notable changes.
+      For x.y.0 releases, indicate the schedule for the x.y+1.0
+      release.
+
+  4.  PGP sign the release announcements and send them to
+      wayland-devel@lists.freedesktop.org
+
+  5.  Update releases.html in wayland-web with links to tarballs and
+      the release email URL.
+
+      The wl_register_release script in wayland-web will generate an HTML
+      snippet that can be pasted into releases.html (or e.g. in emacs
+      insert it via "C-u M-! scripts/wl_register_release x.y.z") and
+      customized.
+
+      Once satisfied:
+
+      $ git commit ./releases.html -m "releases: Add ${RELEASE_NUMBER} release"
+      $ git push
+      $ ./deploy
+
+For x.y.0 releases, also create the release series x.y branch.  The x.y
+branch is for bug fixes and conservative changes to the x.y.0 release,
+and is where we create x.y.z releases from.  Creating the x.y branch
+opens up master for new development and lets new development move on.
+We've done this both after the x.y.0 release (to focus development on
+bug fixing for the x.y.1 release for a little longer) or before the
+x.y.0 release (like we did with the 1.5.0 release, to unblock master
+development early).
+
+    $ git branch x.y [sha]
+    $ git push origin x.y
+
+The master branch's configure.ac version should always be (at least)
+x.y.90, with x.y being the most recent stable branch.  The stable
+branch's configure.ac version is just whatever was most recently
+released from that branch.
+
+For stable branches, we commit fixes to master first, then cherry-pick
+them back to the stable branch.
diff --git a/src/connection.c b/src/connection.c
index f965210..d0c7d9f 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -644,8 +644,12 @@
 		case 'h':
 			fd = args[i].h;
 			dup_fd = wl_os_dupfd_cloexec(fd, 0);
-			if (dup_fd < 0)
-				wl_abort("dup failed: %s\n", strerror(errno));
+			if (dup_fd < 0) {
+				wl_closure_destroy(closure);
+				wl_log("error marshalling arguments for %s: dup failed: %s\n",
+				       message->name, strerror(errno));
+				return NULL;
+			}
 			closure->args[i].h = dup_fd;
 			break;
 		default:
@@ -695,6 +699,14 @@
 	struct wl_closure *closure;
 	struct wl_array *array_extra;
 
+	/* Space for sender_id and opcode */
+	if (size < 2 * sizeof *p) {
+		wl_log("message too short, invalid header\n");
+		wl_connection_consume(connection, size);
+		errno = EINVAL;
+		return NULL;
+	}
+
 	closure = wl_closure_init(message, size, &num_arrays, NULL);
 	if (closure == NULL) {
 		wl_connection_consume(connection, size);
@@ -1278,7 +1290,10 @@
 				wl_fixed_to_double(closure->args[i].f));
 			break;
 		case 's':
-			fprintf(stderr, "\"%s\"", closure->args[i].s);
+			if (closure->args[i].s)
+				fprintf(stderr, "\"%s\"", closure->args[i].s);
+			else
+				fprintf(stderr, "nil");
 			break;
 		case 'o':
 			if (closure->args[i].o)
diff --git a/src/event-loop.c b/src/event-loop.c
index eb2dce6..339ff19 100644
--- a/src/event-loop.c
+++ b/src/event-loop.c
@@ -23,6 +23,7 @@
  * SOFTWARE.
  */
 
+#include <assert.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <errno.h>
@@ -45,19 +46,11 @@
 
 /** \cond INTERNAL */
 
-struct wl_event_loop {
-	int epoll_fd;
-	struct wl_list check_list;
-	struct wl_list idle_list;
-	struct wl_list destroy_list;
+#define TIMER_REMOVED -2
 
-	struct wl_signal destroy_signal;
-};
-
-struct wl_event_source_interface {
-	int (*dispatch)(struct wl_event_source *source,
-			struct epoll_event *ep);
-};
+struct wl_event_loop;
+struct wl_event_source_interface;
+struct wl_event_source_timer;
 
 struct wl_event_source {
 	struct wl_event_source_interface *interface;
@@ -67,6 +60,30 @@
 	int fd;
 };
 
+struct wl_timer_heap {
+	struct wl_event_source base;
+	/* pointers to the user-visible event sources */
+	struct wl_event_source_timer **data;
+	int space, active, count;
+};
+
+struct wl_event_loop {
+	int epoll_fd;
+	struct wl_list check_list;
+	struct wl_list idle_list;
+	struct wl_list destroy_list;
+
+	struct wl_signal destroy_signal;
+
+	struct wl_timer_heap timers;
+};
+
+struct wl_event_source_interface {
+	int (*dispatch)(struct wl_event_source *source,
+			struct epoll_event *ep);
+};
+
+
 struct wl_event_source_fd {
 	struct wl_event_source base;
 	wl_event_loop_fd_func_t func;
@@ -215,31 +232,319 @@
 struct wl_event_source_timer {
 	struct wl_event_source base;
 	wl_event_loop_timer_func_t func;
+	struct wl_event_source_timer *next_due;
+	struct timespec deadline;
+	int heap_idx;
 };
 
-/** \endcond */
+static int
+noop_dispatch(struct wl_event_source *source,
+	      struct epoll_event *ep) {
+	return 0;
+}
+
+struct wl_event_source_interface timer_heap_source_interface = {
+	noop_dispatch,
+};
+
+static bool
+time_lt(struct timespec ta, struct timespec tb)
+{
+	if (ta.tv_sec != tb.tv_sec) {
+		return ta.tv_sec < tb.tv_sec;
+	}
+	return ta.tv_nsec < tb.tv_nsec;
+}
+
+static int
+set_timer(int timerfd, struct timespec deadline) {
+	struct itimerspec its;
+
+	its.it_interval.tv_sec = 0;
+	its.it_interval.tv_nsec = 0;
+	its.it_value = deadline;
+	return timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &its, NULL);
+}
+
+static int
+clear_timer(int timerfd)
+{
+	struct itimerspec its;
+
+	its.it_interval.tv_sec = 0;
+	its.it_interval.tv_nsec = 0;
+	its.it_value.tv_sec = 0;
+	its.it_value.tv_nsec = 0;
+	return timerfd_settime(timerfd, 0, &its, NULL);
+}
+
+static void
+wl_timer_heap_init(struct wl_timer_heap *timers, struct wl_event_loop *loop)
+{
+	timers->base.fd = -1;
+	timers->base.data = NULL;
+	wl_list_init(&timers->base.link);
+	timers->base.interface = &timer_heap_source_interface;
+	timers->base.loop = loop;
+
+	loop->timers.data = NULL;
+	loop->timers.active = 0;
+	loop->timers.space = 0;
+	loop->timers.count = 0;
+}
+
+static void
+wl_timer_heap_release(struct wl_timer_heap *timers)
+{
+	if (timers->base.fd != -1) {
+		close(timers->base.fd);
+	}
+	free(timers->data);
+}
+
+static int
+wl_timer_heap_ensure_timerfd(struct wl_timer_heap *timers)
+{
+	struct epoll_event ep;
+	int timer_fd;
+
+	if (timers->base.fd != -1)
+		return 0;
+
+	memset(&ep, 0, sizeof ep);
+	ep.events = EPOLLIN;
+	ep.data.ptr = timers;
+
+	timer_fd = timerfd_create(CLOCK_MONOTONIC,
+				  TFD_CLOEXEC | TFD_NONBLOCK);
+	if (timer_fd < 0)
+		return -1;
+
+	if (epoll_ctl(timers->base.loop->epoll_fd,
+		      EPOLL_CTL_ADD, timer_fd, &ep) < 0) {
+		close(timer_fd);
+		return -1;
+	}
+
+	timers->base.fd = timer_fd;
+	return 0;
+}
+
+static int
+wl_timer_heap_reserve(struct wl_timer_heap *timers)
+{
+	struct wl_event_source_timer **n;
+	int new_space;
+
+	if (timers->count + 1 > timers->space) {
+		new_space = timers->space >= 8 ? timers->space * 2 : 8;
+		n = realloc(timers->data, (size_t)new_space * sizeof(*n));
+		if (!n) {
+			wl_log("Allocation failure when expanding timer list");
+			return -1;
+		}
+		timers->data = n;
+		timers->space = new_space;
+	}
+
+	timers->count++;
+	return 0;
+}
+
+static void
+wl_timer_heap_unreserve(struct wl_timer_heap *timers)
+{
+	struct wl_event_source_timer **n;
+
+	timers->count--;
+
+	if (timers->space >= 16 && timers->space >= 4 * timers->count) {
+		n = realloc(timers->data, (size_t)timers->space / 2 * sizeof(*n));
+		if (!n) {
+			wl_log("Reallocation failure when shrinking timer list");
+			return;
+		}
+		timers->data = n;
+		timers->space = timers->space / 2;
+	}
+}
+
+static int
+heap_set(struct wl_event_source_timer **data,
+	 struct wl_event_source_timer *a,
+	 int idx)
+{
+	int tmp;
+
+	tmp = a->heap_idx;
+	a->heap_idx = idx;
+	data[a->heap_idx] = a;
+
+	return tmp;
+}
+
+static void
+heap_sift_down(struct wl_event_source_timer **data,
+	       int num_active,
+	       struct wl_event_source_timer *source)
+{
+	struct wl_event_source_timer *child, *other_child;
+	int cursor_idx;
+	struct timespec key;
+
+	cursor_idx = source->heap_idx;
+	key = source->deadline;
+	while (1) {
+		int lchild_idx = cursor_idx * 2 + 1;
+
+		if (lchild_idx >= num_active) {
+			break;
+		}
+
+		child = data[lchild_idx];
+		if (lchild_idx + 1 < num_active) {
+			other_child = data[lchild_idx + 1];
+			if (time_lt(other_child->deadline, child->deadline))
+				child = other_child;
+		}
+
+		if (time_lt(child->deadline, key))
+			cursor_idx = heap_set(data, child, cursor_idx);
+		else
+			break;
+	}
+
+	heap_set(data, source, cursor_idx);
+}
+
+static void
+heap_sift_up(struct wl_event_source_timer **data,
+	     struct wl_event_source_timer *source)
+{
+	int cursor_idx;
+	struct timespec key;
+
+	cursor_idx = source->heap_idx;
+	key = source->deadline;
+	while (cursor_idx > 0) {
+		struct wl_event_source_timer *parent =
+			data[(cursor_idx - 1) / 2];
+
+		if (time_lt(key, parent->deadline))
+			cursor_idx = heap_set(data, parent, cursor_idx);
+		else
+			break;
+	}
+	heap_set(data, source, cursor_idx);
+}
+
+/* requires timer be armed */
+static void
+wl_timer_heap_disarm(struct wl_timer_heap *timers,
+		     struct wl_event_source_timer *source)
+{
+	struct wl_event_source_timer *last_end_evt;
+	int old_source_idx;
+
+	assert(source->heap_idx >= 0);
+
+	old_source_idx = source->heap_idx;
+	source->heap_idx = -1;
+	source->deadline.tv_sec = 0;
+	source->deadline.tv_nsec = 0;
+
+	last_end_evt = timers->data[timers->active - 1];
+	timers->data[timers->active - 1] = NULL;
+	timers->active--;
+
+	if (old_source_idx == timers->active)
+		return;
+
+	timers->data[old_source_idx] = last_end_evt;
+	last_end_evt->heap_idx = old_source_idx;
+
+	/* Move the displaced (active) element to its proper place.
+	 * Only one of sift-down and sift-up will have any effect */
+	heap_sift_down(timers->data, timers->active, last_end_evt);
+	heap_sift_up(timers->data, last_end_evt);
+}
+
+/* requires timer be disarmed */
+static void
+wl_timer_heap_arm(struct wl_timer_heap *timers,
+		  struct wl_event_source_timer *source,
+		  struct timespec deadline)
+{
+	assert(source->heap_idx == -1);
+
+	source->deadline = deadline;
+	timers->data[timers->active] = source;
+	source->heap_idx = timers->active;
+	timers->active++;
+	heap_sift_up(timers->data, source);
+}
+
+
+static int
+wl_timer_heap_dispatch(struct wl_timer_heap *timers)
+{
+	struct timespec now;
+	struct wl_event_source_timer *root;
+	struct wl_event_source_timer *list_cursor = NULL, *list_tail = NULL;
+
+	clock_gettime(CLOCK_MONOTONIC, &now);
+
+	while (timers->active > 0) {
+		root = timers->data[0];
+		if (time_lt(now, root->deadline))
+			break;
+
+		wl_timer_heap_disarm(timers, root);
+
+		if (list_cursor == NULL)
+			list_cursor = root;
+		else
+			list_tail->next_due = root;
+		list_tail = root;
+	}
+	if (list_tail)
+		list_tail->next_due = NULL;
+
+	if (timers->active > 0) {
+		if (set_timer(timers->base.fd, timers->data[0]->deadline) < 0)
+			return -1;
+	} else {
+		if (clear_timer(timers->base.fd) < 0)
+			return -1;
+	}
+
+	/* Execute precisely the functions for events before `now`, in order.
+	 * Because wl_event_loop_dispatch ignores return codes, do the same
+	 * here as well */
+	for (; list_cursor; list_cursor = list_cursor->next_due) {
+		if (list_cursor->base.fd != TIMER_REMOVED)
+			list_cursor->func(list_cursor->base.data);
+	}
+
+	return 0;
+}
 
 static int
 wl_event_source_timer_dispatch(struct wl_event_source *source,
 			       struct epoll_event *ep)
 {
-	struct wl_event_source_timer *timer_source =
-		(struct wl_event_source_timer *) source;
-	uint64_t expires;
-	int len;
+	struct wl_event_source_timer *timer;
 
-	len = read(source->fd, &expires, sizeof expires);
-	if (!(len == -1 && errno == EAGAIN) && len != sizeof expires)
-		/* Is there anything we can do here?  Will this ever happen? */
-		wl_log("timerfd read error: %m\n");
-
-	return timer_source->func(timer_source->base.data);
+	timer = wl_container_of(source, timer, base);
+	return timer->func(timer->base.data);
 }
 
 struct wl_event_source_interface timer_source_interface = {
 	wl_event_source_timer_dispatch,
 };
 
+/** \endcond */
+
 /** Create a timer event source
  *
  * \param loop The event loop that will process the new source.
@@ -260,16 +565,30 @@
 {
 	struct wl_event_source_timer *source;
 
+	if (wl_timer_heap_ensure_timerfd(&loop->timers) < 0)
+		return NULL;
+
 	source = malloc(sizeof *source);
 	if (source == NULL)
 		return NULL;
 
 	source->base.interface = &timer_source_interface;
-	source->base.fd = timerfd_create(CLOCK_MONOTONIC,
-					 TFD_CLOEXEC | TFD_NONBLOCK);
+	source->base.fd = -1;
 	source->func = func;
+	source->base.loop = loop;
+	source->base.data = data;
+	wl_list_init(&source->base.link);
+	source->next_due = NULL;
+	source->deadline.tv_sec = 0;
+	source->deadline.tv_nsec = 0;
+	source->heap_idx = -1;
 
-	return add_source(loop, &source->base, WL_EVENT_READABLE, data);
+	if (wl_timer_heap_reserve(&loop->timers) < 0) {
+		free(source);
+		return NULL;
+	}
+
+	return &source->base;
 }
 
 /** Arm or disarm a timer
@@ -291,14 +610,50 @@
 WL_EXPORT int
 wl_event_source_timer_update(struct wl_event_source *source, int ms_delay)
 {
-	struct itimerspec its;
+	struct wl_event_source_timer *tsource =
+		wl_container_of(source, tsource, base);
+	struct wl_timer_heap *timers = &tsource->base.loop->timers;
 
-	its.it_interval.tv_sec = 0;
-	its.it_interval.tv_nsec = 0;
-	its.it_value.tv_sec = ms_delay / 1000;
-	its.it_value.tv_nsec = (ms_delay % 1000) * 1000 * 1000;
-	if (timerfd_settime(source->fd, 0, &its, NULL) < 0)
-		return -1;
+	if (ms_delay > 0) {
+		struct timespec deadline;
+
+		clock_gettime(CLOCK_MONOTONIC, &deadline);
+
+		deadline.tv_nsec += (ms_delay % 1000) * 1000000L;
+		deadline.tv_sec += ms_delay / 1000;
+		if (deadline.tv_nsec >= 1000000000L) {
+			deadline.tv_nsec -= 1000000000L;
+			deadline.tv_sec += 1;
+		}
+
+		if (tsource->heap_idx == -1) {
+			wl_timer_heap_arm(timers, tsource, deadline);
+		} else if (time_lt(deadline, tsource->deadline)) {
+			tsource->deadline = deadline;
+			heap_sift_up(timers->data, tsource);
+		} else {
+			tsource->deadline = deadline;
+			heap_sift_down(timers->data, timers->active, tsource);
+		}
+
+		if (tsource->heap_idx == 0) {
+			/* Only update the timerfd if the new deadline is
+			 * the earliest */
+			if (set_timer(timers->base.fd, deadline) < 0)
+				return -1;
+		}
+	} else {
+		if (tsource->heap_idx == -1)
+			return 0;
+		wl_timer_heap_disarm(timers, tsource);
+
+		if (timers->active == 0) {
+			/* Only update the timerfd if this was the last
+			 * active timer */
+			if (clear_timer(timers->base.fd) < 0)
+				return -1;
+		}
+	}
 
 	return 0;
 }
@@ -325,7 +680,7 @@
 	len = read(source->fd, &signal_info, sizeof signal_info);
 	if (!(len == -1 && errno == EAGAIN) && len != sizeof signal_info)
 		/* Is there anything we can do here?  Will this ever happen? */
-		wl_log("signalfd read error: %m\n");
+		wl_log("signalfd read error: %s\n", strerror(errno));
 
 	return signal_source->func(signal_source->signal_number,
 				   signal_source->base.data);
@@ -483,6 +838,17 @@
 		source->fd = -1;
 	}
 
+	if (source->interface == &timer_source_interface &&
+	    source->fd != TIMER_REMOVED) {
+		/* Disarm the timer (and the loop's timerfd, if necessary),
+		 * before removing its space in the loop timer heap */
+		wl_event_source_timer_update(source, 0);
+		wl_timer_heap_unreserve(&loop->timers);
+		/* Set the fd field to to indicate that the timer should NOT
+		 * be dispatched in `wl_event_loop_dispatch` */
+		source->fd = TIMER_REMOVED;
+	}
+
 	wl_list_remove(&source->link);
 	wl_list_insert(&loop->destroy_list, &source->link);
 
@@ -534,6 +900,8 @@
 
 	wl_signal_init(&loop->destroy_signal);
 
+	wl_timer_heap_init(&loop->timers, loop);
+
 	return loop;
 }
 
@@ -556,6 +924,7 @@
 	wl_signal_emit(&loop->destroy_signal, loop);
 
 	wl_event_loop_process_destroy_list(loop);
+	wl_timer_heap_release(&loop->timers);
 	close(loop->epoll_fd);
 	free(loop);
 }
@@ -595,8 +964,8 @@
 	struct wl_event_source_idle *source;
 
 	while (!wl_list_empty(&loop->idle_list)) {
-		source = container_of(loop->idle_list.next,
-				      struct wl_event_source_idle, base.link);
+		source = wl_container_of(loop->idle_list.next,
+					 source, base.link);
 		source->func(source->base.data);
 		wl_event_source_remove(&source->base);
 	}
@@ -606,7 +975,7 @@
  *
  * \param loop The event loop whose sources to wait for.
  * \param timeout The polling timeout in milliseconds.
- * \return 0 for success, -1 for polling error.
+ * \return 0 for success, -1 for polling (or timer update) error.
  *
  * All the associated event sources are polled. This function blocks until
  * any event source delivers an event (idle sources excluded), or the timeout
@@ -628,6 +997,7 @@
 	struct epoll_event ep[32];
 	struct wl_event_source *source;
 	int i, count;
+	bool has_timers = false;
 
 	wl_event_loop_dispatch_idle(loop);
 
@@ -637,6 +1007,22 @@
 
 	for (i = 0; i < count; i++) {
 		source = ep[i].data.ptr;
+		if (source == &loop->timers.base)
+			has_timers = true;
+	}
+
+	if (has_timers) {
+		/* Dispatch timer sources before non-timer sources, so that
+		 * the non-timer sources can not cancel (by calling
+		 * `wl_event_source_timer_update`) the dispatching of the timers
+		 * (Note that timer sources also can't cancel pending non-timer
+		 * sources, since epoll_wait has already been called) */
+		if (wl_timer_heap_dispatch(&loop->timers) < 0)
+			return -1;
+	}
+
+	for (i = 0; i < count; i++) {
+		source = ep[i].data.ptr;
 		if (source->fd != -1)
 			source->interface->dispatch(source, &ep[i]);
 	}
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..2d1485c
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,234 @@
+wayland_version = meson.project_version().split('.')
+wayland_version_h = configuration_data()
+wayland_version_h.set('WAYLAND_VERSION', meson.project_version())
+wayland_version_h.set('WAYLAND_VERSION_MAJOR', wayland_version[0].to_int())
+wayland_version_h.set('WAYLAND_VERSION_MINOR', wayland_version[1].to_int())
+wayland_version_h.set('WAYLAND_VERSION_MICRO', wayland_version[2].to_int())
+configure_file(
+	input: 'wayland-version.h.in',
+	output: 'wayland-version.h',
+	configuration: wayland_version_h,
+	install: get_option('libraries'),
+	install_dir: join_paths(get_option('prefix'), get_option('includedir'))
+)
+
+
+wayland_util = static_library(
+	'wayland-util',
+	sources: 'wayland-util.c'
+)
+
+wayland_util_dep = declare_dependency(
+	link_with: wayland_util,
+	include_directories: include_directories('.')
+)
+
+if get_option('scanner')
+	# wayland-scanner
+
+	configure_file(
+		input: '../protocol/wayland.dtd',
+		output: 'wayland.dtd.embed',
+		copy: true
+	)
+
+	wayland_scanner_sources = [ 'scanner.c', 'dtddata.S' ]
+	wayland_scanner_includes = [ root_inc, protocol_inc ]
+
+	wayland_scanner = executable(
+		'wayland-scanner',
+		wayland_scanner_sources,
+		c_args: [ '-include', 'config.h' ],
+		include_directories: wayland_scanner_includes,
+		dependencies: [ scanner_deps, wayland_util_dep, ],
+		install: true
+	)
+
+	pkgconfig.generate(
+		name: 'Wayland Scanner',
+		description: 'Wayland scanner',
+		version: meson.project_version(),
+		variables: [
+			'datarootdir=' + join_paths('${prefix}', get_option('datadir')),
+			'pkgdatadir=' + join_paths('${datarootdir}', meson.project_name()),
+			'bindir=' + join_paths('${prefix}', get_option('bindir')),
+			'wayland_scanner=${bindir}/wayland-scanner'
+		],
+		filebase: 'wayland-scanner'
+	)
+endif
+
+if meson.is_cross_build() or not get_option('scanner')
+	scanner_dep = dependency('wayland-scanner', native: true, version: meson.project_version())
+	wayland_scanner_for_build = find_program(scanner_dep.get_pkgconfig_variable('wayland_scanner'))
+else
+	wayland_scanner_for_build = wayland_scanner
+endif
+
+if get_option('libraries')
+	# wayland libraries
+
+	mathlib_dep = cc.find_library('m', required: false)
+	threads_dep = dependency('threads', required: false)
+
+	wayland_private = static_library(
+		'wayland-private',
+		sources: [
+			'connection.c',
+			'wayland-os.c'
+		],
+		dependencies: [ ffi_dep, ]
+	)
+
+	wayland_private_dep = declare_dependency(
+		link_with: wayland_private,
+		include_directories: include_directories('.')
+	)
+
+	generated_headers = [
+		{
+			'scanner_args': ['server-header'],
+			'output': 'wayland-server-protocol.h',
+			'install': true,
+		},
+		{
+			'scanner_args': ['server-header', '-c'],
+			'output': 'wayland-server-protocol-core.h',
+			'install': false,
+		},
+		{
+			'scanner_args': ['client-header'],
+			'output': 'wayland-client-protocol.h',
+			'install': true,
+		},
+		{
+			'scanner_args': ['client-header', '-c'],
+			'output': 'wayland-client-protocol-core.h',
+			'install': false,
+		},
+	]
+
+	foreach gen: generated_headers
+		scanner_args = gen['scanner_args']
+		output_file = gen['output']
+		install_file = gen['install']
+		install_dir = join_paths(get_option('prefix'), get_option('includedir'))
+		target_name = output_file.underscorify()
+
+		target = custom_target(
+			target_name,
+			command: [
+				wayland_scanner_for_build, '-s', scanner_args,
+				'@INPUT@', '@OUTPUT@'
+			],
+			input: wayland_protocol_xml,
+			output: output_file,
+			install: install_file,
+			install_dir: install_dir
+		)
+
+		set_variable(target_name, target)
+	endforeach
+
+	wayland_protocol_c = custom_target(
+		'protocol source',
+		command: [
+			wayland_scanner_for_build, '-s', 'public-code', '@INPUT@', '@OUTPUT@'
+		],
+		input: wayland_protocol_xml,
+		output: 'wayland-protocol.c'
+	)
+
+	wayland_server = library(
+		'wayland-server',
+		sources: [
+			wayland_server_protocol_core_h,
+			wayland_server_protocol_h,
+			wayland_protocol_c,
+			'wayland-server.c',
+			'wayland-shm.c',
+			'event-loop.c'
+		],
+		version: '0.1.0',
+		dependencies: [
+			ffi_dep,
+			wayland_private_dep,
+			wayland_util_dep,
+			mathlib_dep,
+			threads_dep
+		],
+		include_directories: root_inc,
+		install: true
+	)
+
+	wayland_server_dep = declare_dependency(
+		link_with: wayland_server,
+		include_directories: [ root_inc, include_directories('.') ],
+		dependencies: [ ffi_dep, mathlib_dep, threads_dep ],
+		sources: [
+			wayland_server_protocol_core_h,
+			wayland_server_protocol_h
+		]
+	)
+
+	pkgconfig.generate(
+		wayland_server,
+		name: 'Wayland Server',
+		description: 'Server side implementation of the Wayland protocol',
+		version: meson.project_version(),
+		filebase: 'wayland-server',
+		variables: [
+			'datarootdir=' + join_paths('${prefix}', get_option('datadir')),
+			'pkgdatadir=' + join_paths('${datarootdir}', meson.project_name())
+		]
+	)
+
+	wayland_client = library(
+		'wayland-client',
+		sources: [
+			wayland_client_protocol_core_h,
+			wayland_client_protocol_h,
+			wayland_protocol_c,
+			'wayland-client.c'
+		],
+		version: '0.3.0',
+		dependencies: [
+			ffi_dep,
+			wayland_private_dep,
+			wayland_util_dep,
+			mathlib_dep,
+			threads_dep
+		],
+		include_directories: root_inc,
+		install: true
+	)
+
+	pkgconfig.generate(
+		wayland_client,
+		name: 'Wayland Client',
+		description: 'Wayland client side library',
+		version: meson.project_version(),
+		filebase: 'wayland-client',
+		variables: [
+			'datarootdir=' + join_paths('${prefix}', get_option('datadir')),
+			'pkgdatadir=' + join_paths('${datarootdir}', meson.project_name())
+		]
+	)
+
+	wayland_client_dep = declare_dependency(
+		link_with: wayland_client,
+		include_directories: [ root_inc, include_directories('.') ],
+		sources: [
+			wayland_client_protocol_core_h,
+			wayland_client_protocol_h
+		]
+	)
+
+	install_headers([
+		'wayland-util.h',
+		'wayland-server.h',
+		'wayland-server-core.h',
+		'wayland-client.h',
+		'wayland-client-core.h',
+	])
+endif
diff --git a/src/scanner.c b/src/scanner.c
index a94be5d..36ac905 100644
--- a/src/scanner.c
+++ b/src/scanner.c
@@ -25,7 +25,6 @@
  * SOFTWARE.
  */
 
-#include "config.h"
 #include "wayland-version.h"
 
 #include <stdbool.h>
@@ -251,6 +250,11 @@
 	unsigned int character_data_length;
 };
 
+enum identifier_role {
+	STANDALONE_IDENT,
+	TRAILING_IDENT
+};
+
 static void *
 fail_on_null(void *p)
 {
@@ -627,6 +631,50 @@
 	return (int)ret;
 }
 
+/* Check that the provided string will produce valid "C" identifiers.
+ *
+ * If the string will form the prefix of an identifier in the
+ * generated C code, then it must match [_a-zA-Z][_0-9a-zA-Z]*.
+ *
+ * If the string will form the suffix of an identifier, then
+ * it must match [_0-9a-zA-Z]+.
+ *
+ * Unicode characters or escape sequences are not permitted,
+ * since not all C compilers support them.
+ *
+ * If the above conditions are not met, then fail()
+ */
+static void
+validate_identifier(struct location *loc,
+		const char *str,
+		enum identifier_role role)
+{
+	const char *scan;
+
+	if (!*str) {
+		fail(loc, "element name is empty");
+	}
+
+	for (scan = str; *scan; scan++) {
+		char c = *scan;
+
+		/* we do not use the locale-dependent `isalpha` */
+		bool is_alpha = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+		bool is_digit = c >= '0' && c <= '9';
+		bool leading_char = (scan == str) && role == STANDALONE_IDENT;
+
+		if (is_alpha || c == '_' || (!leading_char && is_digit))
+			continue;
+
+		if (role == TRAILING_IDENT)
+			fail(loc,
+			     "'%s' is not a valid trailing identifier part", str);
+		else
+			fail(loc,
+			     "'%s' is not a valid standalone identifier", str);
+	}
+}
+
 static int
 version_from_since(struct parse_context *ctx, const char *since)
 {
@@ -701,6 +749,7 @@
 		if (name == NULL)
 			fail(&ctx->loc, "no protocol name given");
 
+		validate_identifier(&ctx->loc, name, STANDALONE_IDENT);
 		ctx->protocol->name = xstrdup(name);
 		ctx->protocol->uppercase_name = uppercase_dup(name);
 	} else if (strcmp(element_name, "copyright") == 0) {
@@ -712,6 +761,7 @@
 		if (version == 0)
 			fail(&ctx->loc, "no interface version given");
 
+		validate_identifier(&ctx->loc, name, STANDALONE_IDENT);
 		interface = create_interface(ctx->loc, name, version);
 		ctx->interface = interface;
 		wl_list_insert(ctx->protocol->interface_list.prev,
@@ -721,6 +771,7 @@
 		if (name == NULL)
 			fail(&ctx->loc, "no request name given");
 
+		validate_identifier(&ctx->loc, name, STANDALONE_IDENT);
 		message = create_message(ctx->loc, name);
 
 		if (strcmp(element_name, "request") == 0)
@@ -748,6 +799,7 @@
 		if (name == NULL)
 			fail(&ctx->loc, "no argument name given");
 
+		validate_identifier(&ctx->loc, name, STANDALONE_IDENT);
 		arg = create_arg(name);
 		if (!set_arg_type(arg, type))
 			fail(&ctx->loc, "unknown type (%s)", type);
@@ -757,8 +809,12 @@
 			ctx->message->new_id_count++;
 			/* fallthrough */
 		case OBJECT:
-			if (interface_name)
+			if (interface_name) {
+				validate_identifier(&ctx->loc,
+						    interface_name,
+						    STANDALONE_IDENT);
 				arg->interface_name = xstrdup(interface_name);
+			}
 			break;
 		default:
 			if (interface_name != NULL)
@@ -793,6 +849,7 @@
 		if (name == NULL)
 			fail(&ctx->loc, "no enum name given");
 
+		validate_identifier(&ctx->loc, name, TRAILING_IDENT);
 		enumeration = create_enumeration(name);
 
 		if (bitfield == NULL || strcmp(bitfield, "false") == 0)
@@ -812,6 +869,7 @@
 		if (name == NULL)
 			fail(&ctx->loc, "no entry name given");
 
+		validate_identifier(&ctx->loc, name, TRAILING_IDENT);
 		entry = create_entry(name, value);
 		version = version_from_since(ctx, since);
 
@@ -916,6 +974,17 @@
 
 }
 
+#ifndef HAVE_STRNDUP
+char *
+strndup(const char *s, size_t size)
+{
+	char *r = malloc(size + 1);
+	strncpy(r, s, size);
+	r[size] = '\0';
+	return r;
+}
+#endif
+
 static void
 end_element(void *data, const XML_Char *name)
 {
@@ -1567,6 +1636,8 @@
 	printf("\n");
 
 	wl_list_for_each(i, &protocol->interface_list, link) {
+		printf("#ifndef %s_INTERFACE\n", i->uppercase_name);
+		printf("#define %s_INTERFACE\n", i->uppercase_name);
 		printf("/**\n"
 		       " * @page page_iface_%s %s\n",
 		       i->name, i->name);
@@ -1587,6 +1658,7 @@
 		printf(" */\n");
 		printf("extern const struct wl_interface "
 		       "%s_interface;\n", i->name);
+		printf("#endif\n");
 	}
 
 	printf("\n");
@@ -1663,7 +1735,7 @@
 }
 
 static void
-emit_messages(struct wl_list *message_list,
+emit_messages(const char *name, struct wl_list *message_list,
 	      struct interface *interface, const char *suffix)
 {
 	struct message *m;
@@ -1716,7 +1788,7 @@
 				break;
 			}
 		}
-		printf("\", types + %d },\n", m->type_index);
+		printf("\", %s_types + %d },\n", name, m->type_index);
 	}
 
 	printf("};\n\n");
@@ -1773,7 +1845,7 @@
 	wl_array_release(&types);
 	printf("\n");
 
-	printf("static const struct wl_interface *types[] = {\n");
+	printf("static const struct wl_interface *%s_types[] = {\n", protocol->name);
 	emit_null_run(protocol);
 	wl_list_for_each(i, &protocol->interface_list, link) {
 		emit_types(protocol, &i->request_list);
@@ -1783,8 +1855,8 @@
 
 	wl_list_for_each_safe(i, next, &protocol->interface_list, link) {
 
-		emit_messages(&i->request_list, i, "requests");
-		emit_messages(&i->event_list, i, "events");
+		emit_messages(protocol->name, &i->request_list, i, "requests");
+		emit_messages(protocol->name, &i->event_list, i, "events");
 
 		printf("%s const struct wl_interface "
 		       "%s_interface = {\n"
@@ -1956,7 +2028,7 @@
 		buf = XML_GetBuffer(ctx.parser, XML_BUFFER_SIZE);
 		len = fread(buf, 1, XML_BUFFER_SIZE, input);
 		if (len < 0) {
-			fprintf(stderr, "fread: %m\n");
+			fprintf(stderr, "fread: %s\n", strerror(errno));
 			fclose(input);
 			exit(EXIT_FAILURE);
 		}
diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h
index 03e781b..0cd96e0 100644
--- a/src/wayland-client-core.h
+++ b/src/wayland-client-core.h
@@ -191,6 +191,13 @@
 uint32_t
 wl_proxy_get_id(struct wl_proxy *proxy);
 
+void
+wl_proxy_set_tag(struct wl_proxy *proxy,
+		 const char * const *tag);
+
+const char * const *
+wl_proxy_get_tag(struct wl_proxy *proxy);
+
 const char *
 wl_proxy_get_class(struct wl_proxy *proxy);
 
diff --git a/src/wayland-client.c b/src/wayland-client.c
index 0ccfc66..21d4606 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -69,6 +69,7 @@
 	void *user_data;
 	wl_dispatcher_func_t dispatcher;
 	uint32_t version;
+	const char * const *tag;
 };
 
 struct wl_event_queue {
@@ -185,6 +186,9 @@
 		case WL_DISPLAY_ERROR_NO_MEMORY:
 			err = ENOMEM;
 			break;
+		case WL_DISPLAY_ERROR_IMPLEMENTATION:
+			err = EPROTO;
+			break;
 		default:
 			err = EFAULT;
 		}
@@ -295,8 +299,8 @@
 	struct wl_closure *closure;
 
 	while (!wl_list_empty(&queue->event_list)) {
-		closure = container_of(queue->event_list.next,
-				       struct wl_closure, link);
+		closure = wl_container_of(queue->event_list.next,
+					  closure, link);
 		wl_list_remove(&closure->link);
 		destroy_queued_closure(closure);
 	}
@@ -736,15 +740,24 @@
 			goto err_unlock;
 	}
 
+	if (proxy->display->last_error) {
+		goto err_unlock;
+	}
+
 	closure = wl_closure_marshal(&proxy->object, opcode, args, message);
-	if (closure == NULL)
-		wl_abort("Error marshalling request: %s\n", strerror(errno));
+	if (closure == NULL) {
+		wl_log("Error marshalling request: %s\n", strerror(errno));
+		display_fatal_error(proxy->display, errno);
+		goto err_unlock;
+	}
 
 	if (debug_client)
 		wl_closure_print(closure, &proxy->object, true);
 
-	if (wl_closure_send(closure, proxy->display->connection))
-		wl_abort("Error sending request: %s\n", strerror(errno));
+	if (wl_closure_send(closure, proxy->display->connection)) {
+		wl_log("Error sending request: %s\n", strerror(errno));
+		display_fatal_error(proxy->display, errno);
+	}
 
 	wl_closure_destroy(closure);
 
@@ -1100,6 +1113,13 @@
  * its value will be replaced with the WAYLAND_DISPLAY environment
  * variable if it is set, otherwise display "wayland-0" will be used.
  *
+ * If WAYLAND_SOCKET is set, it's interpreted as a file descriptor number
+ * referring to an already opened socket. In this case, the socket is used
+ * as-is and \c name is ignored.
+ *
+ * If \c name is a relative path, then the socket is opened relative to
+ * the XDG_RUNTIME_DIR directory.
+ *
  * If \c name is an absolute path, then that path is used as-is for
  * the location of the socket at which the Wayland server is listening;
  * no qualification inside XDG_RUNTIME_DIR is attempted.
@@ -1360,6 +1380,12 @@
 		return size;
 	}
 
+	if (opcode >= proxy->object.interface->event_count) {
+		wl_log("interface '%s' has no event %u\n",
+		       proxy->object.interface->name, opcode);
+		return -1;
+	}
+
 	message = &proxy->object.interface->events[opcode];
 	closure = wl_connection_demarshal(display->connection, size,
 					  &display->objects, message);
@@ -1397,8 +1423,7 @@
 	int opcode;
 	bool proxy_destroyed;
 
-	closure = container_of(queue->event_list.next,
-			       struct wl_closure, link);
+	closure = wl_container_of(queue->event_list.next, closure, link);
 	wl_list_remove(&closure->link);
 	opcode = closure->opcode;
 
@@ -2057,6 +2082,70 @@
 	return proxy->object.id;
 }
 
+/** Set the tag of a proxy object
+ *
+ * A toolkit or application can set a unique tag on a proxy in order to
+ * identify whether an object is managed by itself or some external part.
+ *
+ * To create a tag, the recommended way is to define a statically allocated
+ * constant char array containing some descriptive string. The tag will be the
+ * pointer to the non-const pointer to the beginning of the array.
+ *
+ * For example, to define and set a tag on a surface managed by a certain
+ * subsystem:
+ *
+ * 	static const char *my_tag = "my tag";
+ *
+ * 	wl_proxy_set_tag((struct wl_proxy *) surface, &my_tag);
+ *
+ * Then, in a callback with wl_surface as an argument, in order to check
+ * whether it's a surface managed by the same subsystem.
+ *
+ * 	const char * const *tag;
+ *
+ * 	tag = wl_proxy_get_tag((struct wl_proxy *) surface);
+ * 	if (tag != &my_tag)
+ *		return;
+ *
+ *	...
+ *
+ * For debugging purposes, a tag should be suitable to be included in a debug
+ * log entry, e.g.
+ *
+ * 	const char * const *tag;
+ *
+ * 	tag = wl_proxy_get_tag((struct wl_proxy *) surface);
+ * 	printf("Got a surface with the tag %p (%s)\n",
+ * 	       tag, (tag && *tag) ? *tag : "");
+ *
+ * \param proxy The proxy object
+ * \param tag The tag
+ *
+ * \memberof wl_proxy
+ * \since 1.17.90
+ */
+WL_EXPORT void
+wl_proxy_set_tag(struct wl_proxy *proxy,
+		 const char * const *tag)
+{
+	proxy->tag = tag;
+}
+
+/** Get the tag of a proxy object
+ *
+ * See wl_proxy_set_tag for details.
+ *
+ * \param proxy The proxy object
+ *
+ * \memberof wl_proxy
+ * \since 1.17.90
+ */
+WL_EXPORT const char * const *
+wl_proxy_get_tag(struct wl_proxy *proxy)
+{
+	return proxy->tag;
+}
+
 /** Get the interface name (class) of a proxy object
  *
  * \param proxy The proxy object
diff --git a/src/wayland-private.h b/src/wayland-private.h
index 29516ec..9bf8cb7 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -31,21 +31,17 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdbool.h>
 
 #define WL_HIDE_DEPRECATED 1
 
 #include "wayland-util.h"
-#include "wayland-server-core.h"
 
 /* Invalid memory address */
 #define WL_ARRAY_POISON_PTR (void *) 4
 
 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
 
-#define container_of(ptr, type, member) ({				\
-	const __typeof__( ((type *)0)->member ) *__mptr = (ptr);	\
-	(type *)( (char *)__mptr - offsetof(type,member) );})
-
 #define WL_MAP_SERVER_SIDE 0
 #define WL_MAP_CLIENT_SIDE 1
 #define WL_SERVER_ID_START 0xff000000
@@ -236,26 +232,6 @@
 	return calloc(1, s);
 }
 
-struct wl_priv_signal {
-	struct wl_list listener_list;
-	struct wl_list emit_list;
-};
-
-void
-wl_priv_signal_init(struct wl_priv_signal *signal);
-
-void
-wl_priv_signal_add(struct wl_priv_signal *signal, struct wl_listener *listener);
-
-struct wl_listener *
-wl_priv_signal_get(struct wl_priv_signal *signal, wl_notify_func_t notify);
-
-void
-wl_priv_signal_emit(struct wl_priv_signal *signal, void *data);
-
-void
-wl_priv_signal_final_emit(struct wl_priv_signal *signal, void *data);
-
 void
 wl_connection_close_fds_in(struct wl_connection *connection, int max);
 
diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h
index 2e725d9..fd4fc3e 100644
--- a/src/wayland-server-core.h
+++ b/src/wayland-server-core.h
@@ -247,6 +247,9 @@
 		 void *data, wl_global_bind_func_t bind);
 
 void
+wl_global_remove(struct wl_global *global);
+
+void
 wl_global_destroy(struct wl_global *global);
 
 /** A filter function for wl_global objects
@@ -279,6 +282,9 @@
 void *
 wl_global_get_user_data(const struct wl_global *global);
 
+void
+wl_global_set_user_data(struct wl_global *global, void *data);
+
 struct wl_client *
 wl_client_create(struct wl_display *display, int fd);
 
@@ -325,6 +331,10 @@
 wl_client_post_no_memory(struct wl_client *client);
 
 void
+wl_client_post_implementation_error(struct wl_client *client,
+                                    const char* msg, ...) WL_PRINTF(2,3);
+
+void
 wl_client_add_resource_created_listener(struct wl_client *client,
                                         struct wl_listener *listener);
 
diff --git a/src/wayland-server-private.h b/src/wayland-server-private.h
new file mode 100644
index 0000000..23fa458
--- /dev/null
+++ b/src/wayland-server-private.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2013 Jason Ekstrand
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef WAYLAND_SERVER_PRIVATE_H
+#define WAYLAND_SERVER_PRIVATE_H
+
+#include "wayland-server-core.h"
+
+struct wl_priv_signal {
+	struct wl_list listener_list;
+	struct wl_list emit_list;
+};
+
+void
+wl_priv_signal_init(struct wl_priv_signal *signal);
+
+void
+wl_priv_signal_add(struct wl_priv_signal *signal, struct wl_listener *listener);
+
+struct wl_listener *
+wl_priv_signal_get(struct wl_priv_signal *signal, wl_notify_func_t notify);
+
+void
+wl_priv_signal_emit(struct wl_priv_signal *signal, void *data);
+
+void
+wl_priv_signal_final_emit(struct wl_priv_signal *signal, void *data);
+
+#endif
diff --git a/src/wayland-server.c b/src/wayland-server.c
index eae8d2e..3f48dfe 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -25,6 +25,7 @@
 
 #define _GNU_SOURCE
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <stddef.h>
@@ -45,6 +46,7 @@
 
 #include "wayland-util.h"
 #include "wayland-private.h"
+#include "wayland-server-private.h"
 #include "wayland-server.h"
 #include "wayland-os.h"
 
@@ -112,6 +114,7 @@
 	void *data;
 	wl_global_bind_func_t bind;
 	struct wl_list link;
+	bool removed;
 };
 
 struct wl_resource {
@@ -273,17 +276,14 @@
 	wl_resource_queue_event_array(resource, opcode, args);
 }
 
-WL_EXPORT void
-wl_resource_post_error(struct wl_resource *resource,
-		       uint32_t code, const char *msg, ...)
+static void
+wl_resource_post_error_vargs(struct wl_resource *resource,
+			     uint32_t code, const char *msg, va_list argp)
 {
 	struct wl_client *client = resource->client;
 	char buffer[128];
-	va_list ap;
 
-	va_start(ap, msg);
-	vsnprintf(buffer, sizeof buffer, msg, ap);
-	va_end(ap);
+	vsnprintf(buffer, sizeof buffer, msg, argp);
 
 	/*
 	 * When a client aborts, its resources are destroyed in id order,
@@ -298,6 +298,18 @@
 	wl_resource_post_event(client->display_resource,
 			       WL_DISPLAY_ERROR, resource, code, buffer);
 	client->error = 1;
+
+}
+
+WL_EXPORT void
+wl_resource_post_error(struct wl_resource *resource,
+		       uint32_t code, const char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	wl_resource_post_error_vargs(resource, code, msg, ap);
+	va_end(ap);
 }
 
 static void
@@ -641,6 +653,30 @@
 			       WL_DISPLAY_ERROR_NO_MEMORY, "no memory");
 }
 
+/** Report an internal server error
+ *
+ * \param client The client object
+ * \param msg A printf-style format string
+ * \param ... Format string arguments
+ *
+ * Report an unspecified internal implementation error and disconnect
+ * the client.
+ *
+ * \memberof wl_client
+ */
+WL_EXPORT void
+wl_client_post_implementation_error(struct wl_client *client,
+				    char const *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	wl_resource_post_error_vargs(client->display_resource,
+				     WL_DISPLAY_ERROR_IMPLEMENTATION,
+				     msg, ap);
+	va_end(ap);
+}
+
 WL_EXPORT void
 wl_resource_post_no_memory(struct wl_resource *resource)
 {
@@ -884,6 +920,12 @@
 		wl_resource_post_error(resource,
 				       WL_DISPLAY_ERROR_INVALID_OBJECT,
 				       "invalid global %s (%d)", interface, name);
+	else if (strcmp(global->interface->name, interface) != 0)
+		wl_resource_post_error(resource,
+				       WL_DISPLAY_ERROR_INVALID_OBJECT,
+				       "invalid interface for global %u: "
+				       "have %s, wanted %s",
+				       name, interface, global->interface->name);
 	else if (version == 0)
 		wl_resource_post_error(resource,
 				       WL_DISPLAY_ERROR_INVALID_OBJECT,
@@ -953,7 +995,7 @@
 		       &registry_resource->link);
 
 	wl_list_for_each(global, &display->global_list, link)
-		if (wl_global_is_visible(client, global))
+		if (wl_global_is_visible(client, global) && !global->removed)
 			wl_resource_post_event(registry_resource,
 					       WL_REGISTRY_GLOBAL,
 					       global->name,
@@ -1169,6 +1211,7 @@
 	global->version = version;
 	global->data = data;
 	global->bind = bind;
+	global->removed = false;
 	wl_list_insert(display->global_list.prev, &global->link);
 
 	wl_list_for_each(resource, &display->registry_resource_list, link)
@@ -1181,15 +1224,50 @@
 	return global;
 }
 
+/** Remove the global
+ *
+ * \param global The Wayland global.
+ *
+ * Broadcast a global remove event to all clients without destroying the
+ * global. This function can only be called once per global.
+ *
+ * wl_global_destroy() removes the global and immediately destroys it. On
+ * the other end, this function only removes the global, allowing clients
+ * that have not yet received the global remove event to continue to bind to
+ * it.
+ *
+ * This can be used by compositors to mitigate clients being disconnected
+ * because a global has been added and removed too quickly. Compositors can call
+ * wl_global_remove(), then wait an implementation-defined amount of time, then
+ * call wl_global_destroy(). Note that the destruction of a global is still
+ * racy, since clients have no way to acknowledge that they received the remove
+ * event.
+ *
+ * \since 1.17.90
+ */
 WL_EXPORT void
-wl_global_destroy(struct wl_global *global)
+wl_global_remove(struct wl_global *global)
 {
 	struct wl_display *display = global->display;
 	struct wl_resource *resource;
 
+	if (global->removed)
+		wl_abort("wl_global_remove: called twice on the same "
+			 "global '%s@%"PRIu32"'", global->interface->name,
+			 global->name);
+
 	wl_list_for_each(resource, &display->registry_resource_list, link)
 		wl_resource_post_event(resource, WL_REGISTRY_GLOBAL_REMOVE,
 				       global->name);
+
+	global->removed = true;
+}
+
+WL_EXPORT void
+wl_global_destroy(struct wl_global *global)
+{
+	if (!global->removed)
+		wl_global_remove(global);
 	wl_list_remove(&global->link);
 	free(global);
 }
@@ -1206,6 +1284,19 @@
 	return global->data;
 }
 
+/** Set the global's user data
+ *
+ * \param global The global object
+ * \param data The user data pointer
+ *
+ * \since 1.17.90
+ */
+WL_EXPORT void
+wl_global_set_user_data(struct wl_global *global, void *data)
+{
+	global->data = data;
+}
+
 /** Get the current serial number
  *
  * \param display The display object
@@ -1329,7 +1420,7 @@
 	client_fd = wl_os_accept_cloexec(fd, (struct sockaddr *) &name,
 					 &length);
 	if (client_fd < 0)
-		wl_log("failed to accept: %m\n");
+		wl_log("failed to accept: %s\n", strerror(errno));
 	else
 		if (!wl_client_create(display, client_fd))
 			close(client_fd);
@@ -1345,7 +1436,7 @@
 	snprintf(socket->lock_addr, sizeof socket->lock_addr,
 		 "%s%s", socket->addr.sun_path, LOCK_SUFFIX);
 
-	socket->fd_lock = open(socket->lock_addr, O_CREAT | O_CLOEXEC,
+	socket->fd_lock = open(socket->lock_addr, O_CREAT | O_CLOEXEC | O_RDWR,
 			       (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
 
 	if (socket->fd_lock < 0) {
@@ -1360,7 +1451,7 @@
 		goto err_fd;
 	}
 
-	if (stat(socket->addr.sun_path, &socket_stat) < 0 ) {
+	if (lstat(socket->addr.sun_path, &socket_stat) < 0 ) {
 		if (errno != ENOENT) {
 			wl_log("did not manage to stat file %s\n",
 				socket->addr.sun_path);
@@ -1434,12 +1525,12 @@
 
 	size = offsetof (struct sockaddr_un, sun_path) + strlen(s->addr.sun_path);
 	if (bind(s->fd, (struct sockaddr *) &s->addr, size) < 0) {
-		wl_log("bind() failed with error: %m\n");
+		wl_log("bind() failed with error: %s\n", strerror(errno));
 		return -1;
 	}
 
 	if (listen(s->fd, 128) < 0) {
-		wl_log("listen() failed with error: %m\n");
+		wl_log("listen() failed with error: %s\n", strerror(errno));
 		return -1;
 	}
 
@@ -1866,7 +1957,9 @@
 WL_EXPORT struct wl_client *
 wl_client_from_link(struct wl_list *link)
 {
-	return container_of(link, struct wl_client, link);
+	struct wl_client *client;
+
+	return wl_container_of(link, client, link);
 }
 
 /** Add a listener for the client's resource creation signal
diff --git a/src/wayland-shm.c b/src/wayland-shm.c
index 4191231..b85e5a7 100644
--- a/src/wayland-shm.c
+++ b/src/wayland-shm.c
@@ -30,6 +30,8 @@
 
 #define _GNU_SOURCE
 
+#include "config.h"
+
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -40,6 +42,8 @@
 #include <assert.h>
 #include <signal.h>
 #include <pthread.h>
+#include <errno.h>
+#include <fcntl.h>
 
 #include "wayland-util.h"
 #include "wayland-private.h"
@@ -59,6 +63,7 @@
 	char *data;
 	int32_t size;
 	int32_t new_size;
+	bool sigbus_is_impossible;
 };
 
 struct wl_shm_buffer {
@@ -258,6 +263,7 @@
 		uint32_t id, int fd, int32_t size)
 {
 	struct wl_shm_pool *pool;
+	int seals;
 
 	if (size <= 0) {
 		wl_resource_post_error(resource,
@@ -272,6 +278,15 @@
 		goto err_close;
 	}
 
+#ifdef HAVE_MEMFD_CREATE
+	seals = fcntl(fd, F_GET_SEALS);
+	if (seals == -1)
+		seals = 0;
+	pool->sigbus_is_impossible = (seals & F_SEAL_SHRINK) ? true : false;
+#else
+	pool->sigbus_is_impossible = false;
+#endif
+
 	pool->internal_refcount = 1;
 	pool->external_refcount = 0;
 	pool->size = size;
@@ -281,7 +296,8 @@
 	if (pool->data == MAP_FAILED) {
 		wl_resource_post_error(resource,
 				       WL_SHM_ERROR_INVALID_FD,
-				       "failed mmap fd %d: %m", fd);
+				       "failed mmap fd %d: %s", fd,
+				       strerror(errno));
 		goto err_free;
 	}
 	close(fd);
@@ -569,6 +585,9 @@
 	struct wl_shm_pool *pool = buffer->pool;
 	struct wl_shm_sigbus_data *sigbus_data;
 
+	if (pool->sigbus_is_impossible)
+		return;
+
 	pthread_once(&wl_shm_sigbus_once, init_sigbus_data_key);
 
 	sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
@@ -601,9 +620,13 @@
 WL_EXPORT void
 wl_shm_buffer_end_access(struct wl_shm_buffer *buffer)
 {
-	struct wl_shm_sigbus_data *sigbus_data =
-		pthread_getspecific(wl_shm_sigbus_data_key);
+	struct wl_shm_pool *pool = buffer->pool;
+	struct wl_shm_sigbus_data *sigbus_data;
 
+	if (pool->sigbus_is_impossible)
+		return;
+
+	sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
 	assert(sigbus_data && sigbus_data->access_count >= 1);
 
 	if (--sigbus_data->access_count == 0) {
diff --git a/src/wayland-util.c b/src/wayland-util.c
index 3a471a8..d5973bf 100644
--- a/src/wayland-util.c
+++ b/src/wayland-util.c
@@ -131,7 +131,7 @@
 		array->alloc = alloc;
 	}
 
-	p = array->data + array->size;
+	p = (char *)array->data + array->size;
 	array->size += size;
 
 	return p;
diff --git a/src/wayland-util.h b/src/wayland-util.h
index b6cbe0e..7997778 100644
--- a/src/wayland-util.h
+++ b/src/wayland-util.h
@@ -68,6 +68,20 @@
 #define WL_PRINTF(x, y)
 #endif
 
+/** \class wl_object
+ *
+ * \brief A protocol object.
+ *
+ * A `wl_object` is an opaque struct identifying the protocol object
+ * underlying a `wl_proxy` or `wl_resource`.
+ *
+ * \note Functions accessing a `wl_object` are not normally used by client code.
+ * Clients should normally use the higher level interface generated by the
+ * scanner to interact with compositor objects.
+ *
+ */
+struct wl_object;
+
 /**
  * Protocol message signature
  *
@@ -626,7 +640,7 @@
 
 	u.d = d + (3LL << (51 - 8));
 
-	return u.i;
+	return (wl_fixed_t)u.i;
 }
 
 /**
diff --git a/src/wayland-version.h b/src/wayland-version.h
index 2ae610b..71a1162 100644
--- a/src/wayland-version.h
+++ b/src/wayland-version.h
@@ -27,8 +27,8 @@
 #define WAYLAND_VERSION_H
 
 #define WAYLAND_VERSION_MAJOR 1
-#define WAYLAND_VERSION_MINOR 16
+#define WAYLAND_VERSION_MINOR 18
 #define WAYLAND_VERSION_MICRO 90
-#define WAYLAND_VERSION "1.16.90"
+#define WAYLAND_VERSION "1.18.90"
 
 #endif
diff --git a/tests/array-test.c b/tests/array-test.c
index eda610b..78dfbb0 100644
--- a/tests/array-test.c
+++ b/tests/array-test.c
@@ -87,8 +87,7 @@
 
 	/* verify the data */
 	for (i = 0; i < iterations; ++i) {
-		const int index = datasize * i;
-		struct mydata* check = (struct mydata*)(array.data + index);
+		struct mydata* check = (struct mydata*)array.data + i;
 
 		assert(check->a == i * 3);
 		assert(check->b == 20000 - i);
@@ -121,9 +120,8 @@
 
 	/* check the copy */
 	for (i = 0; i < iterations; i++) {
-		const int index = sizeof(int) * i;
-		int *s = (int *)(source.data + index);
-		int *c = (int *)(copy.data + index);
+		int *s = (int *)source.data + i;
+		int *c = (int *)copy.data + i;
 
 		assert(*s == *c); /* verify the values are the same */
 		assert(s != c); /* ensure the addresses aren't the same */
diff --git a/tests/client-test.c b/tests/client-test.c
index 2e332f8..960cfa9 100644
--- a/tests/client-test.c
+++ b/tests/client-test.c
@@ -47,7 +47,7 @@
 client_destroy_notify(struct wl_listener *l, void *data)
 {
 	struct client_destroy_listener *listener =
-		container_of(l, struct client_destroy_listener, listener);
+		wl_container_of(l, listener, listener);
 
 	listener->done = 1;
 }
diff --git a/tests/connection-test.c b/tests/connection-test.c
index 018e2ac..a06a3cc 100644
--- a/tests/connection-test.c
+++ b/tests/connection-test.c
@@ -752,3 +752,90 @@
 
 	display_destroy(d);
 }
+
+/** Raw read from socket expecting wl_display.error
+ *
+ * \param sockfd The socket to read from.
+ * \param expected_error The expected wl_display error code.
+ *
+ * Reads the socket and manually parses one message, expecting it to be a
+ * wl_display.error with the wl_display as the originating object.
+ * Asserts that the received error code is expected_error.
+ */
+static void
+expect_error_recv(int sockfd, uint32_t expected_error)
+{
+	uint32_t buf[1024];
+	ssize_t slen;
+	uint32_t opcode;
+	int str_len;
+
+	slen = recv(sockfd, buf, sizeof buf, 0);
+	assert(slen >= 2 * (ssize_t)sizeof (uint32_t));
+	opcode = buf[1] & 0xffff;
+	fprintf(stderr, "Received %zd bytes, object %u, opcode %u\n",
+		slen, buf[0], opcode);
+
+	/* check error event */
+	assert(buf[0] == 1);
+	assert(opcode == WL_DISPLAY_ERROR);
+
+	str_len = buf[4];
+	assert(str_len > 0);
+	assert(str_len <= slen - 5 * (ssize_t)sizeof (uint32_t));
+	fprintf(stderr, "Error event on object %u, code %u, message \"%*s\"\n",
+		buf[2], buf[3], str_len, (const char *)&buf[5]);
+
+	assert(buf[3] == expected_error);
+}
+
+/* A test for https://gitlab.freedesktop.org/wayland/wayland/issues/52
+ * trying to provoke a read from uninitialized memory in
+ * wl_connection_demarshal() for sender_id and opcode.
+ *
+ * This test might not fail as is even with #52 unfixed, since there is no way
+ * to detect what happens and the crash with zero size depends on stack content.
+ * However, running under Valgrind would point out invalid reads and use of
+ * uninitialized values.
+ */
+TEST(request_bogus_size)
+{
+	struct wl_display *display;
+	struct wl_client *client;
+	int s[2];
+	uint32_t msg[3];
+	int bogus_size;
+
+	test_set_timeout(1);
+
+	/*
+	 * The manufactured message has real size 12. Test all bogus sizes
+	 * smaller than that, and zero as the last one since wl_closure_init
+	 * handles zero specially and having garbage in the stack makes it more
+	 * likely to crash in wl_connection_demarshal.
+	 */
+	for (bogus_size = 11; bogus_size >= 0; bogus_size--) {
+		fprintf(stderr, "* bogus size %d\n", bogus_size);
+
+		assert(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, s) == 0);
+		display = wl_display_create();
+		assert(display);
+		client = wl_client_create(display, s[0]);
+		assert(client);
+
+		/* manufacture a request that lies about its size */
+		msg[0] = 1; /* sender id: wl_display */
+		msg[1] = (bogus_size << 16) | WL_DISPLAY_SYNC; /* size and opcode */
+		msg[2] = 2; /* sync argument: new_id for wl_callback */
+
+		assert(send(s[1], msg, sizeof msg, 0) == sizeof msg);
+
+		wl_event_loop_dispatch(wl_display_get_event_loop(display), 0);
+
+		expect_error_recv(s[1], WL_DISPLAY_ERROR_INVALID_METHOD);
+
+		/* Do not wl_client_destroy, the error already caused it. */
+		close(s[1]);
+		wl_display_destroy(display);
+	}
+}
diff --git a/tests/data/bad-identifier-arg.xml b/tests/data/bad-identifier-arg.xml
new file mode 100644
index 0000000..ac2a6b7
--- /dev/null
+++ b/tests/data/bad-identifier-arg.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="the_protocol">
+  <interface name="the_interface" version="1">
+    <description summary="the summary">
+    </description>
+    <request name="the_request">
+      <arg name="" type="uint"/>
+    </request>
+  </interface>
+</protocol>
diff --git a/tests/data/bad-identifier-entry.xml b/tests/data/bad-identifier-entry.xml
new file mode 100644
index 0000000..6ea2fae
--- /dev/null
+++ b/tests/data/bad-identifier-entry.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="the_protocol">
+  <interface name="the_interface" version="1">
+    <description summary="the summary">
+    </description>
+    <enum name="4the_enum">
+      <entry name="60_seconds" value="1" summary="this is the first"/>
+      <entry name="invalid entry" value="2" summary="this is the first"/>
+    </enum>
+  </interface>
+</protocol>
diff --git a/tests/data/bad-identifier-enum.xml b/tests/data/bad-identifier-enum.xml
new file mode 100644
index 0000000..3225384
--- /dev/null
+++ b/tests/data/bad-identifier-enum.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="the_protocol">
+  <interface name="the_interface" version="1">
+    <description summary="the summary">
+    </description>
+    <enum name="the-enum">
+      <entry name="the_entry" value="0" summary="entry summary"/>
+    </enum>
+  </interface>
+</protocol>
diff --git a/tests/data/bad-identifier-event.xml b/tests/data/bad-identifier-event.xml
new file mode 100644
index 0000000..9708e3b
--- /dev/null
+++ b/tests/data/bad-identifier-event.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="the_protocol">
+  <interface name="the_interface" version="1">
+    <description summary="the summary">
+    </description>
+    <event name="theΔevent"/>
+  </interface>
+</protocol>
diff --git a/tests/data/bad-identifier-interface.xml b/tests/data/bad-identifier-interface.xml
new file mode 100644
index 0000000..17404c5
--- /dev/null
+++ b/tests/data/bad-identifier-interface.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="the_protocol">
+  <interface name="inter face" version="1">
+    <description summary="the summary">
+    </description>
+    <event name="the_event"/>
+  </interface>
+</protocol>
diff --git a/tests/data/bad-identifier-protocol.xml b/tests/data/bad-identifier-protocol.xml
new file mode 100644
index 0000000..7a17204
--- /dev/null
+++ b/tests/data/bad-identifier-protocol.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="1badprotocol">
+  <interface name="required_interface" version="13">
+    <description summary="required summary">
+    </description>
+    <event name="requied_event"/>
+  </interface>
+</protocol>
diff --git a/tests/data/bad-identifier-request.xml b/tests/data/bad-identifier-request.xml
new file mode 100644
index 0000000..a68c8aa
--- /dev/null
+++ b/tests/data/bad-identifier-request.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="the_protocol">
+  <interface name="the_interface" version="1">
+    <description summary="the summary">
+    </description>
+    <request name="req-west">
+      <arg name="the_arg" type="uint"/>
+    </request>
+  </interface>
+</protocol>
diff --git a/tests/data/example-client.h b/tests/data/example-client.h
index c40e361..d421af9 100644
--- a/tests/data/example-client.h
+++ b/tests/data/example-client.h
@@ -88,6 +88,8 @@
 struct wl_surface;
 struct wl_touch;
 
+#ifndef WL_DISPLAY_INTERFACE
+#define WL_DISPLAY_INTERFACE
 /**
  * @page page_iface_wl_display wl_display
  * @section page_iface_wl_display_desc Description
@@ -104,6 +106,9 @@
  * is used for internal Wayland protocol features.
  */
 extern const struct wl_interface wl_display_interface;
+#endif
+#ifndef WL_REGISTRY_INTERFACE
+#define WL_REGISTRY_INTERFACE
 /**
  * @page page_iface_wl_registry wl_registry
  * @section page_iface_wl_registry_desc Description
@@ -156,6 +161,9 @@
  * the object.
  */
 extern const struct wl_interface wl_registry_interface;
+#endif
+#ifndef WL_CALLBACK_INTERFACE
+#define WL_CALLBACK_INTERFACE
 /**
  * @page page_iface_wl_callback wl_callback
  * @section page_iface_wl_callback_desc Description
@@ -172,6 +180,9 @@
  * the related request is done.
  */
 extern const struct wl_interface wl_callback_interface;
+#endif
+#ifndef WL_COMPOSITOR_INTERFACE
+#define WL_COMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_compositor wl_compositor
  * @section page_iface_wl_compositor_desc Description
@@ -190,6 +201,9 @@
  * surfaces into one displayable output.
  */
 extern const struct wl_interface wl_compositor_interface;
+#endif
+#ifndef WL_SHM_POOL_INTERFACE
+#define WL_SHM_POOL_INTERFACE
 /**
  * @page page_iface_wl_shm_pool wl_shm_pool
  * @section page_iface_wl_shm_pool_desc Description
@@ -216,6 +230,9 @@
  * a surface or for many small buffers.
  */
 extern const struct wl_interface wl_shm_pool_interface;
+#endif
+#ifndef WL_SHM_INTERFACE
+#define WL_SHM_INTERFACE
 /**
  * @page page_iface_wl_shm wl_shm
  * @section page_iface_wl_shm_desc Description
@@ -246,6 +263,9 @@
  * that can be used for buffers.
  */
 extern const struct wl_interface wl_shm_interface;
+#endif
+#ifndef WL_BUFFER_INTERFACE
+#define WL_BUFFER_INTERFACE
 /**
  * @page page_iface_wl_buffer wl_buffer
  * @section page_iface_wl_buffer_desc Description
@@ -268,6 +288,9 @@
  * updates the contents is defined by the buffer factory interface.
  */
 extern const struct wl_interface wl_buffer_interface;
+#endif
+#ifndef WL_DATA_OFFER_INTERFACE
+#define WL_DATA_OFFER_INTERFACE
 /**
  * @page page_iface_wl_data_offer wl_data_offer
  * @section page_iface_wl_data_offer_desc Description
@@ -292,6 +315,9 @@
  * data directly from the source client.
  */
 extern const struct wl_interface wl_data_offer_interface;
+#endif
+#ifndef WL_DATA_SOURCE_INTERFACE
+#define WL_DATA_SOURCE_INTERFACE
 /**
  * @page page_iface_wl_data_source wl_data_source
  * @section page_iface_wl_data_source_desc Description
@@ -312,6 +338,9 @@
  * to requests to transfer the data.
  */
 extern const struct wl_interface wl_data_source_interface;
+#endif
+#ifndef WL_DATA_DEVICE_INTERFACE
+#define WL_DATA_DEVICE_INTERFACE
 /**
  * @page page_iface_wl_data_device wl_data_device
  * @section page_iface_wl_data_device_desc Description
@@ -334,6 +363,9 @@
  * mechanisms such as copy-and-paste and drag-and-drop.
  */
 extern const struct wl_interface wl_data_device_interface;
+#endif
+#ifndef WL_DATA_DEVICE_MANAGER_INTERFACE
+#define WL_DATA_DEVICE_MANAGER_INTERFACE
 /**
  * @page page_iface_wl_data_device_manager wl_data_device_manager
  * @section page_iface_wl_data_device_manager_desc Description
@@ -366,6 +398,9 @@
  * wl_data_offer.accept and wl_data_offer.finish for details.
  */
 extern const struct wl_interface wl_data_device_manager_interface;
+#endif
+#ifndef WL_SHELL_INTERFACE
+#define WL_SHELL_INTERFACE
 /**
  * @page page_iface_wl_shell wl_shell
  * @section page_iface_wl_shell_desc Description
@@ -388,6 +423,9 @@
  * a basic surface.
  */
 extern const struct wl_interface wl_shell_interface;
+#endif
+#ifndef WL_SHELL_SURFACE_INTERFACE
+#define WL_SHELL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_shell_surface wl_shell_surface
  * @section page_iface_wl_shell_surface_desc Description
@@ -422,6 +460,9 @@
  * the wl_surface object.
  */
 extern const struct wl_interface wl_shell_surface_interface;
+#endif
+#ifndef WL_SURFACE_INTERFACE
+#define WL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_surface wl_surface
  * @section page_iface_wl_surface_desc Description
@@ -512,6 +553,9 @@
  * switching is not allowed).
  */
 extern const struct wl_interface wl_surface_interface;
+#endif
+#ifndef WL_SEAT_INTERFACE
+#define WL_SEAT_INTERFACE
 /**
  * @page page_iface_wl_seat wl_seat
  * @section page_iface_wl_seat_desc Description
@@ -532,6 +576,9 @@
  * maintains a keyboard focus and a pointer focus.
  */
 extern const struct wl_interface wl_seat_interface;
+#endif
+#ifndef WL_POINTER_INTERFACE
+#define WL_POINTER_INTERFACE
 /**
  * @page page_iface_wl_pointer wl_pointer
  * @section page_iface_wl_pointer_desc Description
@@ -560,6 +607,9 @@
  * and scrolling.
  */
 extern const struct wl_interface wl_pointer_interface;
+#endif
+#ifndef WL_KEYBOARD_INTERFACE
+#define WL_KEYBOARD_INTERFACE
 /**
  * @page page_iface_wl_keyboard wl_keyboard
  * @section page_iface_wl_keyboard_desc Description
@@ -576,6 +626,9 @@
  * associated with a seat.
  */
 extern const struct wl_interface wl_keyboard_interface;
+#endif
+#ifndef WL_TOUCH_INTERFACE
+#define WL_TOUCH_INTERFACE
 /**
  * @page page_iface_wl_touch wl_touch
  * @section page_iface_wl_touch_desc Description
@@ -604,6 +657,9 @@
  * contact point can be identified by the ID of the sequence.
  */
 extern const struct wl_interface wl_touch_interface;
+#endif
+#ifndef WL_OUTPUT_INTERFACE
+#define WL_OUTPUT_INTERFACE
 /**
  * @page page_iface_wl_output wl_output
  * @section page_iface_wl_output_desc Description
@@ -628,6 +684,9 @@
  * as global during start up, or when a monitor is hotplugged.
  */
 extern const struct wl_interface wl_output_interface;
+#endif
+#ifndef WL_REGION_INTERFACE
+#define WL_REGION_INTERFACE
 /**
  * @page page_iface_wl_region wl_region
  * @section page_iface_wl_region_desc Description
@@ -648,6 +707,9 @@
  * regions of a surface.
  */
 extern const struct wl_interface wl_region_interface;
+#endif
+#ifndef WL_SUBCOMPOSITOR_INTERFACE
+#define WL_SUBCOMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_subcompositor wl_subcompositor
  * @section page_iface_wl_subcompositor_desc Description
@@ -698,6 +760,9 @@
  * processing to dedicated overlay hardware when possible.
  */
 extern const struct wl_interface wl_subcompositor_interface;
+#endif
+#ifndef WL_SUBSURFACE_INTERFACE
+#define WL_SUBSURFACE_INTERFACE
 /**
  * @page page_iface_wl_subsurface wl_subsurface
  * @section page_iface_wl_subsurface_desc Description
@@ -808,6 +873,7 @@
  * unmapped.
  */
 extern const struct wl_interface wl_subsurface_interface;
+#endif
 
 #ifndef WL_DISPLAY_ERROR_ENUM
 #define WL_DISPLAY_ERROR_ENUM
diff --git a/tests/data/example-code.c b/tests/data/example-code.c
index bc03fe5..2055c88 100644
--- a/tests/data/example-code.c
+++ b/tests/data/example-code.c
@@ -48,7 +48,7 @@
 extern const struct wl_interface wl_surface_interface;
 extern const struct wl_interface wl_touch_interface;
 
-static const struct wl_interface *types[] = {
+static const struct wl_interface *wayland_types[] = {
 	NULL,
 	NULL,
 	NULL,
@@ -147,13 +147,13 @@
 };
 
 static const struct wl_message wl_display_requests[] = {
-	{ "sync", "n", types + 8 },
-	{ "get_registry", "n", types + 9 },
+	{ "sync", "n", wayland_types + 8 },
+	{ "get_registry", "n", wayland_types + 9 },
 };
 
 static const struct wl_message wl_display_events[] = {
-	{ "error", "ous", types + 0 },
-	{ "delete_id", "u", types + 0 },
+	{ "error", "ous", wayland_types + 0 },
+	{ "delete_id", "u", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_display_interface = {
@@ -163,12 +163,12 @@
 };
 
 static const struct wl_message wl_registry_requests[] = {
-	{ "bind", "usun", types + 0 },
+	{ "bind", "usun", wayland_types + 0 },
 };
 
 static const struct wl_message wl_registry_events[] = {
-	{ "global", "usu", types + 0 },
-	{ "global_remove", "u", types + 0 },
+	{ "global", "usu", wayland_types + 0 },
+	{ "global_remove", "u", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_registry_interface = {
@@ -178,7 +178,7 @@
 };
 
 static const struct wl_message wl_callback_events[] = {
-	{ "done", "u", types + 0 },
+	{ "done", "u", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_callback_interface = {
@@ -188,8 +188,8 @@
 };
 
 static const struct wl_message wl_compositor_requests[] = {
-	{ "create_surface", "n", types + 10 },
-	{ "create_region", "n", types + 11 },
+	{ "create_surface", "n", wayland_types + 10 },
+	{ "create_region", "n", wayland_types + 11 },
 };
 
 WL_EXPORT const struct wl_interface wl_compositor_interface = {
@@ -199,9 +199,9 @@
 };
 
 static const struct wl_message wl_shm_pool_requests[] = {
-	{ "create_buffer", "niiiiu", types + 12 },
-	{ "destroy", "", types + 0 },
-	{ "resize", "i", types + 0 },
+	{ "create_buffer", "niiiiu", wayland_types + 12 },
+	{ "destroy", "", wayland_types + 0 },
+	{ "resize", "i", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_shm_pool_interface = {
@@ -211,11 +211,11 @@
 };
 
 static const struct wl_message wl_shm_requests[] = {
-	{ "create_pool", "nhi", types + 18 },
+	{ "create_pool", "nhi", wayland_types + 18 },
 };
 
 static const struct wl_message wl_shm_events[] = {
-	{ "format", "u", types + 0 },
+	{ "format", "u", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_shm_interface = {
@@ -225,11 +225,11 @@
 };
 
 static const struct wl_message wl_buffer_requests[] = {
-	{ "destroy", "", types + 0 },
+	{ "destroy", "", wayland_types + 0 },
 };
 
 static const struct wl_message wl_buffer_events[] = {
-	{ "release", "", types + 0 },
+	{ "release", "", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_buffer_interface = {
@@ -239,17 +239,17 @@
 };
 
 static const struct wl_message wl_data_offer_requests[] = {
-	{ "accept", "u?s", types + 0 },
-	{ "receive", "sh", types + 0 },
-	{ "destroy", "", types + 0 },
-	{ "finish", "3", types + 0 },
-	{ "set_actions", "3uu", types + 0 },
+	{ "accept", "u?s", wayland_types + 0 },
+	{ "receive", "sh", wayland_types + 0 },
+	{ "destroy", "", wayland_types + 0 },
+	{ "finish", "3", wayland_types + 0 },
+	{ "set_actions", "3uu", wayland_types + 0 },
 };
 
 static const struct wl_message wl_data_offer_events[] = {
-	{ "offer", "s", types + 0 },
-	{ "source_actions", "3u", types + 0 },
-	{ "action", "3u", types + 0 },
+	{ "offer", "s", wayland_types + 0 },
+	{ "source_actions", "3u", wayland_types + 0 },
+	{ "action", "3u", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_data_offer_interface = {
@@ -259,18 +259,18 @@
 };
 
 static const struct wl_message wl_data_source_requests[] = {
-	{ "offer", "s", types + 0 },
-	{ "destroy", "", types + 0 },
-	{ "set_actions", "3u", types + 0 },
+	{ "offer", "s", wayland_types + 0 },
+	{ "destroy", "", wayland_types + 0 },
+	{ "set_actions", "3u", wayland_types + 0 },
 };
 
 static const struct wl_message wl_data_source_events[] = {
-	{ "target", "?s", types + 0 },
-	{ "send", "sh", types + 0 },
-	{ "cancelled", "", types + 0 },
-	{ "dnd_drop_performed", "3", types + 0 },
-	{ "dnd_finished", "3", types + 0 },
-	{ "action", "3u", types + 0 },
+	{ "target", "?s", wayland_types + 0 },
+	{ "send", "sh", wayland_types + 0 },
+	{ "cancelled", "", wayland_types + 0 },
+	{ "dnd_drop_performed", "3", wayland_types + 0 },
+	{ "dnd_finished", "3", wayland_types + 0 },
+	{ "action", "3u", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_data_source_interface = {
@@ -280,18 +280,18 @@
 };
 
 static const struct wl_message wl_data_device_requests[] = {
-	{ "start_drag", "?oo?ou", types + 21 },
-	{ "set_selection", "?ou", types + 25 },
-	{ "release", "2", types + 0 },
+	{ "start_drag", "?oo?ou", wayland_types + 21 },
+	{ "set_selection", "?ou", wayland_types + 25 },
+	{ "release", "2", wayland_types + 0 },
 };
 
 static const struct wl_message wl_data_device_events[] = {
-	{ "data_offer", "n", types + 27 },
-	{ "enter", "uoff?o", types + 28 },
-	{ "leave", "", types + 0 },
-	{ "motion", "uff", types + 0 },
-	{ "drop", "", types + 0 },
-	{ "selection", "?o", types + 33 },
+	{ "data_offer", "n", wayland_types + 27 },
+	{ "enter", "uoff?o", wayland_types + 28 },
+	{ "leave", "", wayland_types + 0 },
+	{ "motion", "uff", wayland_types + 0 },
+	{ "drop", "", wayland_types + 0 },
+	{ "selection", "?o", wayland_types + 33 },
 };
 
 WL_EXPORT const struct wl_interface wl_data_device_interface = {
@@ -301,8 +301,8 @@
 };
 
 static const struct wl_message wl_data_device_manager_requests[] = {
-	{ "create_data_source", "n", types + 34 },
-	{ "get_data_device", "no", types + 35 },
+	{ "create_data_source", "n", wayland_types + 34 },
+	{ "get_data_device", "no", wayland_types + 35 },
 };
 
 WL_EXPORT const struct wl_interface wl_data_device_manager_interface = {
@@ -312,7 +312,7 @@
 };
 
 static const struct wl_message wl_shell_requests[] = {
-	{ "get_shell_surface", "no", types + 37 },
+	{ "get_shell_surface", "no", wayland_types + 37 },
 };
 
 WL_EXPORT const struct wl_interface wl_shell_interface = {
@@ -322,22 +322,22 @@
 };
 
 static const struct wl_message wl_shell_surface_requests[] = {
-	{ "pong", "u", types + 0 },
-	{ "move", "ou", types + 39 },
-	{ "resize", "ouu", types + 41 },
-	{ "set_toplevel", "", types + 0 },
-	{ "set_transient", "oiiu", types + 44 },
-	{ "set_fullscreen", "uu?o", types + 48 },
-	{ "set_popup", "ouoiiu", types + 51 },
-	{ "set_maximized", "?o", types + 57 },
-	{ "set_title", "s", types + 0 },
-	{ "set_class", "s", types + 0 },
+	{ "pong", "u", wayland_types + 0 },
+	{ "move", "ou", wayland_types + 39 },
+	{ "resize", "ouu", wayland_types + 41 },
+	{ "set_toplevel", "", wayland_types + 0 },
+	{ "set_transient", "oiiu", wayland_types + 44 },
+	{ "set_fullscreen", "uu?o", wayland_types + 48 },
+	{ "set_popup", "ouoiiu", wayland_types + 51 },
+	{ "set_maximized", "?o", wayland_types + 57 },
+	{ "set_title", "s", wayland_types + 0 },
+	{ "set_class", "s", wayland_types + 0 },
 };
 
 static const struct wl_message wl_shell_surface_events[] = {
-	{ "ping", "u", types + 0 },
-	{ "configure", "uii", types + 0 },
-	{ "popup_done", "", types + 0 },
+	{ "ping", "u", wayland_types + 0 },
+	{ "configure", "uii", wayland_types + 0 },
+	{ "popup_done", "", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_shell_surface_interface = {
@@ -347,21 +347,21 @@
 };
 
 static const struct wl_message wl_surface_requests[] = {
-	{ "destroy", "", types + 0 },
-	{ "attach", "?oii", types + 58 },
-	{ "damage", "iiii", types + 0 },
-	{ "frame", "n", types + 61 },
-	{ "set_opaque_region", "?o", types + 62 },
-	{ "set_input_region", "?o", types + 63 },
-	{ "commit", "", types + 0 },
-	{ "set_buffer_transform", "2i", types + 0 },
-	{ "set_buffer_scale", "3i", types + 0 },
-	{ "damage_buffer", "4iiii", types + 0 },
+	{ "destroy", "", wayland_types + 0 },
+	{ "attach", "?oii", wayland_types + 58 },
+	{ "damage", "iiii", wayland_types + 0 },
+	{ "frame", "n", wayland_types + 61 },
+	{ "set_opaque_region", "?o", wayland_types + 62 },
+	{ "set_input_region", "?o", wayland_types + 63 },
+	{ "commit", "", wayland_types + 0 },
+	{ "set_buffer_transform", "2i", wayland_types + 0 },
+	{ "set_buffer_scale", "3i", wayland_types + 0 },
+	{ "damage_buffer", "4iiii", wayland_types + 0 },
 };
 
 static const struct wl_message wl_surface_events[] = {
-	{ "enter", "o", types + 64 },
-	{ "leave", "o", types + 65 },
+	{ "enter", "o", wayland_types + 64 },
+	{ "leave", "o", wayland_types + 65 },
 };
 
 WL_EXPORT const struct wl_interface wl_surface_interface = {
@@ -371,15 +371,15 @@
 };
 
 static const struct wl_message wl_seat_requests[] = {
-	{ "get_pointer", "n", types + 66 },
-	{ "get_keyboard", "n", types + 67 },
-	{ "get_touch", "n", types + 68 },
-	{ "release", "5", types + 0 },
+	{ "get_pointer", "n", wayland_types + 66 },
+	{ "get_keyboard", "n", wayland_types + 67 },
+	{ "get_touch", "n", wayland_types + 68 },
+	{ "release", "5", wayland_types + 0 },
 };
 
 static const struct wl_message wl_seat_events[] = {
-	{ "capabilities", "u", types + 0 },
-	{ "name", "2s", types + 0 },
+	{ "capabilities", "u", wayland_types + 0 },
+	{ "name", "2s", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_seat_interface = {
@@ -389,20 +389,20 @@
 };
 
 static const struct wl_message wl_pointer_requests[] = {
-	{ "set_cursor", "u?oii", types + 69 },
-	{ "release", "3", types + 0 },
+	{ "set_cursor", "u?oii", wayland_types + 69 },
+	{ "release", "3", wayland_types + 0 },
 };
 
 static const struct wl_message wl_pointer_events[] = {
-	{ "enter", "uoff", types + 73 },
-	{ "leave", "uo", types + 77 },
-	{ "motion", "uff", types + 0 },
-	{ "button", "uuuu", types + 0 },
-	{ "axis", "uuf", types + 0 },
-	{ "frame", "5", types + 0 },
-	{ "axis_source", "5u", types + 0 },
-	{ "axis_stop", "5uu", types + 0 },
-	{ "axis_discrete", "5ui", types + 0 },
+	{ "enter", "uoff", wayland_types + 73 },
+	{ "leave", "uo", wayland_types + 77 },
+	{ "motion", "uff", wayland_types + 0 },
+	{ "button", "uuuu", wayland_types + 0 },
+	{ "axis", "uuf", wayland_types + 0 },
+	{ "frame", "5", wayland_types + 0 },
+	{ "axis_source", "5u", wayland_types + 0 },
+	{ "axis_stop", "5uu", wayland_types + 0 },
+	{ "axis_discrete", "5ui", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_pointer_interface = {
@@ -412,16 +412,16 @@
 };
 
 static const struct wl_message wl_keyboard_requests[] = {
-	{ "release", "3", types + 0 },
+	{ "release", "3", wayland_types + 0 },
 };
 
 static const struct wl_message wl_keyboard_events[] = {
-	{ "keymap", "uhu", types + 0 },
-	{ "enter", "uoa", types + 79 },
-	{ "leave", "uo", types + 82 },
-	{ "key", "uuuu", types + 0 },
-	{ "modifiers", "uuuuu", types + 0 },
-	{ "repeat_info", "4ii", types + 0 },
+	{ "keymap", "uhu", wayland_types + 0 },
+	{ "enter", "uoa", wayland_types + 79 },
+	{ "leave", "uo", wayland_types + 82 },
+	{ "key", "uuuu", wayland_types + 0 },
+	{ "modifiers", "uuuuu", wayland_types + 0 },
+	{ "repeat_info", "4ii", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_keyboard_interface = {
@@ -431,17 +431,17 @@
 };
 
 static const struct wl_message wl_touch_requests[] = {
-	{ "release", "3", types + 0 },
+	{ "release", "3", wayland_types + 0 },
 };
 
 static const struct wl_message wl_touch_events[] = {
-	{ "down", "uuoiff", types + 84 },
-	{ "up", "uui", types + 0 },
-	{ "motion", "uiff", types + 0 },
-	{ "frame", "", types + 0 },
-	{ "cancel", "", types + 0 },
-	{ "shape", "6iff", types + 0 },
-	{ "orientation", "6if", types + 0 },
+	{ "down", "uuoiff", wayland_types + 84 },
+	{ "up", "uui", wayland_types + 0 },
+	{ "motion", "uiff", wayland_types + 0 },
+	{ "frame", "", wayland_types + 0 },
+	{ "cancel", "", wayland_types + 0 },
+	{ "shape", "6iff", wayland_types + 0 },
+	{ "orientation", "6if", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_touch_interface = {
@@ -451,14 +451,14 @@
 };
 
 static const struct wl_message wl_output_requests[] = {
-	{ "release", "3", types + 0 },
+	{ "release", "3", wayland_types + 0 },
 };
 
 static const struct wl_message wl_output_events[] = {
-	{ "geometry", "iiiiissi", types + 0 },
-	{ "mode", "uiii", types + 0 },
-	{ "done", "2", types + 0 },
-	{ "scale", "2i", types + 0 },
+	{ "geometry", "iiiiissi", wayland_types + 0 },
+	{ "mode", "uiii", wayland_types + 0 },
+	{ "done", "2", wayland_types + 0 },
+	{ "scale", "2i", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_output_interface = {
@@ -468,9 +468,9 @@
 };
 
 static const struct wl_message wl_region_requests[] = {
-	{ "destroy", "", types + 0 },
-	{ "add", "iiii", types + 0 },
-	{ "subtract", "iiii", types + 0 },
+	{ "destroy", "", wayland_types + 0 },
+	{ "add", "iiii", wayland_types + 0 },
+	{ "subtract", "iiii", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_region_interface = {
@@ -480,8 +480,8 @@
 };
 
 static const struct wl_message wl_subcompositor_requests[] = {
-	{ "destroy", "", types + 0 },
-	{ "get_subsurface", "noo", types + 90 },
+	{ "destroy", "", wayland_types + 0 },
+	{ "get_subsurface", "noo", wayland_types + 90 },
 };
 
 WL_EXPORT const struct wl_interface wl_subcompositor_interface = {
@@ -491,12 +491,12 @@
 };
 
 static const struct wl_message wl_subsurface_requests[] = {
-	{ "destroy", "", types + 0 },
-	{ "set_position", "ii", types + 0 },
-	{ "place_above", "o", types + 93 },
-	{ "place_below", "o", types + 94 },
-	{ "set_sync", "", types + 0 },
-	{ "set_desync", "", types + 0 },
+	{ "destroy", "", wayland_types + 0 },
+	{ "set_position", "ii", wayland_types + 0 },
+	{ "place_above", "o", wayland_types + 93 },
+	{ "place_below", "o", wayland_types + 94 },
+	{ "set_sync", "", wayland_types + 0 },
+	{ "set_desync", "", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_subsurface_interface = {
diff --git a/tests/data/example-server.h b/tests/data/example-server.h
index adfc973..3311c5d 100644
--- a/tests/data/example-server.h
+++ b/tests/data/example-server.h
@@ -91,6 +91,8 @@
 struct wl_surface;
 struct wl_touch;
 
+#ifndef WL_DISPLAY_INTERFACE
+#define WL_DISPLAY_INTERFACE
 /**
  * @page page_iface_wl_display wl_display
  * @section page_iface_wl_display_desc Description
@@ -107,6 +109,9 @@
  * is used for internal Wayland protocol features.
  */
 extern const struct wl_interface wl_display_interface;
+#endif
+#ifndef WL_REGISTRY_INTERFACE
+#define WL_REGISTRY_INTERFACE
 /**
  * @page page_iface_wl_registry wl_registry
  * @section page_iface_wl_registry_desc Description
@@ -159,6 +164,9 @@
  * the object.
  */
 extern const struct wl_interface wl_registry_interface;
+#endif
+#ifndef WL_CALLBACK_INTERFACE
+#define WL_CALLBACK_INTERFACE
 /**
  * @page page_iface_wl_callback wl_callback
  * @section page_iface_wl_callback_desc Description
@@ -175,6 +183,9 @@
  * the related request is done.
  */
 extern const struct wl_interface wl_callback_interface;
+#endif
+#ifndef WL_COMPOSITOR_INTERFACE
+#define WL_COMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_compositor wl_compositor
  * @section page_iface_wl_compositor_desc Description
@@ -193,6 +204,9 @@
  * surfaces into one displayable output.
  */
 extern const struct wl_interface wl_compositor_interface;
+#endif
+#ifndef WL_SHM_POOL_INTERFACE
+#define WL_SHM_POOL_INTERFACE
 /**
  * @page page_iface_wl_shm_pool wl_shm_pool
  * @section page_iface_wl_shm_pool_desc Description
@@ -219,6 +233,9 @@
  * a surface or for many small buffers.
  */
 extern const struct wl_interface wl_shm_pool_interface;
+#endif
+#ifndef WL_SHM_INTERFACE
+#define WL_SHM_INTERFACE
 /**
  * @page page_iface_wl_shm wl_shm
  * @section page_iface_wl_shm_desc Description
@@ -249,6 +266,9 @@
  * that can be used for buffers.
  */
 extern const struct wl_interface wl_shm_interface;
+#endif
+#ifndef WL_BUFFER_INTERFACE
+#define WL_BUFFER_INTERFACE
 /**
  * @page page_iface_wl_buffer wl_buffer
  * @section page_iface_wl_buffer_desc Description
@@ -271,6 +291,9 @@
  * updates the contents is defined by the buffer factory interface.
  */
 extern const struct wl_interface wl_buffer_interface;
+#endif
+#ifndef WL_DATA_OFFER_INTERFACE
+#define WL_DATA_OFFER_INTERFACE
 /**
  * @page page_iface_wl_data_offer wl_data_offer
  * @section page_iface_wl_data_offer_desc Description
@@ -295,6 +318,9 @@
  * data directly from the source client.
  */
 extern const struct wl_interface wl_data_offer_interface;
+#endif
+#ifndef WL_DATA_SOURCE_INTERFACE
+#define WL_DATA_SOURCE_INTERFACE
 /**
  * @page page_iface_wl_data_source wl_data_source
  * @section page_iface_wl_data_source_desc Description
@@ -315,6 +341,9 @@
  * to requests to transfer the data.
  */
 extern const struct wl_interface wl_data_source_interface;
+#endif
+#ifndef WL_DATA_DEVICE_INTERFACE
+#define WL_DATA_DEVICE_INTERFACE
 /**
  * @page page_iface_wl_data_device wl_data_device
  * @section page_iface_wl_data_device_desc Description
@@ -337,6 +366,9 @@
  * mechanisms such as copy-and-paste and drag-and-drop.
  */
 extern const struct wl_interface wl_data_device_interface;
+#endif
+#ifndef WL_DATA_DEVICE_MANAGER_INTERFACE
+#define WL_DATA_DEVICE_MANAGER_INTERFACE
 /**
  * @page page_iface_wl_data_device_manager wl_data_device_manager
  * @section page_iface_wl_data_device_manager_desc Description
@@ -369,6 +401,9 @@
  * wl_data_offer.accept and wl_data_offer.finish for details.
  */
 extern const struct wl_interface wl_data_device_manager_interface;
+#endif
+#ifndef WL_SHELL_INTERFACE
+#define WL_SHELL_INTERFACE
 /**
  * @page page_iface_wl_shell wl_shell
  * @section page_iface_wl_shell_desc Description
@@ -391,6 +426,9 @@
  * a basic surface.
  */
 extern const struct wl_interface wl_shell_interface;
+#endif
+#ifndef WL_SHELL_SURFACE_INTERFACE
+#define WL_SHELL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_shell_surface wl_shell_surface
  * @section page_iface_wl_shell_surface_desc Description
@@ -425,6 +463,9 @@
  * the wl_surface object.
  */
 extern const struct wl_interface wl_shell_surface_interface;
+#endif
+#ifndef WL_SURFACE_INTERFACE
+#define WL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_surface wl_surface
  * @section page_iface_wl_surface_desc Description
@@ -515,6 +556,9 @@
  * switching is not allowed).
  */
 extern const struct wl_interface wl_surface_interface;
+#endif
+#ifndef WL_SEAT_INTERFACE
+#define WL_SEAT_INTERFACE
 /**
  * @page page_iface_wl_seat wl_seat
  * @section page_iface_wl_seat_desc Description
@@ -535,6 +579,9 @@
  * maintains a keyboard focus and a pointer focus.
  */
 extern const struct wl_interface wl_seat_interface;
+#endif
+#ifndef WL_POINTER_INTERFACE
+#define WL_POINTER_INTERFACE
 /**
  * @page page_iface_wl_pointer wl_pointer
  * @section page_iface_wl_pointer_desc Description
@@ -563,6 +610,9 @@
  * and scrolling.
  */
 extern const struct wl_interface wl_pointer_interface;
+#endif
+#ifndef WL_KEYBOARD_INTERFACE
+#define WL_KEYBOARD_INTERFACE
 /**
  * @page page_iface_wl_keyboard wl_keyboard
  * @section page_iface_wl_keyboard_desc Description
@@ -579,6 +629,9 @@
  * associated with a seat.
  */
 extern const struct wl_interface wl_keyboard_interface;
+#endif
+#ifndef WL_TOUCH_INTERFACE
+#define WL_TOUCH_INTERFACE
 /**
  * @page page_iface_wl_touch wl_touch
  * @section page_iface_wl_touch_desc Description
@@ -607,6 +660,9 @@
  * contact point can be identified by the ID of the sequence.
  */
 extern const struct wl_interface wl_touch_interface;
+#endif
+#ifndef WL_OUTPUT_INTERFACE
+#define WL_OUTPUT_INTERFACE
 /**
  * @page page_iface_wl_output wl_output
  * @section page_iface_wl_output_desc Description
@@ -631,6 +687,9 @@
  * as global during start up, or when a monitor is hotplugged.
  */
 extern const struct wl_interface wl_output_interface;
+#endif
+#ifndef WL_REGION_INTERFACE
+#define WL_REGION_INTERFACE
 /**
  * @page page_iface_wl_region wl_region
  * @section page_iface_wl_region_desc Description
@@ -651,6 +710,9 @@
  * regions of a surface.
  */
 extern const struct wl_interface wl_region_interface;
+#endif
+#ifndef WL_SUBCOMPOSITOR_INTERFACE
+#define WL_SUBCOMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_subcompositor wl_subcompositor
  * @section page_iface_wl_subcompositor_desc Description
@@ -701,6 +763,9 @@
  * processing to dedicated overlay hardware when possible.
  */
 extern const struct wl_interface wl_subcompositor_interface;
+#endif
+#ifndef WL_SUBSURFACE_INTERFACE
+#define WL_SUBSURFACE_INTERFACE
 /**
  * @page page_iface_wl_subsurface wl_subsurface
  * @section page_iface_wl_subsurface_desc Description
@@ -811,6 +876,7 @@
  * unmapped.
  */
 extern const struct wl_interface wl_subsurface_interface;
+#endif
 
 #ifndef WL_DISPLAY_ERROR_ENUM
 #define WL_DISPLAY_ERROR_ENUM
diff --git a/tests/data/small-client-core.h b/tests/data/small-client-core.h
index c85cca6..d424757 100644
--- a/tests/data/small-client-core.h
+++ b/tests/data/small-client-core.h
@@ -46,6 +46,8 @@
 struct intf_A;
 struct intf_not_here;
 
+#ifndef INTF_A_INTERFACE
+#define INTF_A_INTERFACE
 /**
  * @page page_iface_intf_A intf_A
  * @section page_iface_intf_A_desc Description
@@ -60,6 +62,7 @@
  * A useless example trying to tickle the scanner.
  */
 extern const struct wl_interface intf_A_interface;
+#endif
 
 #ifndef INTF_A_FOO_ENUM
 #define INTF_A_FOO_ENUM
diff --git a/tests/data/small-client.h b/tests/data/small-client.h
index 884346d..2a1f961 100644
--- a/tests/data/small-client.h
+++ b/tests/data/small-client.h
@@ -46,6 +46,8 @@
 struct intf_A;
 struct intf_not_here;
 
+#ifndef INTF_A_INTERFACE
+#define INTF_A_INTERFACE
 /**
  * @page page_iface_intf_A intf_A
  * @section page_iface_intf_A_desc Description
@@ -60,6 +62,7 @@
  * A useless example trying to tickle the scanner.
  */
 extern const struct wl_interface intf_A_interface;
+#endif
 
 #ifndef INTF_A_FOO_ENUM
 #define INTF_A_FOO_ENUM
diff --git a/tests/data/small-code-core.c b/tests/data/small-code-core.c
index 28a00ab..bd6d33d 100644
--- a/tests/data/small-code-core.c
+++ b/tests/data/small-code-core.c
@@ -32,7 +32,7 @@
 extern const struct wl_interface another_intf_interface;
 extern const struct wl_interface intf_not_here_interface;
 
-static const struct wl_interface *types[] = {
+static const struct wl_interface *small_test_types[] = {
 	NULL,
 	&intf_not_here_interface,
 	NULL,
@@ -44,13 +44,13 @@
 };
 
 static const struct wl_message intf_A_requests[] = {
-	{ "rq1", "sun", types + 0 },
-	{ "rq2", "nsiufho", types + 1 },
-	{ "destroy", "", types + 0 },
+	{ "rq1", "sun", small_test_types + 0 },
+	{ "rq2", "nsiufho", small_test_types + 1 },
+	{ "destroy", "", small_test_types + 0 },
 };
 
 static const struct wl_message intf_A_events[] = {
-	{ "hey", "", types + 0 },
+	{ "hey", "", small_test_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface intf_A_interface = {
diff --git a/tests/data/small-code.c b/tests/data/small-code.c
index 28a00ab..bd6d33d 100644
--- a/tests/data/small-code.c
+++ b/tests/data/small-code.c
@@ -32,7 +32,7 @@
 extern const struct wl_interface another_intf_interface;
 extern const struct wl_interface intf_not_here_interface;
 
-static const struct wl_interface *types[] = {
+static const struct wl_interface *small_test_types[] = {
 	NULL,
 	&intf_not_here_interface,
 	NULL,
@@ -44,13 +44,13 @@
 };
 
 static const struct wl_message intf_A_requests[] = {
-	{ "rq1", "sun", types + 0 },
-	{ "rq2", "nsiufho", types + 1 },
-	{ "destroy", "", types + 0 },
+	{ "rq1", "sun", small_test_types + 0 },
+	{ "rq2", "nsiufho", small_test_types + 1 },
+	{ "destroy", "", small_test_types + 0 },
 };
 
 static const struct wl_message intf_A_events[] = {
-	{ "hey", "", types + 0 },
+	{ "hey", "", small_test_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface intf_A_interface = {
diff --git a/tests/data/small-private-code.c b/tests/data/small-private-code.c
index 5e0bc88..fe035ff 100644
--- a/tests/data/small-private-code.c
+++ b/tests/data/small-private-code.c
@@ -42,7 +42,7 @@
 extern const struct wl_interface another_intf_interface;
 extern const struct wl_interface intf_not_here_interface;
 
-static const struct wl_interface *types[] = {
+static const struct wl_interface *small_test_types[] = {
 	NULL,
 	&intf_not_here_interface,
 	NULL,
@@ -54,13 +54,13 @@
 };
 
 static const struct wl_message intf_A_requests[] = {
-	{ "rq1", "sun", types + 0 },
-	{ "rq2", "nsiufho", types + 1 },
-	{ "destroy", "", types + 0 },
+	{ "rq1", "sun", small_test_types + 0 },
+	{ "rq2", "nsiufho", small_test_types + 1 },
+	{ "destroy", "", small_test_types + 0 },
 };
 
 static const struct wl_message intf_A_events[] = {
-	{ "hey", "", types + 0 },
+	{ "hey", "", small_test_types + 0 },
 };
 
 WL_PRIVATE const struct wl_interface intf_A_interface = {
diff --git a/tests/data/small-server-core.h b/tests/data/small-server-core.h
index 6dd2d05..f0fd1f9 100644
--- a/tests/data/small-server-core.h
+++ b/tests/data/small-server-core.h
@@ -49,6 +49,8 @@
 struct intf_A;
 struct intf_not_here;
 
+#ifndef INTF_A_INTERFACE
+#define INTF_A_INTERFACE
 /**
  * @page page_iface_intf_A intf_A
  * @section page_iface_intf_A_desc Description
@@ -63,6 +65,7 @@
  * A useless example trying to tickle the scanner.
  */
 extern const struct wl_interface intf_A_interface;
+#endif
 
 #ifndef INTF_A_FOO_ENUM
 #define INTF_A_FOO_ENUM
diff --git a/tests/data/small-server.h b/tests/data/small-server.h
index 4763f5b..22b8113 100644
--- a/tests/data/small-server.h
+++ b/tests/data/small-server.h
@@ -49,6 +49,8 @@
 struct intf_A;
 struct intf_not_here;
 
+#ifndef INTF_A_INTERFACE
+#define INTF_A_INTERFACE
 /**
  * @page page_iface_intf_A intf_A
  * @section page_iface_intf_A_desc Description
@@ -63,6 +65,7 @@
  * A useless example trying to tickle the scanner.
  */
 extern const struct wl_interface intf_A_interface;
+#endif
 
 #ifndef INTF_A_FOO_ENUM
 #define INTF_A_FOO_ENUM
diff --git a/tests/data/small.xml b/tests/data/small.xml
index a6e62ad..832ed0e 100644
--- a/tests/data/small.xml
+++ b/tests/data/small.xml
@@ -50,9 +50,9 @@
     <event name="hey"/>
 
     <enum name="foo">
-        <entry name="first" value="0" summary="this is the first"/>
-        <entry name="second" value="1" summary="this is the second"/>
-        <entry name="third" value="2" since="2" summary="this is the third"/>
+	<entry name="first" value="0" summary="this is the first"/>
+	<entry name="second" value="1" summary="this is the second"/>
+	<entry name="third" value="2" since="2" summary="this is the third"/>
     </enum>
   </interface>
 </protocol>
diff --git a/tests/display-test.c b/tests/display-test.c
index 9b49a0e..3db7c95 100644
--- a/tests/display-test.c
+++ b/tests/display-test.c
@@ -60,7 +60,7 @@
 {
 	struct display_destroy_listener *listener;
 
-	listener = container_of(l, struct display_destroy_listener, listener);
+	listener = wl_container_of(l, listener, listener);
 	listener->done = 1;
 }
 
@@ -420,6 +420,46 @@
 }
 
 static void
+post_implementation_error_main(void)
+{
+	struct client *c = client_connect();
+	struct wl_seat *seat = client_get_seat(c);
+	uint32_t object_id, protocol_error;
+	const struct wl_interface *interface;
+
+	assert(stop_display(c, 1) == -1);
+	int err = wl_display_get_error(c->wl_display);
+	fprintf(stderr, "Err is %i\n", err);
+	assert(err == EPROTO);
+	protocol_error = wl_display_get_protocol_error(c->wl_display,
+						       &interface,
+						       &object_id);
+	assert(protocol_error == WL_DISPLAY_ERROR_IMPLEMENTATION);
+	assert(interface == &wl_display_interface);
+
+	wl_proxy_destroy((struct wl_proxy *) seat);
+	client_disconnect_nocheck(c);
+}
+
+TEST(post_internal_error_tst)
+{
+	struct display *d = display_create();
+	struct client_info *cl;
+
+	wl_global_create(d->wl_display, &wl_seat_interface,
+			 1, d, bind_seat);
+
+	cl = client_create_noarg(d, post_implementation_error_main);
+	display_run(d);
+
+	wl_client_post_implementation_error(cl->wl_client, "Error %i", 20);
+
+	display_resume(d);
+
+	display_destroy(d);
+}
+
+static void
 register_reading(struct wl_display *display)
 {
 	while(wl_display_prepare_read(display) != 0 && errno == EAGAIN)
@@ -1315,3 +1355,277 @@
 
 	display_destroy(d);
 }
+
+
+static void
+registry_bind_interface_mismatch_handle_global(void *data,
+					       struct wl_registry *registry,
+					       uint32_t id, const char *intf,
+					       uint32_t ver)
+{
+	uint32_t *seat_id_ptr = data;
+
+	if (strcmp(intf, wl_seat_interface.name) == 0) {
+		*seat_id_ptr = id;
+	}
+}
+
+static const struct wl_registry_listener bind_interface_mismatch_registry_listener = {
+	registry_bind_interface_mismatch_handle_global,
+	NULL
+};
+
+static void
+registry_bind_interface_mismatch_client(void *data)
+{
+	struct client *c = client_connect();
+	struct wl_registry *registry;
+	uint32_t seat_id = 0;
+	void *ptr;
+	int ret;
+
+	registry = wl_display_get_registry(c->wl_display);
+	wl_registry_add_listener(registry,
+				 &bind_interface_mismatch_registry_listener,
+				 &seat_id);
+
+	ret = wl_display_roundtrip(c->wl_display);
+	assert(ret >= 0);
+	assert(seat_id != 0);
+
+	/* Bind with a different interface */
+	ptr = wl_registry_bind(registry, seat_id, &wl_output_interface, 1);
+	ret = wl_display_roundtrip(c->wl_display);
+	assert(ret < 0);
+	check_bind_error(c);
+
+	wl_proxy_destroy((struct wl_proxy *) ptr);
+	wl_registry_destroy(registry);
+
+	client_disconnect_nocheck(c);
+}
+
+TEST(registry_bind_interface_mismatch)
+{
+	struct display *d;
+	struct wl_global *seat_global;
+
+	d = display_create();
+
+	seat_global = wl_global_create(d->wl_display, &wl_seat_interface,
+				       1, NULL, NULL);
+
+	client_create_noarg(d, registry_bind_interface_mismatch_client);
+	display_run(d);
+
+	wl_global_destroy(seat_global);
+
+	display_destroy(d);
+}
+
+static void
+send_overflow_client(void *data)
+{
+	struct client *c = client_connect();
+	int i, err = 0;
+	int *pipes = data;
+	char tmp = '\0';
+	int sock, optval = 16384;
+
+	/* Limit the send buffer size for the display socket to guarantee
+	 * that the test will cause an overflow. */
+	sock = wl_display_get_fd(c->wl_display);
+	assert(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == 0);
+
+	/* Request to break out of 'display_run' in the main process */
+	assert(stop_display(c, 1) >= 0);
+
+	/* On Linux, the actual socket data + metadata space is twice `optval`;
+	 * since each noop request requires 8 bytes, the buffer should overflow
+	 * within <=4096 iterations. */
+	for (i = 0; i < 1000000; i++) {
+		noop_request(c);
+		err = wl_display_get_error(c->wl_display);
+		if (err)
+			break;
+	}
+
+	/* Do not close the pipe file descriptors afterwards, because the leak
+	 * check verifies that the initial/final FD counts are the same */
+	assert(write(pipes[1], &tmp, sizeof(tmp)) == (ssize_t)sizeof(tmp));
+
+	/* Expect an error */
+	fprintf(stderr, "Send loop failed on try %d, err = %d, %s\n", i, err, strerror(err));
+	assert(err == EAGAIN);
+
+	client_disconnect_nocheck(c);
+}
+
+TEST(send_overflow_disconnection)
+{
+	struct display *d;
+	char tmp;
+	int rpipe[2];
+	ssize_t ret;
+
+	assert(pipe(rpipe) != -1);
+
+	d = display_create();
+
+	(void) client_create(d, send_overflow_client, &rpipe);
+
+	/* Close write end of the pipe, so that the later read() call gets
+	 * interrupted if the client dies */
+	close(rpipe[1]);
+
+	/* Run the display until the client sends a `stop_display`, then
+	 * send a resume message but don't actually look at new messages */
+	display_run(d);
+	display_post_resume_events(d);
+	wl_display_flush_clients(d->wl_display);
+
+	/* Wait until all noop requests have been sent (read returns 1), or
+	 * until client process aborts (read returns 0) */
+	do {
+		ret = read(rpipe[0], &tmp, sizeof(tmp));
+	} while (ret == -1 && errno == EINTR);
+	assert(ret != -1);
+	close(rpipe[0]);
+
+	/* For a clean shutdown */
+	display_run(d);
+
+	display_destroy(d);
+}
+
+static void
+registry_global_remove_before_handle_global(void *data,
+					    struct wl_registry *registry,
+					    uint32_t id, const char *intf,
+					    uint32_t ver)
+{
+	uint32_t *id_ptr = data;
+
+	if (strcmp(intf, wl_seat_interface.name) == 0) {
+		assert(*id_ptr == 0);
+		*id_ptr = id;
+	}
+}
+
+static void
+registry_global_remove_before_handle_global_remove(void *data,
+						   struct wl_registry *registry,
+						   uint32_t id)
+{
+	uint32_t *id_ptr = data;
+
+	if (*id_ptr == id) {
+		*id_ptr = 0;
+	}
+}
+
+/* This listener expects a uint32_t user data pointer, sets it to the wl_seat
+ * global ID when receiving a "global" event, and sets it to zero when receiving
+ * a "global_remove" event. */
+static const struct wl_registry_listener global_remove_before_registry_listener = {
+	registry_global_remove_before_handle_global,
+	registry_global_remove_before_handle_global_remove,
+};
+
+static void
+global_remove_before_client(void *data)
+{
+	struct client *c = client_connect();
+	struct wl_registry *registry;
+	uint32_t global_id = 0, saved_global_id;
+	struct wl_seat *seat;
+	int ret;
+
+	registry = wl_display_get_registry(c->wl_display);
+	wl_registry_add_listener(registry,
+				 &global_remove_before_registry_listener,
+				 &global_id);
+
+	ret = wl_display_roundtrip(c->wl_display);
+	assert(ret >= 0);
+	assert(global_id != 0);
+	saved_global_id = global_id;
+
+	/* Wait for the compositor to remove the global */
+	assert(stop_display(c, 1) >= 0);
+
+	/* Check binding still works after the global has been removed. Also
+	 * check we get the global_remove event. */
+	seat = wl_registry_bind(registry, saved_global_id, &wl_seat_interface, 1);
+	ret = wl_display_roundtrip(c->wl_display);
+	assert(ret >= 0);
+	assert(global_id == 0);
+
+	wl_seat_destroy(seat);
+	wl_registry_destroy(registry);
+
+	client_disconnect(c);
+}
+
+static void
+registry_global_remove_after_handle_global(void *data,
+					   struct wl_registry *registry,
+					   uint32_t id, const char *intf,
+					   uint32_t ver)
+{
+	/* Make sure the global isn't advertised anymore after being removed */
+	assert(strcmp(intf, wl_seat_interface.name) != 0);
+}
+
+static const struct wl_registry_listener global_remove_after_registry_listener = {
+	registry_global_remove_after_handle_global,
+	NULL,
+};
+
+static void
+global_remove_after_client(void *data)
+{
+	struct client *c = client_connect();
+	struct wl_registry *registry;
+	uint32_t global_id = 0;
+	int ret;
+
+	registry = wl_display_get_registry(c->wl_display);
+	wl_registry_add_listener(registry,
+				 &global_remove_after_registry_listener,
+				 &global_id);
+
+	ret = wl_display_roundtrip(c->wl_display);
+	assert(ret >= 0);
+
+	wl_registry_destroy(registry);
+
+	client_disconnect(c);
+}
+
+TEST(global_remove)
+{
+	struct display *d;
+	struct wl_global *global;
+
+	d = display_create();
+
+	global = wl_global_create(d->wl_display, &wl_seat_interface,
+				  1, d, bind_seat);
+
+	/* Create a client before removing the global */
+	client_create_noarg(d, global_remove_before_client);
+
+	display_run(d);
+
+	wl_global_remove(global);
+
+	/* Create another client after removing the global */
+	client_create_noarg(d, global_remove_after_client);
+
+	display_resume(d);
+
+	wl_global_destroy(global);
+
+	display_destroy(d);
+}
diff --git a/tests/event-loop-test.c b/tests/event-loop-test.c
index 33566b4..cbeaf8e 100644
--- a/tests/event-loop-test.c
+++ b/tests/event-loop-test.c
@@ -29,6 +29,7 @@
 #include <assert.h>
 #include <unistd.h>
 #include <signal.h>
+#include <string.h>
 #include <sys/time.h>
 
 #include "wayland-private.h"
@@ -229,18 +230,33 @@
 TEST(event_loop_timer)
 {
 	struct wl_event_loop *loop = wl_event_loop_create();
-	struct wl_event_source *source;
+	struct wl_event_source *source1, *source2;
 	int got_it = 0;
 
-	source = wl_event_loop_add_timer(loop, timer_callback, &got_it);
-	assert(source);
-	wl_event_source_timer_update(source, 10);
-	wl_event_loop_dispatch(loop, 0);
-	assert(!got_it);
-	wl_event_loop_dispatch(loop, 20);
-	assert(got_it == 1);
+	source1 = wl_event_loop_add_timer(loop, timer_callback, &got_it);
+	assert(source1);
+	wl_event_source_timer_update(source1, 20);
 
-	wl_event_source_remove(source);
+	source2 = wl_event_loop_add_timer(loop, timer_callback, &got_it);
+	assert(source2);
+	wl_event_source_timer_update(source2, 100);
+
+	/* Check that the timer marked for 20 msec from now fires within 30
+	 * msec, and that the timer marked for 100 msec is expected to fire
+	 * within an additional 90 msec. (Some extra wait time is provided to
+	 * account for reasonable code execution / thread preemption delays.) */
+
+	wl_event_loop_dispatch(loop, 0);
+	assert(got_it == 0);
+	wl_event_loop_dispatch(loop, 30);
+	assert(got_it == 1);
+	wl_event_loop_dispatch(loop, 0);
+	assert(got_it == 1);
+	wl_event_loop_dispatch(loop, 90);
+	assert(got_it == 2);
+
+	wl_event_source_remove(source1);
+	wl_event_source_remove(source2);
 	wl_event_loop_destroy(loop);
 }
 
@@ -330,6 +346,138 @@
 	wl_event_loop_destroy(loop);
 }
 
+struct timer_order_data {
+	struct wl_event_source *source;
+	int *last_number;
+	int number;
+};
+
+static int
+timer_order_callback(void *data)
+{
+	struct timer_order_data *tod = data;
+
+	/* Check that the timers have the correct sequence */
+	assert(tod->number == *tod->last_number + 2);
+	*tod->last_number = tod->number;
+	return 0;
+}
+
+TEST(event_loop_timer_order)
+{
+	struct wl_event_loop *loop = wl_event_loop_create();
+	struct timer_order_data order[20];
+	int i, j;
+	int last = -1;
+
+	/* Configure a set of timers so that only timers 1, 3, 5, ..., 19
+	 * (in that order) will be dispatched when the event loop is run */
+
+	for (i = 0; i < 20; i++) {
+		order[i].number = i;
+		order[i].last_number = &last;
+		order[i].source =
+			wl_event_loop_add_timer(loop, timer_order_callback,
+						&order[i]);
+		assert(order[i].source);
+		assert(wl_event_source_timer_update(order[i].source, 10) == 0);
+	}
+
+	for (i = 0; i < 20; i++) {
+		/* Permute the order in which timers are updated, so as to
+		 * more exhaustively test the underlying priority queue code */
+		j = ((i + 3) * 17) % 20;
+		assert(wl_event_source_timer_update(order[j].source, j) == 0);
+	}
+	for (i = 0; i < 20; i += 2) {
+		assert(wl_event_source_timer_update(order[i].source, 0) == 0);
+	}
+
+	/* Wait until all timers are due */
+	usleep(MSEC_TO_USEC(21));
+	wl_event_loop_dispatch(loop, 0);
+	assert(last == 19);
+
+	for (i = 0; i < 20; i++) {
+		wl_event_source_remove(order[i].source);
+	}
+	wl_event_loop_destroy(loop);
+}
+
+struct timer_cancel_context {
+	struct wl_event_source *timers[4];
+	struct timer_cancel_context *back_refs[4];
+	int order[4];
+	int called, first;
+};
+
+static int
+timer_cancel_callback(void *data) {
+	struct timer_cancel_context **context_ref = data;
+	struct timer_cancel_context *context = *context_ref;
+	int i = (int)(context_ref - context->back_refs);
+
+	context->called++;
+	context->order[i] = context->called;
+
+	if (context->called == 1) {
+		context->first = i;
+		/* Removing a timer always prevents its callback from
+		 * being called ... */
+		wl_event_source_remove(context->timers[(i + 1) % 4]);
+		/* ... but disarming or rescheduling a timer does not,
+		 * (in the case where the modified timers had already expired
+		 * as of when `wl_event_loop_dispatch` was called.) */
+		assert(wl_event_source_timer_update(context->timers[(i + 2) % 4],
+						    0) == 0);
+		assert(wl_event_source_timer_update(context->timers[(i + 3) % 4],
+						    2000000000) == 0);
+	}
+
+	return 0;
+}
+
+TEST(event_loop_timer_cancellation)
+{
+	struct wl_event_loop *loop = wl_event_loop_create();
+	struct timer_cancel_context context;
+	int i;
+
+	memset(&context, 0, sizeof(context));
+
+	/* Test that when multiple timers are dispatched in a single call
+	 * of `wl_event_loop_dispatch`, that having some timers run code
+	 * to modify the other timers only actually prevents the other timers
+	 * from running their callbacks when the those timers are removed, not
+	 * when they are disarmed or rescheduled. */
+
+	for (i = 0; i < 4; i++) {
+		context.back_refs[i] = &context;
+		context.timers[i] =
+			wl_event_loop_add_timer(loop, timer_cancel_callback,
+						&context.back_refs[i]);
+		assert(context.timers[i]);
+
+		assert(wl_event_source_timer_update(context.timers[i], 1) == 0);
+	}
+
+	usleep(MSEC_TO_USEC(2));
+	assert(wl_event_loop_dispatch(loop, 0) == 0);
+
+	/* Tracking which timer was first makes this test independent of the
+	 * actual timer dispatch order, which is not guaranteed by the docs */
+	assert(context.order[context.first] == 1);
+	assert(context.order[(context.first + 1) % 4] == 0);
+	assert(context.order[(context.first + 2) % 4] > 1);
+	assert(context.order[(context.first + 3) % 4] > 1);
+
+	wl_event_source_remove(context.timers[context.first]);
+	wl_event_source_remove(context.timers[(context.first + 2) % 4]);
+	wl_event_source_remove(context.timers[(context.first + 3) % 4]);
+
+	wl_event_loop_destroy(loop);
+}
+
 struct event_loop_destroy_listener {
 	struct wl_listener listener;
 	int done;
@@ -339,7 +487,7 @@
 event_loop_destroy_notify(struct wl_listener *l, void *data)
 {
 	struct event_loop_destroy_listener *listener =
-		container_of(l, struct event_loop_destroy_listener, listener);
+		wl_container_of(l, listener, listener);
 
 	listener->done = 1;
 }
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 0000000..224f48d
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,165 @@
+test_runner = static_library(
+	'test-runner',
+	sources: [
+		'test-runner.c',
+		'test-helpers.c',
+		'test-compositor.c'
+	],
+	include_directories: [ root_inc, src_inc ],
+	dependencies: [
+		cc.find_library('dl', required: false),
+		dependency('threads'),
+		ffi_dep,
+		wayland_util_dep,
+		wayland_private_dep,
+		wayland_client_dep,
+		wayland_server_dep
+	]
+)
+
+test_runner_dep = declare_dependency(
+	link_with: test_runner,
+	include_directories: [ src_inc ],
+	dependencies: [
+		dependency('threads'),
+		cc.find_library('dl', required: false)
+	]
+)
+
+tests_protocol_xml = files('../protocol/tests.xml')
+
+tests_server_protocol_h = custom_target(
+	'test server protocol header',
+	command: [ wayland_scanner_for_build, '-s', 'server-header', '@INPUT@', '@OUTPUT@' ],
+	input: tests_protocol_xml,
+	output: 'tests-server-protocol.h'
+)
+
+tests_client_protocol_c = custom_target(
+	'test client protocol header',
+	command: [ wayland_scanner_for_build, '-s', 'client-header', '@INPUT@', '@OUTPUT@' ],
+	input: tests_protocol_xml,
+	output: 'tests-client-protocol.h'
+)
+
+tests_protocol_c = custom_target(
+	'test protocol source',
+	command: [ wayland_scanner_for_build, '-s', 'public-code', '@INPUT@', '@OUTPUT@' ],
+	input: tests_protocol_xml,
+	output: 'tests-protocol.c'
+)
+
+benchmark(
+	'fixed-benchmark',
+	executable(
+		'fixed-benchmark',
+		'fixed-benchmark.c',
+		dependencies: test_runner_dep
+	)
+)
+
+executable(
+	'exec-fd-leak-checker',
+	'exec-fd-leak-checker.c',
+	dependencies: test_runner_dep
+)
+
+test(
+	'cpp-compile-test',
+	executable(
+		'cpp-compile-test',
+		'cpp-compile-test.cpp',
+		wayland_server_protocol_h,
+		include_directories: src_inc
+	)
+)
+
+sed_path = find_program('sed').path()
+
+if get_option('scanner')
+	test(
+		'scanner-test',
+		find_program('scanner-test.sh'),
+		env: [
+			'TEST_DATA_DIR=@0@/data'.format(meson.current_source_dir()),
+			'TEST_OUTPUT_DIR=@0@/output'.format(meson.current_build_dir()),
+			'SED=@0@'.format(sed_path),
+			'WAYLAND_SCANNER=@0@'.format(wayland_scanner.full_path()),
+		],
+	)
+endif
+
+tests = {
+	'array-test': [],
+	'client-test': [ wayland_server_protocol_h ],
+	'display-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+		tests_server_protocol_h,
+		tests_client_protocol_c,
+		tests_protocol_c,
+	],
+	'connection-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+	],
+	'event-loop-test': [ wayland_server_protocol_h ],
+	'fixed-test': [],
+	'interface-test': [ wayland_client_protocol_h ],
+	'list-test': [],
+	'map-test': [],
+	'sanity-test' : [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+	],
+	'socket-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+	],
+	'queue-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+	],
+	'signal-test': [ wayland_server_protocol_h ],
+	'newsignal-test': [
+		# wayland-server.c is needed here to access wl_priv_* functions
+		files('../src/wayland-server.c'),
+		wayland_server_protocol_h,
+	],
+	'resources-test': [ wayland_server_protocol_h ],
+	'message-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+	],
+	'compositor-introspection-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+	],
+	'protocol-logger-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+	],
+	'headers-test': [
+		wayland_client_protocol_h,
+		wayland_server_protocol_h,
+		'headers-protocol-test.c',
+		wayland_client_protocol_core_h,
+		wayland_server_protocol_core_h,
+		'headers-protocol-core-test.c',
+	],
+	'os-wrappers-test': [],
+}
+
+foreach test_name, test_extra_sources: tests
+	test_sources = [ test_name + '.c' ] + test_extra_sources
+	test_deps = [test_runner_dep]
+	bin = executable(test_name, test_sources, dependencies: test_deps)
+	test(
+		test_name,
+		bin,
+		env: [
+			'TEST_SRC_DIR=@0@'.format(meson.current_source_dir()),
+			'TEST_BUILD_DIR=@0@'.format(meson.current_build_dir()),
+		],
+	)
+endforeach
diff --git a/tests/newsignal-test.c b/tests/newsignal-test.c
index 47c429b..f3a7bd9 100644
--- a/tests/newsignal-test.c
+++ b/tests/newsignal-test.c
@@ -26,7 +26,7 @@
 #include <assert.h>
 
 #include "test-runner.h"
-#include "wayland-private.h"
+#include "wayland-server-private.h"
 
 static void
 signal_notify(struct wl_listener *listener, void *data)
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
new file mode 100644
index 0000000..c09468d
--- /dev/null
+++ b/tests/proxy-test.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "wayland-server.h"
+#include "wayland-client.h"
+#include "test-runner.h"
+
+static struct {
+	struct wl_display *display;
+	struct wl_event_loop *loop;
+	int sync_count;
+} server;
+
+static struct {
+	struct wl_display *display;
+	struct wl_callback *callback_a;
+	struct wl_callback *callback_b;
+	int callback_count;
+} client;
+
+static const char *tag_a = "tag";
+static const char *tag_b = "tag";
+
+static void
+callback_done(void *data, struct wl_callback *cb, uint32_t time)
+{
+	const char * const *expected_tag;
+	const char * const *tag;
+
+	if (cb == client.callback_a)
+		expected_tag = &tag_a;
+	else if (cb == client.callback_b)
+		expected_tag = &tag_b;
+	else
+		assert(!"unexpected callback");
+
+	tag = wl_proxy_get_tag((struct wl_proxy *) cb);
+
+	assert(tag == expected_tag);
+	assert(strcmp(*tag, "tag") == 0);
+
+	wl_callback_destroy(cb);
+
+	client.callback_count++;
+}
+
+static const struct wl_callback_listener callback_listener = {
+	callback_done,
+};
+
+static void
+logger_func(void *user_data,
+	    enum wl_protocol_logger_type type,
+	    const struct wl_protocol_logger_message *message)
+{
+	if (type != WL_PROTOCOL_LOGGER_REQUEST)
+		return;
+
+	assert(strcmp(wl_resource_get_class(message->resource),
+		      "wl_display") == 0);
+	assert(strcmp(message->message->name, "sync") == 0);
+
+	server.sync_count++;
+}
+
+TEST(proxy_tag)
+{
+	const char *socket;
+	struct wl_protocol_logger *logger;
+
+	assert(&tag_a != &tag_b);
+
+	server.display = wl_display_create();
+	assert(server.display);
+	server.loop = wl_display_get_event_loop(server.display);
+	assert(server.loop);
+	socket = wl_display_add_socket_auto(server.display);
+	assert(socket);
+	logger = wl_display_add_protocol_logger(server.display,
+						logger_func, NULL);
+	assert(logger);
+
+	client.display = wl_display_connect(socket);
+	assert(client.display);
+
+	client.callback_a = wl_display_sync(client.display);
+	wl_callback_add_listener(client.callback_a, &callback_listener, NULL);
+	wl_proxy_set_tag((struct wl_proxy *) client.callback_a,
+			 &tag_a);
+
+	client.callback_b = wl_display_sync(client.display);
+	wl_callback_add_listener(client.callback_b, &callback_listener, NULL);
+	wl_proxy_set_tag((struct wl_proxy *) client.callback_b,
+			 &tag_b);
+
+	wl_display_flush(client.display);
+
+	while (server.sync_count < 2) {
+		wl_event_loop_dispatch(server.loop, -1);
+		wl_display_flush_clients(server.display);
+	}
+
+	wl_display_dispatch(client.display);
+
+	assert(client.callback_count == 2);
+
+	wl_protocol_logger_destroy(logger);
+	wl_display_disconnect(client.display);
+	wl_event_loop_dispatch(server.loop, 100);
+
+	wl_display_destroy(server.display);
+}
diff --git a/tests/scanner-test.sh b/tests/scanner-test.sh
index ff25089..35ba047 100755
--- a/tests/scanner-test.sh
+++ b/tests/scanner-test.sh
@@ -36,6 +36,20 @@
 		fail "$2 -> $3"
 }
 
+verify_error() {
+	echo
+	echo "Checking that reading $1 gives an error on line $3"
+
+	[ -f "$TEST_DATA_DIR/$1" ] || hard_fail "$1 not present"
+
+	# Confirm failure error code
+	"$WAYLAND_SCANNER" server-header < "$TEST_DATA_DIR/$1" >/dev/null 2>"$TEST_OUTPUT_DIR/$2" && \
+		fail "$1 return code check"
+
+	# Verify that an error is produced at the correct line
+	grep -q "<stdin>:$3: error:" "$TEST_OUTPUT_DIR/$2" && echo "$1 PASS" || fail "$1 line number check"
+}
+
 generate_and_compare "code" "example.xml" "example-code.c"
 generate_and_compare "client-header" "example.xml" "example-client.h"
 generate_and_compare "server-header" "example.xml" "example-server.h"
@@ -52,4 +66,13 @@
 generate_and_compare "code" "small.xml" "small-code.c"
 generate_and_compare "public-code" "small.xml" "small-code.c"
 generate_and_compare "private-code" "small.xml" "small-private-code.c"
+
+verify_error "bad-identifier-arg.xml" "bad-identifier-arg.log" 7
+verify_error "bad-identifier-entry.xml" "bad-identifier-entry.log" 8
+verify_error "bad-identifier-enum.xml" "bad-identifier-enum.log" 6
+verify_error "bad-identifier-event.xml" "bad-identifier-event.log" 6
+verify_error "bad-identifier-interface.xml" "bad-identifier-interface.log" 3
+verify_error "bad-identifier-protocol.xml" "bad-identifier-protocol.log" 2
+verify_error "bad-identifier-request.xml" "bad-identifier-request.log" 6
+
 exit $RETCODE
diff --git a/tests/test-compositor.c b/tests/test-compositor.c
index 72f6351..468ee56 100644
--- a/tests/test-compositor.c
+++ b/tests/test-compositor.c
@@ -45,7 +45,8 @@
 
 static const struct wl_message tc_requests[] = {
 	/* this request serves as a barrier for synchronizing*/
-	{ "stop_display", "u", NULL }
+	{ "stop_display", "u", NULL },
+	{ "noop", "", NULL },
 };
 
 static const struct wl_message tc_events[] = {
@@ -54,7 +55,7 @@
 
 const struct wl_interface test_compositor_interface = {
 	"test", 1,
-	1, tc_requests,
+	2, tc_requests,
 	1, tc_events
 };
 
@@ -62,6 +63,8 @@
 	void (*stop_display)(struct wl_client *client,
 			     struct wl_resource *resource,
 			     uint32_t num);
+	void (*noop)(struct wl_client *client,
+			     struct wl_resource *resource);
 };
 
 struct test_compositor_listener {
@@ -70,7 +73,8 @@
 };
 
 enum {
-	STOP_DISPLAY = 0
+	STOP_DISPLAY = 0,
+	TEST_NOOP = 1
 };
 
 enum {
@@ -294,8 +298,16 @@
 		wl_display_terminate(d->wl_display);
 }
 
+static void
+handle_noop(struct wl_client *client, struct wl_resource *resource)
+{
+	(void)client;
+	(void)resource;
+}
+
 static const struct test_compositor_interface tc_implementation = {
-	handle_stop_display
+	handle_stop_display,
+	handle_noop,
 };
 
 static void
@@ -354,7 +366,7 @@
 }
 
 void
-display_resume(struct display *d)
+display_post_resume_events(struct display *d)
 {
 	struct wfr *wfr, *next;
 
@@ -368,7 +380,12 @@
 
 	assert(wl_list_empty(&d->waiting_for_resume));
 	d->wfr_num = 0;
+}
 
+void
+display_resume(struct display *d)
+{
+	display_post_resume_events(d);
 	wl_display_run(d->wl_display);
 }
 
@@ -509,3 +526,9 @@
 
 	return n;
 }
+
+void
+noop_request(struct client *c)
+{
+	wl_proxy_marshal((struct wl_proxy *) c->tc, TEST_NOOP);
+}
diff --git a/tests/test-compositor.h b/tests/test-compositor.h
index 876d0c0..180dad2 100644
--- a/tests/test-compositor.h
+++ b/tests/test-compositor.h
@@ -70,6 +70,7 @@
 struct client *client_connect(void);
 void client_disconnect(struct client *);
 int stop_display(struct client *, int);
+void noop_request(struct client *);
 
 /**
  * Usual workflow:
@@ -89,12 +90,21 @@
 void display_destroy(struct display *d);
 void display_run(struct display *d);
 
+/* This function posts the display_resumed event to all waiting clients,
+ * so that after flushing events the clients will stop waiting and continue.
+ *
+ * (Calling `display_run` after this function will resume the display loop.)
+ */
+void display_post_resume_events(struct display *d);
 /* After n clients called stop_display(..., n), the display
  * is stopped and can process the code after display_run().
- * This function rerun the display again and send display_resumed
- * event to waiting clients, so the clients will stop waiting and continue */
+ *
+ * This function posts the display_resumed event to the waiting
+ * clients, so that the clients will stop waiting and continue;
+ * it then reruns the display. */
 void display_resume(struct display *d);
 
+
 struct client_info *client_create_with_name(struct display *d,
 					    void (*client_main)(void *data),
 					    void *data,
diff --git a/tests/test-helpers.c b/tests/test-helpers.c
index b2189d8..20b6690 100644
--- a/tests/test-helpers.c
+++ b/tests/test-helpers.c
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <dirent.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <time.h>
 #include <sys/time.h>
@@ -67,11 +68,19 @@
 void
 exec_fd_leak_check(int nr_expected_fds)
 {
-	const char *exe = "./exec-fd-leak-checker";
+	const char *exe = "exec-fd-leak-checker";
 	char number[16] = { 0 };
+	const char *test_build_dir = getenv("TEST_BUILD_DIR");
+	char exe_path[256] = { 0 };
+
+	if (test_build_dir == NULL || test_build_dir[0] == 0) {
+	        test_build_dir = ".";
+	}
+
+	snprintf(exe_path, sizeof exe_path - 1, "%s/%s", test_build_dir, exe);
 
 	snprintf(number, sizeof number - 1, "%d", nr_expected_fds);
-	execl(exe, exe, number, (char *)NULL);
+	execl(exe_path, exe, number, (char *)NULL);
 	assert(0 && "execing fd leak checker failed");
 }
 
diff --git a/tests/test-runner.c b/tests/test-runner.c
index 1487dc4..8f08445 100644
--- a/tests/test-runner.c
+++ b/tests/test-runner.c
@@ -338,7 +338,8 @@
 
 		if (waitid(P_PID, pid, &info, WEXITED)) {
 			stderr_set_color(RED);
-			fprintf(stderr, "waitid failed: %m\n");
+			fprintf(stderr, "waitid failed: %s\n",
+				strerror(errno));
 			stderr_reset_color();
 
 			abort();
diff --git a/wayland-scanner.m4 b/wayland-scanner.m4
index 4e4222a..2b222e8 100644
--- a/wayland-scanner.m4
+++ b/wayland-scanner.m4
@@ -1,7 +1,7 @@
 AC_DEFUN([WAYLAND_SCANNER_RULES], [
     PKG_PROG_PKG_CONFIG
 
-    PKG_CHECK_MODULES([WAYLAND_SCANNER], [wayland-scanner])
+    PKG_CHECK_MODULES([WAYLAND_SCANNER], [wayland-scanner >= 1.14.0])
 
     wayland_scanner=`$PKG_CONFIG --variable=wayland_scanner wayland-scanner`
     AC_SUBST([wayland_scanner])