aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk6
-rw-r--r--Makefile.am8
-rw-r--r--configure.ac19
-rw-r--r--doc/Contributing75
-rw-r--r--doc/doxygen/Makefile.am4
-rw-r--r--doc/publican/protocol-to-docbook.xsl9
-rw-r--r--doc/publican/sources/Protocol.xml41
-rw-r--r--protocol/wayland.dtd4
-rw-r--r--protocol/wayland.xml592
-rw-r--r--src/connection.c40
-rw-r--r--src/dtddata.S39
-rw-r--r--src/event-loop.c1
-rw-r--r--src/scanner.c192
-rw-r--r--src/wayland-client-core.h179
-rw-r--r--src/wayland-client.c582
-rw-r--r--src/wayland-private.h95
-rw-r--r--src/wayland-server-core.h209
-rw-r--r--src/wayland-server.c105
-rw-r--r--src/wayland-shm.c108
-rw-r--r--src/wayland-util.c12
-rw-r--r--src/wayland-util.h45
-rw-r--r--tests/connection-test.c4
-rw-r--r--tests/display-test.c327
-rw-r--r--tests/queue-test.c6
-rw-r--r--tests/sanity-test.c27
-rw-r--r--tests/socket-test.c15
-rw-r--r--tests/test-compositor.c44
-rw-r--r--tests/test-compositor.h11
28 files changed, 2171 insertions, 628 deletions
diff --git a/Android.mk b/Android.mk
index e32970b..79836d8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -19,8 +19,8 @@ WAYLAND_CFLAGS += -Wmissing-prototypes -fvisibility=hidden -Wno-pointer-arith
WAYLAND_VERSION_MAJOR := 1
WAYLAND_VERSION_MINOR := 9
-WAYLAND_VERSION_MICRO := 90
-WAYLAND_VERSION := "1.9.90"
+WAYLAND_VERSION_MICRO := 91
+WAYLAND_VERSION := "1.9.91"
WAYLAND_TEMPLATE_SUBST := -e s/@WAYLAND_VERSION_MAJOR@/$(WAYLAND_VERSION_MAJOR)/
WAYLAND_TEMPLATE_SUBST += -e s/@WAYLAND_VERSION_MINOR@/$(WAYLAND_VERSION_MINOR)/
@@ -130,4 +130,4 @@ $(GEN) : $(LOCAL_PATH)/src/%.h : $(LOCAL_PATH)/src/%.h.in
# which adds this in build/core/binary.mk, to avoid relying on build internals.
$(call local-intermediates-dir)/export_includes : $(GEN)
-include $(BUILD_STATIC_LIBRARY) \ No newline at end of file
+include $(BUILD_STATIC_LIBRARY)
diff --git a/Makefile.am b/Makefile.am
index 9114d98..e850abc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,11 +23,13 @@ pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA =
bin_PROGRAMS = wayland-scanner
-wayland_scanner_SOURCES = src/scanner.c
-wayland_scanner_CFLAGS = $(EXPAT_CFLAGS) $(AM_CFLAGS)
-wayland_scanner_LDADD = $(EXPAT_LIBS) libwayland-util.la
+wayland_scanner_SOURCES = src/scanner.c src/dtddata.S
+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
+src/dtddata.o: protocol/wayland.dtd
+
if USE_HOST_SCANNER
wayland_scanner = wayland-scanner
else
diff --git a/configure.ac b/configure.ac
index 41cea2a..2469cdb 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], [9])
-m4_define([wayland_micro_version], [0])
+m4_define([wayland_major_version], [1])
+m4_define([wayland_minor_version], [9])
+m4_define([wayland_micro_version], [91])
m4_define([wayland_version],
[wayland_major_version.wayland_minor_version.wayland_micro_version])
@@ -28,6 +28,7 @@ AM_SILENT_RULES([yes])
AC_PROG_CC
AC_PROG_CXX
AC_PROG_GREP
+AM_PROG_AS
# check if we have C++ compiler. This is hacky workaround,
# for a reason why it is this way see
@@ -71,6 +72,12 @@ AC_ARG_ENABLE([documentation],
[],
[enable_documentation=yes])
+AC_ARG_ENABLE([dtd-validation],
+ [AC_HELP_STRING([--disable-dtd-validation],
+ [Disable DTD validation of the protocol])],
+ [],
+ [enable_dtdvalidation=yes])
+
AM_CONDITIONAL(USE_HOST_SCANNER, test "x$with_host_scanner" = xyes)
AM_CONDITIONAL(ENABLE_LIBRARIES, test "x$enable_libraries" = xyes)
@@ -105,6 +112,12 @@ PKG_CHECK_MODULES(EXPAT, [expat], [],
AC_SUBST(EXPAT_LIBS)
])
+if test "x$enable_dtdvalidation" = "xyes"; then
+ PKG_CHECK_MODULES(LIBXML, [libxml-2.0])
+ AC_DEFINE(HAVE_LIBXML, 1, [libxml-2.0 is available])
+ AC_CONFIG_LINKS([src/wayland.dtd.embed:protocol/wayland.dtd])
+fi
+
AC_PATH_PROG(XSLTPROC, xsltproc)
AM_CONDITIONAL([HAVE_XSLTPROC], [test "x$XSLTPROC" != "x"])
diff --git a/doc/Contributing b/doc/Contributing
index 39c3e39..fe90614 100644
--- a/doc/Contributing
+++ b/doc/Contributing
@@ -30,6 +30,81 @@ cope with the way git log presents them.
See [2] for a recommend reading on writing commit messages.
+
+== Tracking patches and following up ==
+
+Patchwork is used for tracking patches to Wayland and Weston:
+http://patchwork.freedesktop.org/project/wayland/list/
+
+Xwayland patches are tracked with the Xorg project, not here.
+
+Libinput patches, even though they use the same mailing list as Wayland, are
+not tracked in the Wayland Patchwork.
+
+The following applies only to Wayland and Weston.
+
+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 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 or
+ Weston. 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/Weston.
+
+
== Coding style ==
You should follow the style of the file you're editing. In general, we
diff --git a/doc/doxygen/Makefile.am b/doc/doxygen/Makefile.am
index 5520d39..a8bb95f 100644
--- a/doc/doxygen/Makefile.am
+++ b/doc/doxygen/Makefile.am
@@ -44,14 +44,14 @@ $(diagrams): $(diagramssrc)
$(diagram_maps): $(diagramssrc)
-xml/%/index.xml: $(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) | xml/%
$(AM_V_GEN)(cat wayland.doxygen; \
echo "GENERATE_XML=YES"; \
echo "XML_OUTPUT=xml/$*"; \
echo "INPUT= $(scanned_src_files_$*)"; \
) | $(DOXYGEN) -
-man/man3/wl_display.3: $(scanned_src_files_man) wayland.doxygen | man/man3
+man/man3/wl_display.3: $(top_srcdir)/src/scanner.c $(scanned_src_files_man) wayland.doxygen | man/man3
$(AM_V_GEN)(cat wayland.doxygen; \
echo "GENERATE_MAN=YES"; \
echo "MAN_OUTPUT=man"; \
diff --git a/doc/publican/protocol-to-docbook.xsl b/doc/publican/protocol-to-docbook.xsl
index 7b45969..fad207a 100644
--- a/doc/publican/protocol-to-docbook.xsl
+++ b/doc/publican/protocol-to-docbook.xsl
@@ -102,6 +102,12 @@
<term><xsl:value-of select="@name"/></term>
<listitem>
<simpara>
+ <xsl:if test="@enum">
+ <link linkend="protocol-spec-{../../@name}-enum-{@enum}">
+ <xsl:value-of select="@enum"/>
+ </link>
+ <xsl:text> </xsl:text>
+ </xsl:if>
<xsl:value-of select="@type"/>
<xsl:if test="@summary" >
- <xsl:value-of select="@summary"/>
@@ -171,6 +177,9 @@
<section id="protocol-spec-{../@name}-{name()}-{@name}">
<title>
<xsl:value-of select="../@name"/>::<xsl:value-of select="@name" />
+ <xsl:if test="@bitfield">
+ - bitfield
+ </xsl:if>
<xsl:if test="description/@summary">
- <xsl:value-of select="description/@summary" />
</xsl:if>
diff --git a/doc/publican/sources/Protocol.xml b/doc/publican/sources/Protocol.xml
index 477063b..66cebfb 100644
--- a/doc/publican/sources/Protocol.xml
+++ b/doc/publican/sources/Protocol.xml
@@ -15,6 +15,38 @@
identifies which method in the interface to invoke.
</para>
<para>
+ The protocol is message-based. A message sent by a client to the server
+ is called request. A message from the server to a client is called event.
+ A message has a number of arguments, each of which has a certain type (see
+ <xref linkend="sect-Protocol-Wire-Format"/> for a list of argument types).
+ </para>
+ <para>
+ Additionally, the protocol can specify <type>enum</type>s which associate
+ names to specific numeric enumeration values. These are primarily just
+ descriptive in nature: at the wire format level enums are just integers.
+ But they also serve a secondary purpose to enhance type safety or
+ otherwise add context for use in language bindings or other such code.
+ This latter usage is only supported so long as code written before these
+ attributes were introduced still works after; in other words, adding an
+ enum should not break API, otherwise it puts backwards compatibility at
+ risk.
+ </para>
+ <para>
+ <type>enum</type>s can be defined as just a set of integers, or as
+ bitfields. This is specified via the <type>bitfield</type> boolean
+ attribute in the <type>enum</type> definition. If this attribute is true,
+ the enum is intended to be accessed primarily using bitwise operations,
+ for example when arbitrarily many choices of the enum can be ORed
+ together; if it is false, or the attribute is omitted, then the enum
+ arguments are a just a sequence of numerical values.
+ </para>
+ <para>
+ The <type>enum</type> attribute can be used on either <type>uint</type>
+ or <type>int</type> arguments, however if the <type>enum</type> is
+ defined as a <type>bitfield</type>, it can only be used on
+ <type>uint</type> args.
+ </para>
+ <para>
The server sends back events to the client, each event is emitted from
an object. Events can be error conditions. The event includes the
object ID and the event opcode, from which the client can determine
@@ -62,14 +94,11 @@
The protocol is sent over a UNIX domain stream socket, where the endpoint
usually is named <systemitem class="service">wayland-0</systemitem>
(although it can be changed via <emphasis>WAYLAND_DISPLAY</emphasis>
- in the environment). The protocol is message-based. A
- message sent by a client to the server is called request. A message
- from the server to a client is called event. Every message is
- structured as 32-bit words, values are represented in the host's
- byte-order.
+ in the environment).
</para>
<para>
- The message header has 2 words in it:
+ Every message is structured as 32-bit words; values are represented in the
+ host's byte-order. The message header has 2 words in it:
<itemizedlist>
<listitem>
<para>
diff --git a/protocol/wayland.dtd b/protocol/wayland.dtd
index b8b1573..15f20ab 100644
--- a/protocol/wayland.dtd
+++ b/protocol/wayland.dtd
@@ -1,4 +1,4 @@
-<!ELEMENT protocol (copyright?, interface+)>
+<!ELEMENT protocol (copyright?, description?, interface+)>
<!ATTLIST protocol name CDATA #REQUIRED>
<!ELEMENT copyright (#PCDATA)>
<!ELEMENT interface (description?,(request|event|enum)+)>
@@ -14,6 +14,7 @@
<!ELEMENT enum (description?,entry*)>
<!ATTLIST enum name CDATA #REQUIRED>
<!ATTLIST enum since CDATA #IMPLIED>
+ <!ATTLIST enum bitfield CDATA #IMPLIED>
<!ELEMENT entry (description?)>
<!ATTLIST entry name CDATA #REQUIRED>
<!ATTLIST entry value CDATA #REQUIRED>
@@ -25,5 +26,6 @@
<!ATTLIST arg summary CDATA #IMPLIED>
<!ATTLIST arg interface CDATA #IMPLIED>
<!ATTLIST arg allow-null CDATA #IMPLIED>
+ <!ATTLIST arg enum CDATA #IMPLIED>
<!ELEMENT description (#PCDATA)>
<!ATTLIST description summary CDATA #REQUIRED>
diff --git a/protocol/wayland.xml b/protocol/wayland.xml
index 42c9309..b223bb4 100644
--- a/protocol/wayland.xml
+++ b/protocol/wayland.xml
@@ -176,7 +176,7 @@
</event>
</interface>
- <interface name="wl_compositor" version="3">
+ <interface name="wl_compositor" version="4">
<description summary="the compositor singleton">
A compositor. This object is a singleton global. The
compositor is in charge of combining the contents of multiple
@@ -367,7 +367,7 @@
can be used for buffers. Known formats include
argb8888 and xrgb8888.
</description>
- <arg name="format" type="uint"/>
+ <arg name="format" type="uint" enum="format"/>
</event>
</interface>
@@ -408,7 +408,7 @@
</interface>
- <interface name="wl_data_offer" version="1">
+ <interface name="wl_data_offer" version="3">
<description summary="offer to transfer data">
A wl_data_offer represents a piece of data offered for transfer
by another client (the source client). It is used by the
@@ -418,12 +418,33 @@
data directly from the source client.
</description>
+ <enum name="error">
+ <entry name="invalid_finish" value="0"
+ summary="finish request was called untimely"/>
+ <entry name="invalid_action_mask" value="1"
+ summary="action mask contains invalid values"/>
+ <entry name="invalid_action" value="2"
+ summary="action argument has an invalid value"/>
+ <entry name="invalid_offer" value="3"
+ summary="offer doesn't accept this request"/>
+ </enum>
+
<request name="accept">
<description summary="accept one of the offered mime types">
Indicate that the client can accept the given mime type, or
NULL for not accepted.
- Used for feedback during drag-and-drop.
+ For objects of version 2 or older, this request is used by the
+ client to give feedback whether the client can receive the given
+ mime type, or NULL if none is accepted; the feedback does not
+ determine whether the drag-and-drop operation succeeds or not.
+
+ For objects of version 3 or newer, this request determines the
+ final result of the drag-and-drop operation. If the end result
+ is that no mime types were accepted, the drag-and-drop operation
+ will be cancelled and the corresponding drag source will receive
+ wl_data_source.cancelled. Clients may still use this event in
+ conjunction with wl_data_source.action for feedback.
</description>
<arg name="serial" type="uint"/>
@@ -442,6 +463,11 @@
The receiving client reads from the read end of the pipe until
EOF and then closes its end, at which point the transfer is
complete.
+
+ This request may happen multiple times for different mimetypes,
+ both before and after wl_data_device.drop. Drag-and-drop destination
+ clients may preemptively fetch data or examine it more closely to
+ determine acceptance.
</description>
<arg name="mime_type" type="string"/>
<arg name="fd" type="fd"/>
@@ -461,9 +487,115 @@
<arg name="mime_type" type="string"/>
</event>
+
+ <!-- Version 3 additions -->
+
+ <request name="finish" since="3">
+ <description summary="the offer will no longer be used">
+ Notifies the compositor that the drag destination successfully
+ finished the drag-and-drop operation.
+
+ Upon receiving this request, the compositor will emit
+ wl_data_source.dnd_finished on the drag source client.
+
+ It is a client error to perform other requests than
+ wl_data_offer.destroy after this one. It is also an error to perform
+ 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.
+ </description>
+ </request>
+
+ <request name="set_actions" since="3">
+ <description summary="set the available/preferred drag-and-drop actions">
+ Sets the actions that the destination side client supports for
+ this operation. This request may trigger the emission of
+ wl_data_source.action and wl_data_offer.action events if the compositor
+ need to change the selected action.
+
+ This request can be called multiple times throughout the
+ drag-and-drop operation, typically in response to wl_data_device.enter
+ or wl_data_device.motion events.
+
+ 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 dnd_actions argument must contain only values expressed in the
+ wl_data_device_manager.dnd_actions enum, and the preferred_action
+ argument must only contain one of those values set, otherwise it
+ will result in a protocol error.
+
+ While managing an "ask" action, the destination drag-and-drop client
+ may perform further wl_data_offer.receive requests, and is expected
+ to perform one last wl_data_offer.set_actions request with a preferred
+ action other than "ask" (and optionally wl_data_offer.accept) before
+ requesting wl_data_offer.finish, in order to convey the action selected
+ by the user. If the preferred action is not in the
+ wl_data_offer.source_actions mask, an error will be raised.
+
+ If the "ask" action is dismissed (e.g. user cancellation), the client
+ is expected to perform wl_data_offer.destroy right away.
+
+ 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"/>
+ <arg name="preferred_action" type="uint"/>
+ </request>
+
+ <event name="source_actions" since="3">
+ <description summary="notify the source-side available actions">
+ This event indicates the actions offered by the data source. It
+ 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"/>
+ </event>
+
+ <event name="action" since="3">
+ <description summary="notify the selected action">
+ This event indicates the action selected by the compositor after
+ matching the source/destination side actions. Only one action (or
+ none) will be offered here.
+
+ This event can be emitted multiple times during the drag-and-drop
+ operation in response to destination side action changes through
+ wl_data_offer.set_actions.
+
+ This event will no longer be emitted after wl_data_device.drop
+ happened on the drag-and-drop destination, the client must
+ honor the last action received, or the last preferred one set
+ through wl_data_offer.set_actions when handling an "ask" action.
+
+ Compositors may also change the selected action on the fly, mainly
+ in response to keyboard modifier changes during the drag-and-drop
+ operation.
+
+ The most recent action received is always the valid one. Prior to
+ receiving wl_data_device.drop, the chosen action may change (e.g.
+ due to keyboard modifiers being pressed). At the time of receiving
+ wl_data_device.drop the drag-and-drop destination must honor the
+ last action received.
+
+ Action changes may still happen after wl_data_device.drop,
+ especially on "ask" actions, where the drag-and-drop destination
+ may choose another action afterwards. Action changes happening
+ at this stage are always the result of inter-client negotiation, the
+ compositor shall no longer be able to induce a different action.
+
+ Upon "ask" actions, it is expected that the drag-and-drop destination
+ may potentially choose different a different action and/or mime type,
+ based on wl_data_offer.source_actions and finally chosen by the
+ user (e.g. popping up a menu with the available options). The
+ 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"/>
+ </event>
</interface>
- <interface name="wl_data_source" version="1">
+ <interface name="wl_data_source" version="3">
<description summary="offer to transfer data">
The wl_data_source object is the source side of a wl_data_offer.
It is created by the source client in a data transfer and
@@ -471,6 +603,13 @@
to requests to transfer the data.
</description>
+ <enum name="error">
+ <entry name="invalid_action_mask" value="0"
+ summary="action mask contains invalid values"/>
+ <entry name="invalid_source" value="1"
+ summary="source doesn't accept this request"/>
+ </enum>
+
<request name="offer">
<description summary="add an offered mime type">
This request adds a mime type to the set of mime types
@@ -510,14 +649,108 @@
<event name="cancelled">
<description summary="selection was cancelled">
- This data source has been replaced by another data source.
+ This data source is no longer valid. There are several reasons why
+ this could happen:
+
+ - The data source has been replaced by another data source.
+ - The drag-and-drop operation was performed, but the drop destination
+ did not accept any of the mimetypes offered through
+ wl_data_source.target.
+ - The drag-and-drop operation was performed, but the drop destination
+ did not select any of the actions present in the mask offered through
+ wl_data_source.action.
+ - The drag-and-drop operation was performed but didn't happen over a
+ surface.
+ - The compositor cancelled the drag-and-drop operation (e.g. compositor
+ dependent timeouts to avoid stale drag-and-drop transfers).
+
The client should clean up and destroy this data source.
+
+ For objects of version 2 or older, wl_data_source.cancelled will
+ only be emitted if the data source was replaced by another data
+ source.
</description>
</event>
+ <!-- Version 3 additions -->
+
+ <request name="set_actions" since="3">
+ <description summary="set the available drag-and-drop actions">
+ Sets the actions that the source side client supports for this
+ operation. This request may trigger wl_data_source.action and
+ wl_data_offer.action events if the compositor needs to change the
+ selected action.
+
+ The dnd_actions argument must contain only values expressed in the
+ wl_data_device_manager.dnd_actions enum, otherwise it will result
+ in a protocol error.
+
+ This request must be made once only, and can only be made on sources
+ used in drag-and-drop, so it must be performed before
+ 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"/>
+ </request>
+
+ <event name="dnd_drop_performed" since="3">
+ <description summary="the drag-and-drop operation physically finished">
+ The user performed the drop action. This event does not indicate
+ acceptance, wl_data_source.cancelled may still be emitted afterwards
+ if the drop destination does not accept any mimetype.
+
+ However, this event might however not be received if the compositor
+ cancelled the drag-and-drop operation before this event could happen.
+
+ Note that the data_source may still be used in the future and should
+ not be destroyed here.
+ </description>
+ </event>
+
+ <event name="dnd_finished" since="3">
+ <description summary="the drag-and-drop operation concluded">
+ The drop destination finished interoperating with this data
+ source, so the client is now free to destroy this data source and
+ free all associated data.
+
+ If the action used to perform the operation was "move", the
+ source can now delete the transferred data.
+ </description>
+ </event>
+
+ <event name="action" since="3">
+ <description summary="notify the selected action">
+ This event indicates the action selected by the compositor after
+ matching the source/destination side actions. Only one action (or
+ none) will be offered here.
+
+ This event can be emitted multiple times during the drag-and-drop
+ operation, mainly in response to destination side changes through
+ wl_data_offer.set_actions, and as the data device enters/leaves
+ surfaces.
+
+ It is only possible to receive this event after
+ wl_data_source.dnd_drop_performed if the drag-and-drop operation
+ ended in an "ask" action, in which case the final wl_data_source.action
+ event will happen immediately before wl_data_source.dnd_finished.
+
+ Compositors may also change the selected action on the fly, mainly
+ in response to keyboard modifier changes during the drag-and-drop
+ operation.
+
+ The most recent action received is always the valid one. The chosen
+ action may change alongside negotiation (e.g. an "ask" action can turn
+ into a "move" operation), so the effects of the final action must
+ always be applied in wl_data_offer.dnd_finished.
+
+ Clients can trigger cursor surface changes from this point, so
+ they reflect the current action.
+ </description>
+ <arg name="dnd_action" type="uint"/>
+ </event>
</interface>
- <interface name="wl_data_device" version="2">
+ <interface name="wl_data_device" version="3">
<description summary="data transfer device">
There is one wl_data_device per seat which can be obtained
from the global wl_data_device_manager singleton.
@@ -630,6 +863,17 @@
<description summary="end drag-and-drag session successfully">
The event is sent when a drag-and-drop operation is ended
because the implicit grab is removed.
+
+ The drag-and-drop destination is expected to honor the last action
+ received through wl_data_offer.action, if the resulting action is
+ "copy" or "move", the destination can still perform
+ wl_data_offer.receive requests, and is expected to end all
+ transfers with a wl_data_offer.finish request.
+
+ If the resulting action is "ask", the action will not be considered
+ final. The drag-and-drop destination is expected to perform one last
+ wl_data_offer.set_actions request, or wl_data_offer.destroy in order
+ to cancel the operation.
</description>
</event>
@@ -659,7 +903,7 @@
</request>
</interface>
- <interface name="wl_data_device_manager" version="2">
+ <interface name="wl_data_device_manager" version="3">
<description summary="data transfer interface">
The wl_data_device_manager is a singleton global object that
provides access to inter-client data transfer mechanisms such as
@@ -682,6 +926,40 @@
<arg name="id" type="new_id" interface="wl_data_device"/>
<arg name="seat" type="object" interface="wl_seat"/>
</request>
+
+ <!-- Version 3 additions -->
+
+ <enum name="dnd_action" bitfield="true" since="3">
+ <description summary="drag and drop actions">
+ This is a bitmask of the available/preferred actions in a
+ drag-and-drop operation.
+
+ In the compositor, the selected action is a result of matching the
+ actions offered by the source and destination sides. "action" events
+ with a "none" action will be sent to both source and destination if
+ there is no match. All further checks will effectively happen on
+ (source actions ∩ destination actions).
+
+ In addition, compositors may also pick different actions in
+ reaction to key modifiers being pressed, one common design that
+ is used in major toolkits (and the behavior recommended for
+ compositors) is:
+
+ - If no modifiers are pressed, the first match (in bit order)
+ will be used.
+ - Pressing Shift selects "move", if enabled in the mask.
+ - Pressing Control selects "copy", if enabled in the mask.
+
+ Behavior beyond that is considered implementation-dependent.
+ Compositors may for example bind other modifiers (like Alt/Meta)
+ or drags initiated with other buttons than BTN_LEFT to specific
+ actions (e.g. "ask").
+ </description>
+ <entry name="none" value="0"/>
+ <entry name="copy" value="1"/>
+ <entry name="move" value="2"/>
+ <entry name="ask" value="4"/>
+ </enum>
</interface>
<interface name="wl_shell" version="1">
@@ -746,7 +1024,7 @@
<arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
</request>
- <enum name="resize">
+ <enum name="resize" bitfield="true">
<description summary="edge values for resizing">
These values are used to indicate which edge of a surface
is being dragged in a resize operation. The server may
@@ -774,7 +1052,7 @@
</description>
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
<arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
- <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
+ <arg name="edges" type="uint" enum="resize" summary="which edge or corner is being dragged"/>
</request>
<request name="set_toplevel">
@@ -785,7 +1063,7 @@
</description>
</request>
- <enum name="transient">
+ <enum name="transient" bitfield="true">
<description summary="details of transient behaviour">
These flags specify details of the expected behaviour
of transient surfaces. Used in the set_transient request.
@@ -807,7 +1085,7 @@
<arg name="parent" type="object" interface="wl_surface"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
- <arg name="flags" type="uint"/>
+ <arg name="flags" type="uint" enum="transient"/>
</request>
<enum name="fullscreen_method">
@@ -858,7 +1136,7 @@
with the dimensions for the output on which the surface will
be made fullscreen.
</description>
- <arg name="method" type="uint"/>
+ <arg name="method" type="uint" enum="fullscreen_method"/>
<arg name="framerate" type="uint"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
</request>
@@ -891,7 +1169,7 @@
<arg name="parent" type="object" interface="wl_surface"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
- <arg name="flags" type="uint"/>
+ <arg name="flags" type="uint" enum="transient"/>
</request>
<request name="set_maximized">
@@ -972,7 +1250,7 @@
in surface local coordinates.
</description>
- <arg name="edges" type="uint"/>
+ <arg name="edges" type="uint" enum="resize"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</event>
@@ -986,7 +1264,7 @@
</event>
</interface>
- <interface name="wl_surface" version="3">
+ <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.
@@ -1095,10 +1373,8 @@
<description summary="mark part of the surface damaged">
This request is used to describe the regions where the pending
buffer is different from the current surface contents, and where
- the surface therefore needs to be repainted. The pending buffer
- must be set by wl_surface.attach before sending damage. The
- compositor ignores the parts of the damage that fall outside of
- the surface.
+ the surface therefore needs to be repainted. The compositor
+ ignores the parts of the damage that fall outside of the surface.
Damage is double-buffered state, see wl_surface.commit.
@@ -1111,6 +1387,10 @@
wl_surface.commit assigns pending damage as the current damage,
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 co-ordinates instead of surface co-ordinates,
+ and is probably the preferred and intuitive way of doing this.
</description>
<arg name="x" type="int"/>
@@ -1327,9 +1607,51 @@
</description>
<arg name="scale" type="int"/>
</request>
+
+ <!-- Version 4 additions -->
+ <request name="damage_buffer" since="4">
+ <description summary="mark part of the surface damaged using buffer co-ordinates">
+ This request is used to describe the regions where the pending
+ buffer is different from the current surface contents, and where
+ the surface therefore needs to be repainted. The compositor
+ ignores the parts of the damage that fall outside of the surface.
+
+ Damage is double-buffered state, see wl_surface.commit.
+
+ The damage rectangle is specified in buffer coordinates.
+
+ The initial value for pending damage is empty: no damage.
+ wl_surface.damage_buffer adds pending damage: the new pending
+ damage is the union of old pending damage and the given rectangle.
+
+ wl_surface.commit assigns pending damage as the current damage,
+ and clears pending damage. The server will clear the current
+ damage as it repaints the surface.
+
+ This request differs from wl_surface.damage in only one way - it
+ takes damage in buffer co-ordinates instead of surface local
+ co-ordinates. While this generally is more intuitive than surface
+ co-ordinates, it is especially desirable when using wp_viewport
+ or when a drawing library (like EGL) is unaware of buffer scale
+ and buffer transform.
+
+ Note: Because buffer transformation changes and damage requests may
+ be interleaved in the protocol stream, It is impossible to determine
+ the actual mapping between surface and buffer damage until
+ wl_surface.commit time. Therefore, compositors wishing to take both
+ kinds of damage into account will have to accumulate damage from the
+ two requests separately and only transform from one to the other
+ after receiving the wl_surface.commit.
+ </description>
+
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
</interface>
- <interface name="wl_seat" version="4">
+ <interface name="wl_seat" version="5">
<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
@@ -1337,7 +1659,7 @@
maintains a keyboard focus and a pointer focus.
</description>
- <enum name="capability">
+ <enum name="capability" bitfield="true">
<description summary="seat capability bitmask">
This is a bitmask of capabilities this seat has; if a member is
set, then it is present on the seat.
@@ -1349,42 +1671,69 @@
<event name="capabilities">
<description summary="seat capabilities changed">
- This is emitted whenever a seat gains or loses the pointer,
+ This is emitted whenever a seat gains or loses the pointer,
keyboard or touch capabilities. The argument is a capability
enum containing the complete set of capabilities this seat has.
+
+ When the pointer capability is added, a client may create a
+ wl_pointer object using the wl_seat.get_pointer request. This object
+ will receive pointer events until the capability is removed in the
+ future.
+
+ When the pointer capability is removed, a client should destroy the
+ wl_pointer objects associated with the seat where the capability was
+ removed, using the wl_pointer.release request. No further pointer
+ events will be received on these objects.
+
+ In some compositors, if a seat regains the pointer capability and a
+ client has a previously obtained wl_pointer object of version 4 or
+ less, that object may start sending pointer events again. This
+ behavior is considered a misinterpretation of the intended behavior
+ and must not be relied upon by the client. wl_pointer objects of
+ version 5 or later must not send events if created before the most
+ recent event notifying the client of an added pointer capability.
+
+ The above behavior also applies to wl_keyboard and wl_touch with the
+ keyboard and touch capabilities, respectively.
</description>
- <arg name="capabilities" type="uint"/>
+ <arg name="capabilities" type="uint" enum="capability"/>
</event>
<request name="get_pointer">
<description summary="return pointer object">
- The ID provided will be initialized to the wl_pointer interface
+ The ID provided will be initialized to the wl_pointer interface
for this seat.
- This request only takes effect if the seat has the pointer
- capability.
+ 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.
</description>
<arg name="id" type="new_id" interface="wl_pointer"/>
</request>
<request name="get_keyboard">
<description summary="return keyboard object">
- The ID provided will be initialized to the wl_keyboard interface
+ The ID provided will be initialized to the wl_keyboard interface
for this seat.
- This request only takes effect if the seat has the keyboard
- capability.
+ 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.
</description>
<arg name="id" type="new_id" interface="wl_keyboard"/>
</request>
<request name="get_touch">
<description summary="return touch object">
- The ID provided will be initialized to the wl_touch interface
+ The ID provided will be initialized to the wl_touch interface
for this seat.
- This request only takes effect if the seat has the touch
- capability.
+ 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.
</description>
<arg name="id" type="new_id" interface="wl_touch"/>
</request>
@@ -1400,9 +1749,18 @@
<arg name="name" type="string"/>
</event>
+ <!-- Version 5 additions -->
+
+ <request name="release" type="destructor" since="5">
+ <description summary="release the seat object">
+ Using this request client can tell the server that it is not going to
+ use the seat object anymore.
+ </description>
+ </request>
+
</interface>
- <interface name="wl_pointer" version="3">
+ <interface name="wl_pointer" version="5">
<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
@@ -1521,7 +1879,7 @@
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="button" type="uint"/>
- <arg name="state" type="uint"/>
+ <arg name="state" type="uint" enum="button_state"/>
</event>
<enum name="axis">
@@ -1553,7 +1911,7 @@
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
- <arg name="axis" type="uint"/>
+ <arg name="axis" type="uint" enum="axis"/>
<arg name="value" type="fixed"/>
</event>
@@ -1561,7 +1919,7 @@
<request name="release" type="destructor" since="3">
<description summary="release the pointer object">
- Using this request client can tell the server that it is not going to
+ Using this request client can tell the server that it is not going to
use the pointer object anymore.
This request destroys the pointer proxy object, so user must not call
@@ -1569,9 +1927,153 @@
</description>
</request>
+ <!-- Version 5 additions -->
+
+ <event name="frame" since="5">
+ <description summary="end of a pointer event sequence">
+ Indicates the end of a set of events that logically belong together.
+ A client is expected to accumulate the data in all events within the
+ frame before proceeding.
+
+ All wl_pointer events before a wl_pointer.frame event belong
+ logically together. For example, in a diagonal scroll motion the
+ compositor will send an optional wl_pointer.axis_source event, two
+ wl_pointer.axis events (horizontal and vertical) and finally a
+ wl_pointer.frame event. The client may use this information to
+ calculate a diagonal vector for scrolling.
+
+ When multiple wl_pointer.axis events occur within the same frame,
+ the motion vector is the combined motion of all events.
+ When a wl_pointer.axis and a wl_pointer.axis_stop event occur within
+ the same frame, this indicates that axis movement in one axis has
+ stopped but continues in the other axis.
+ When multiple wl_pointer.axis_stop events occur within in the same
+ frame, this indicates that these axes stopped in the same instance.
+
+ A wl_pointer.frame event is sent for every logical event group,
+ even if the group only contains a single wl_pointer event.
+ Specifically, a client may get a sequence: motion, frame, button,
+ frame, axis, frame, axis_stop, frame.
+
+ The wl_pointer.enter and wl_pointer.leave events are logical events
+ generated by the compositor and not the hardware. These events are
+ also grouped by a wl_pointer.frame. When a pointer moves from one
+ surface to the another, a compositor should group the
+ wl_pointer.leave event within the same wl_pointer.frame.
+ However, a client must not rely on wl_pointer.leave and
+ wl_pointer.enter being in the same wl_pointer.frame.
+ Compositor-specific policies may require the wl_pointer.leave and
+ wl_pointer.enter event being split across multiple wl_pointer.frame
+ groups.
+ </description>
+ </event>
+
+ <enum name="axis_source">
+ <description summary="axis source types">
+ Describes the source types for axis events. This indicates to the
+ client how an axis event was physically generated; a client may
+ adjust the user interface accordingly. For example, scroll events
+ from a "finger" source may be in a smooth coordinate space with
+ kinetic scrolling whereas a "wheel" source may be in discrete steps
+ of a number of lines.
+
+ The "continuous" axis source is a device generating events in a
+ continuous coordinate space, but using something other than a
+ finger. One example for this source is button-based scrolling where
+ the vertical motion of a device is converted to scroll events while
+ a button is held down.
+ </description>
+ <entry name="wheel" value="0" summary="A physical wheel" />
+ <entry name="finger" value="1" summary="Finger on a touch surface" />
+ <entry name="continuous" value="2" summary="Continuous coordinate space"/>
+ </enum>
+
+ <event name="axis_source" since="5">
+ <description summary="axis source event">
+ Source information for scroll and other axes.
+
+ This event does not occur on its own. It is sent before a
+ wl_pointer.frame event and carries the source information for
+ all events within that frame.
+
+ The source specifies how this event was generated. If the source is
+ wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be
+ sent when the user lifts the finger off the device.
+
+ If the source is wl_pointer axis_source.wheel or
+ wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may
+ or may not be sent. Whether a compositor sends a axis_stop event
+ for these sources is hardware-specific and implementation-dependent;
+ clients must not rely on receiving an axis_stop event for these
+ scroll sources and should treat scroll sequences from these scroll
+ sources as unterminated by default.
+
+ This event is optional. If the source is unknown for a particular
+ axis event sequence, no event is sent.
+ Only one wl_pointer.axis_source event is permitted per frame.
+
+ The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+ not guaranteed.
+ </description>
+ <arg name="axis_source" type="uint" enum="axis_source"/>
+ </event>
+
+ <event name="axis_stop" since="5">
+ <description summary="axis stop event">
+ Stop notification for scroll and other axes.
+
+ For some wl_pointer.axis_source types, a wl_pointer.axis_stop event
+ is sent to notify a client that the axis sequence has terminated.
+ This enables the client to implement kinetic scrolling.
+ See the wl_pointer.axis_source documentation for information on when
+ this event may be generated.
+
+ Any wl_pointer.axis events with the same axis_source after this
+ event should be considered as the start of a new axis motion.
+
+ The timestamp is to be interpreted identical to the timestamp in the
+ wl_pointer.axis event. The timestamp value may be the same as a
+ preceeding wl_pointer.axis event.
+ </description>
+ <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+ <arg name="axis" type="uint" enum="axis" summary="the axis stopped with this event"/>
+ </event>
+
+ <event name="axis_discrete" since="5">
+ <description summary="axis click event">
+ Discrete step information for scroll and other axes.
+
+ This event carries the axis value of the wl_pointer.axis event in
+ discrete steps (e.g. mouse wheel clicks).
+
+ This event does not occur on its own, it is coupled with a
+ wl_pointer.axis event that represents this axis value on a
+ continuous scale. The protocol guarantees that each axis_discrete
+ event is always followed by exactly one axis event with the same
+ axis number within the same wl_pointer.frame. Note that the protocol
+ allows for other events to occur between the axis_discrete and
+ its coupled axis event, including other axis_discrete or axis
+ events.
+
+ This event is optional; continuous scrolling devices
+ like two-finger scrolling on touchpads do not have discrete
+ steps and do not generate this event.
+
+ The discrete value carries the directional information. e.g. a value
+ of -2 is two steps towards the negative direction of this axis.
+
+ The axis number is identical to the axis number in the associate
+ axis event.
+
+ The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+ not guaranteed.
+ </description>
+ <arg name="axis" type="uint" enum="axis" />
+ <arg name="discrete" type="int"/>
+ </event>
</interface>
- <interface name="wl_keyboard" version="4">
+ <interface name="wl_keyboard" version="5">
<description summary="keyboard input device">
The wl_keyboard interface represents one or more keyboards
associated with a seat.
@@ -1593,7 +2095,7 @@
This event provides a file descriptor to the client which can be
memory-mapped to provide a keyboard mapping description.
</description>
- <arg name="format" type="uint"/>
+ <arg name="format" type="uint" enum="keymap_format"/>
<arg name="fd" type="fd"/>
<arg name="size" type="uint"/>
</event>
@@ -1638,7 +2140,7 @@
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="key" type="uint"/>
- <arg name="state" type="uint"/>
+ <arg name="state" type="uint" enum="key_state"/>
</event>
<event name="modifiers">
@@ -1685,7 +2187,7 @@
</event>
</interface>
- <interface name="wl_touch" version="3">
+ <interface name="wl_touch" version="5">
<description summary="touchscreen input device">
The wl_touch interface represents a touchscreen
associated with a seat.
@@ -1819,17 +2321,17 @@
summary="width in millimeters of the output"/>
<arg name="physical_height" type="int"
summary="height in millimeters of the output"/>
- <arg name="subpixel" type="int"
+ <arg name="subpixel" type="int" enum="subpixel"
summary="subpixel orientation of the output"/>
<arg name="make" type="string"
summary="textual description of the manufacturer"/>
<arg name="model" type="string"
summary="textual description of the model"/>
- <arg name="transform" type="int"
+ <arg name="transform" type="int" enum="transform"
summary="transform that maps framebuffer to output"/>
</event>
- <enum name="mode">
+ <enum name="mode" bitfield="true">
<description summary="mode information">
These flags describe properties of an output mode.
They are used in the flags bitfield of the mode event.
@@ -1856,7 +2358,7 @@
the output may be scaled, as described in wl_output.scale,
or transformed , as described in wl_output.transform.
</description>
- <arg name="flags" type="uint" summary="bitfield of mode flags"/>
+ <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"/>
<arg name="height" type="int" summary="height of the mode in hardware units"/>
<arg name="refresh" type="int" summary="vertical refresh rate in mHz"/>
diff --git a/src/connection.c b/src/connection.c
index b3d9bd4..65b64e9 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -33,7 +33,6 @@
#include <stdio.h>
#include <errno.h>
#include <sys/uio.h>
-#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
@@ -164,10 +163,10 @@ wl_connection_create(int fd)
{
struct wl_connection *connection;
- connection = malloc(sizeof *connection);
+ connection = zalloc(sizeof *connection);
if (connection == NULL)
return NULL;
- memset(connection, 0, sizeof *connection);
+
connection->fd = fd;
return connection;
@@ -315,6 +314,12 @@ wl_connection_flush(struct wl_connection *connection)
return connection->out.head - tail;
}
+uint32_t
+wl_connection_pending_input(struct wl_connection *connection)
+{
+ return wl_buffer_size(&connection->in);
+}
+
int
wl_connection_read(struct wl_connection *connection)
{
@@ -351,7 +356,7 @@ wl_connection_read(struct wl_connection *connection)
connection->in.head += len;
- return connection->in.head - connection->in.tail;
+ return wl_connection_pending_input(connection);
}
int
@@ -400,6 +405,12 @@ wl_message_count_arrays(const struct wl_message *message)
return arrays;
}
+int
+wl_connection_get_fd(struct wl_connection *connection)
+{
+ return connection->fd;
+}
+
static int
wl_connection_put_fd(struct wl_connection *connection, int32_t fd)
{
@@ -569,16 +580,12 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode,
case 'h':
fd = args[i].h;
dup_fd = wl_os_dupfd_cloexec(fd, 0);
- if (dup_fd < 0) {
- wl_log("dup failed: %m");
- abort();
- }
+ if (dup_fd < 0)
+ wl_abort("dup failed: %s\n", strerror(errno));
closure->args[i].h = dup_fd;
break;
default:
- wl_log("unhandled format code: '%c'\n",
- arg.type);
- assert(0);
+ wl_abort("unhandled format code: '%c'\n", arg.type);
break;
}
}
@@ -771,8 +778,7 @@ wl_connection_demarshal(struct wl_connection *connection,
closure->args[i].h = fd;
break;
default:
- wl_log("unknown type\n");
- assert(0);
+ wl_abort("unknown type\n");
break;
}
}
@@ -906,8 +912,7 @@ convert_arguments_to_ffi(const char *signature, uint32_t flags,
ffi_args[i] = &args[i].h;
break;
default:
- wl_log("unknown type\n");
- assert(0);
+ wl_abort("unknown type\n");
break;
}
}
@@ -938,9 +943,8 @@ wl_closure_invoke(struct wl_closure *closure, uint32_t flags,
implementation = target->implementation;
if (!implementation[opcode]) {
- wl_log("listener function for opcode %u of %s is NULL\n",
- opcode, target->interface->name);
- abort();
+ wl_abort("listener function for opcode %u of %s is NULL\n",
+ opcode, target->interface->name);
}
ffi_call(&cif, implementation[opcode], NULL, ffi_args);
}
diff --git a/src/dtddata.S b/src/dtddata.S
new file mode 100644
index 0000000..68e3435
--- /dev/null
+++ b/src/dtddata.S
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2015 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* from: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967#comment-348129 */
+
+.macro binfile name file
+ .p2align 2
+ .globl \name\()_begin
+\name\()_begin:
+ .incbin "\file"
+\name\()_end:
+ .byte 0
+ .p2align 2
+ .globl \name\()_len
+\name\()_len:
+ .int (\name\()_end - \name\()_begin)
+.endm
+
+.section .rodata
+binfile DTD_DATA src/wayland.dtd.embed
diff --git a/src/event-loop.c b/src/event-loop.c
index 130c7be..ea27b69 100644
--- a/src/event-loop.c
+++ b/src/event-loop.c
@@ -36,7 +36,6 @@
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <unistd.h>
-#include <assert.h>
#include "wayland-private.h"
#include "wayland-server.h"
#include "wayland-os.h"
diff --git a/src/scanner.c b/src/scanner.c
index 9b41ae4..1d626f4 100644
--- a/src/scanner.c
+++ b/src/scanner.c
@@ -25,6 +25,8 @@
* SOFTWARE.
*/
+#include "config.h"
+
#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
@@ -34,9 +36,18 @@
#include <expat.h>
#include <getopt.h>
#include <limits.h>
+#include <unistd.h>
+
+#if HAVE_LIBXML
+#include <libxml/parser.h>
+#endif
#include "wayland-util.h"
+/* Embedded wayland.dtd file, see dtddata.S */
+extern char DTD_DATA_begin;
+extern int DTD_DATA_len;
+
enum side {
CLIENT,
SERVER,
@@ -59,6 +70,57 @@ usage(int ret)
exit(ret);
}
+static bool
+is_dtd_valid(FILE *input)
+{
+ bool rc = true;
+#if HAVE_LIBXML
+ xmlParserCtxtPtr ctx = NULL;
+ xmlDocPtr doc = NULL;
+ xmlDtdPtr dtd = NULL;
+ xmlValidCtxtPtr dtdctx;
+ xmlParserInputBufferPtr buffer;
+ int fd = fileno(input);
+
+ dtdctx = xmlNewValidCtxt();
+ ctx = xmlNewParserCtxt();
+ if (!ctx || !dtdctx)
+ abort();
+
+ buffer = xmlParserInputBufferCreateMem(&DTD_DATA_begin,
+ DTD_DATA_len,
+ XML_CHAR_ENCODING_UTF8);
+ if (!buffer) {
+ fprintf(stderr, "Failed to init buffer for DTD.\n");
+ abort();
+ }
+
+ dtd = xmlIOParseDTD(NULL, buffer, XML_CHAR_ENCODING_UTF8);
+ if (!dtd) {
+ fprintf(stderr, "Failed to parse DTD.\n");
+ abort();
+ }
+
+ doc = xmlCtxtReadFd(ctx, fd, "protocol", NULL, 0);
+ if (!doc) {
+ fprintf(stderr, "Failed to read XML\n");
+ abort();
+ }
+
+ rc = xmlValidateDtd(dtdctx, doc, dtd);
+ xmlFreeDoc(doc);
+ xmlFreeParserCtxt(ctx);
+ xmlFreeValidCtxt(dtdctx);
+ /* xmlIOParseDTD consumes buffer */
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ fprintf(stderr, "Failed to reset fd, output would be garbage.\n");
+ abort();
+ }
+#endif
+ return rc;
+}
+
#define XML_BUFFER_SIZE 4096
struct location {
@@ -128,6 +190,7 @@ struct arg {
char *interface_name;
struct wl_list link;
char *summary;
+ char *enumeration_name;
};
struct enumeration {
@@ -136,6 +199,7 @@ struct enumeration {
struct wl_list entry_list;
struct wl_list link;
struct description *description;
+ bool bitfield;
};
struct entry {
@@ -540,16 +604,19 @@ start_element(void *data, const char *element_name, const char **atts)
const char *summary = NULL;
const char *since = NULL;
const char *allow_null = NULL;
+ const char *enumeration_name = NULL;
+ const char *bitfield = NULL;
int i, version = 0;
ctx->loc.line_number = XML_GetCurrentLineNumber(ctx->parser);
for (i = 0; atts[i]; i += 2) {
if (strcmp(atts[i], "name") == 0)
name = atts[i + 1];
- if (strcmp(atts[i], "version") == 0)
+ if (strcmp(atts[i], "version") == 0) {
version = strtouint(atts[i + 1]);
if (version == -1)
fail(&ctx->loc, "wrong version (%s)", atts[i + 1]);
+ }
if (strcmp(atts[i], "type") == 0)
type = atts[i + 1];
if (strcmp(atts[i], "value") == 0)
@@ -562,6 +629,10 @@ start_element(void *data, const char *element_name, const char **atts)
since = atts[i + 1];
if (strcmp(atts[i], "allow-null") == 0)
allow_null = atts[i + 1];
+ if (strcmp(atts[i], "enum") == 0)
+ enumeration_name = atts[i + 1];
+ if (strcmp(atts[i], "bitfield") == 0)
+ bitfield = atts[i + 1];
}
ctx->character_data_length = 0;
@@ -603,8 +674,12 @@ start_element(void *data, const char *element_name, const char **atts)
if (since != NULL) {
version = strtouint(since);
- if (version == -1)
+ if (version == -1) {
fail(&ctx->loc, "invalid integer (%s)\n", since);
+ } else if (version > ctx->interface->version) {
+ fail(&ctx->loc, "since (%u) larger than version (%u)\n",
+ version, ctx->interface->version);
+ }
} else {
version = 1;
}
@@ -655,6 +730,11 @@ start_element(void *data, const char *element_name, const char **atts)
"allow-null is only valid for objects, strings, and arrays");
}
+ if (enumeration_name == NULL || strcmp(enumeration_name, "") == 0)
+ arg->enumeration_name = NULL;
+ else
+ arg->enumeration_name = xstrdup(enumeration_name);
+
if (summary)
arg->summary = xstrdup(summary);
@@ -665,6 +745,16 @@ start_element(void *data, const char *element_name, const char **atts)
fail(&ctx->loc, "no enum name given");
enumeration = create_enumeration(name);
+
+ if (bitfield == NULL || strcmp(bitfield, "false") == 0)
+ enumeration->bitfield = false;
+ else if (strcmp(bitfield, "true") == 0)
+ enumeration->bitfield = true;
+ else
+ fail(&ctx->loc,
+ "invalid value (%s) for bitfield attribute (only true/false are accepted)",
+ bitfield);
+
wl_list_insert(ctx->interface->enumeration_list.prev,
&enumeration->link);
@@ -701,6 +791,46 @@ start_element(void *data, const char *element_name, const char **atts)
}
static void
+verify_arguments(struct parse_context *ctx, struct wl_list *messages, struct wl_list *enumerations)
+{
+ struct message *m;
+ wl_list_for_each(m, messages, link) {
+ struct arg *a;
+ wl_list_for_each(a, &m->arg_list, link) {
+ struct enumeration *e, *f;
+
+ if (!a->enumeration_name)
+ continue;
+
+ f = NULL;
+ wl_list_for_each(e, enumerations, link) {
+ if(strcmp(e->name, a->enumeration_name) == 0)
+ f = e;
+ }
+
+ if (f == NULL)
+ fail(&ctx->loc,
+ "could not find enumeration %s",
+ a->enumeration_name);
+
+ switch (a->type) {
+ case INT:
+ if (f->bitfield)
+ fail(&ctx->loc,
+ "bitfield-style enum must only be referenced by uint");
+ break;
+ case UNSIGNED:
+ break;
+ default:
+ fail(&ctx->loc,
+ "enumeration-style argument has wrong type");
+ }
+ }
+ }
+
+}
+
+static void
end_element(void *data, const XML_Char *name)
{
struct parse_context *ctx = data;
@@ -723,6 +853,12 @@ end_element(void *data, const XML_Char *name)
ctx->enumeration->name);
}
ctx->enumeration = NULL;
+ } else if (strcmp(name, "interface") == 0) {
+ struct interface *i = ctx->interface;
+
+ verify_arguments(ctx, &i->request_list, &i->enumeration_list);
+ verify_arguments(ctx, &i->event_list, &i->enumeration_list);
+
}
}
@@ -820,6 +956,14 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
interface->name, interface->name, interface->name,
interface->name);
+ printf("static inline uint32_t\n"
+ "%s_get_version(struct %s *%s)\n"
+ "{\n"
+ "\treturn wl_proxy_get_version((struct wl_proxy *) %s);\n"
+ "}\n\n",
+ interface->name, interface->name, interface->name,
+ interface->name);
+
has_destructor = 0;
has_destroy = 0;
wl_list_for_each(m, message_list, link) {
@@ -891,21 +1035,31 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
printf(")\n"
"{\n");
- if (ret) {
+ if (ret && ret->interface_name == NULL) {
+ /* an arg has type ="new_id" but interface is not
+ * provided, such as in wl_registry.bind */
printf("\tstruct wl_proxy *%s;\n\n"
- "\t%s = wl_proxy_marshal_constructor("
+ "\t%s = wl_proxy_marshal_constructor_versioned("
"(struct wl_proxy *) %s,\n"
- "\t\t\t %s_%s, ",
+ "\t\t\t %s_%s, interface, version",
ret->name, ret->name,
interface->name,
interface->uppercase_name,
m->uppercase_name);
-
- if (ret->interface_name == NULL)
- printf("interface");
- else
- printf("&%s_interface", ret->interface_name);
+ } else if (ret) {
+ /* Normal factory case, an arg has type="new_id" and
+ * an interface is provided */
+ printf("\tstruct wl_proxy *%s;\n\n"
+ "\t%s = wl_proxy_marshal_constructor("
+ "(struct wl_proxy *) %s,\n"
+ "\t\t\t %s_%s, &%s_interface",
+ ret->name, ret->name,
+ interface->name,
+ interface->uppercase_name,
+ m->uppercase_name,
+ ret->interface_name);
} else {
+ /* No args have type="new_id" */
printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n"
"\t\t\t %s_%s",
interface->name,
@@ -1141,8 +1295,9 @@ format_copyright(const char *copyright)
}
if (copyright[i] == '\n' || copyright[i] == '\0') {
- printf("%s %.*s\n",
+ printf("%s%s%.*s\n",
i == 0 ? "/*" : " *",
+ i > start ? " " : "",
i - start, copyright + start);
bol = 1;
}
@@ -1270,6 +1425,7 @@ emit_header(struct protocol *protocol, enum side side)
} else {
emit_structs(&i->event_list, i, side);
emit_opcodes(&i->request_list, i);
+ emit_opcode_versions(&i->request_list, i);
emit_stubs(&i->request_list, i);
}
@@ -1531,6 +1687,7 @@ int main(int argc, char *argv[])
if (freopen(argv[2], "w", stdout) == NULL) {
fprintf(stderr, "Could not open output file: %s\n",
strerror(errno));
+ fclose(input);
exit(EXIT_FAILURE);
}
}
@@ -1545,11 +1702,21 @@ int main(int argc, char *argv[])
ctx.protocol = &protocol;
ctx.loc.filename = "<stdin>";
+ if (!is_dtd_valid(input)) {
+ fprintf(stderr,
+ "*******************************************************\n"
+ "* *\n"
+ "* WARNING: XML failed validation against built-in DTD *\n"
+ "* *\n"
+ "*******************************************************\n");
+ }
+
/* create XML parser */
ctx.parser = XML_ParserCreate(NULL);
XML_SetUserData(ctx.parser, &ctx);
if (ctx.parser == NULL) {
fprintf(stderr, "failed to create parser\n");
+ fclose(input);
exit(EXIT_FAILURE);
}
@@ -1561,6 +1728,7 @@ int main(int argc, char *argv[])
len = fread(buf, 1, XML_BUFFER_SIZE, input);
if (len < 0) {
fprintf(stderr, "fread: %m\n");
+ fclose(input);
exit(EXIT_FAILURE);
}
if (XML_ParseBuffer(ctx.parser, len, len == 0) == 0) {
@@ -1569,6 +1737,7 @@ int main(int argc, char *argv[])
XML_GetCurrentLineNumber(ctx.parser),
XML_GetCurrentColumnNumber(ctx.parser),
XML_ErrorString(XML_GetErrorCode(ctx.parser)));
+ fclose(input);
exit(EXIT_FAILURE);
}
} while (len > 0);
@@ -1588,6 +1757,7 @@ int main(int argc, char *argv[])
}
free_protocol(&protocol);
+ fclose(input);
return 0;
}
diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h
index dea70d9..91f7e7b 100644
--- a/src/wayland-client-core.h
+++ b/src/wayland-client-core.h
@@ -103,8 +103,8 @@ struct wl_proxy;
* queue, reading all the data from the display fd. If the application
* would call \em poll(2) after that it would block, even though there
* might be events queued on the default queue. Those events should be
- * dispatched with \ref wl_display_dispatch_(queue_)pending() before
- * flushing and blocking.
+ * dispatched with \ref wl_display_dispatch_pending() or \ref
+ * wl_display_dispatch_queue_pending() before flushing and blocking.
*/
struct wl_display;
@@ -118,63 +118,138 @@ struct wl_display;
*/
struct wl_event_queue;
-void wl_event_queue_destroy(struct wl_event_queue *queue);
-
-void wl_proxy_marshal(struct wl_proxy *p, uint32_t opcode, ...);
-void wl_proxy_marshal_array(struct wl_proxy *p, uint32_t opcode,
- union wl_argument *args);
-struct wl_proxy *wl_proxy_create(struct wl_proxy *factory,
- const struct wl_interface *interface);
-struct wl_proxy *wl_proxy_marshal_constructor(struct wl_proxy *proxy,
- uint32_t opcode,
- const struct wl_interface *interface,
- ...);
+void
+wl_event_queue_destroy(struct wl_event_queue *queue);
+
+void
+wl_proxy_marshal(struct wl_proxy *p, uint32_t opcode, ...);
+
+void
+wl_proxy_marshal_array(struct wl_proxy *p, uint32_t opcode,
+ union wl_argument *args);
+
+struct wl_proxy *
+wl_proxy_create(struct wl_proxy *factory,
+ const struct wl_interface *interface);
+
+struct wl_proxy *
+wl_proxy_marshal_constructor(struct wl_proxy *proxy,
+ uint32_t opcode,
+ const struct wl_interface *interface,
+ ...);
+
+struct wl_proxy *
+wl_proxy_marshal_constructor_versioned(struct wl_proxy *proxy,
+ uint32_t opcode,
+ const struct wl_interface *interface,
+ uint32_t version,
+ ...);
struct wl_proxy *
wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,
uint32_t opcode, union wl_argument *args,
const struct wl_interface *interface);
+struct wl_proxy *
+wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
+ uint32_t opcode,
+ union wl_argument *args,
+ const struct wl_interface *interface,
+ uint32_t version);
-void wl_proxy_destroy(struct wl_proxy *proxy);
-int wl_proxy_add_listener(struct wl_proxy *proxy,
- void (**implementation)(void), void *data);
-const void *wl_proxy_get_listener(struct wl_proxy *proxy);
-int wl_proxy_add_dispatcher(struct wl_proxy *proxy,
- wl_dispatcher_func_t dispatcher_func,
- const void * dispatcher_data, void *data);
-void wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data);
-void *wl_proxy_get_user_data(struct wl_proxy *proxy);
-uint32_t wl_proxy_get_id(struct wl_proxy *proxy);
-const char *wl_proxy_get_class(struct wl_proxy *proxy);
-void wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue);
-
-struct wl_display *wl_display_connect(const char *name);
-struct wl_display *wl_display_connect_to_fd(int fd);
-void wl_display_disconnect(struct wl_display *display);
-int wl_display_get_fd(struct wl_display *display);
-int wl_display_dispatch(struct wl_display *display);
-int wl_display_dispatch_queue(struct wl_display *display,
- struct wl_event_queue *queue);
-int wl_display_dispatch_queue_pending(struct wl_display *display,
- struct wl_event_queue *queue);
-int wl_display_dispatch_pending(struct wl_display *display);
-int wl_display_get_error(struct wl_display *display);
-uint32_t wl_display_get_protocol_error(struct wl_display *display,
- const struct wl_interface **interface,
- uint32_t *id);
-
-int wl_display_flush(struct wl_display *display);
-int wl_display_roundtrip_queue(struct wl_display *display,
- struct wl_event_queue *queue);
-int wl_display_roundtrip(struct wl_display *display);
-struct wl_event_queue *wl_display_create_queue(struct wl_display *display);
-
-int wl_display_prepare_read_queue(struct wl_display *display,
+void
+wl_proxy_destroy(struct wl_proxy *proxy);
+
+int
+wl_proxy_add_listener(struct wl_proxy *proxy,
+ void (**implementation)(void), void *data);
+
+const void *
+wl_proxy_get_listener(struct wl_proxy *proxy);
+
+int
+wl_proxy_add_dispatcher(struct wl_proxy *proxy,
+ wl_dispatcher_func_t dispatcher_func,
+ const void * dispatcher_data, void *data);
+
+void
+wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data);
+
+void *
+wl_proxy_get_user_data(struct wl_proxy *proxy);
+
+uint32_t
+wl_proxy_get_version(struct wl_proxy *proxy);
+
+uint32_t
+wl_proxy_get_id(struct wl_proxy *proxy);
+
+const char *
+wl_proxy_get_class(struct wl_proxy *proxy);
+
+void
+wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue);
+
+struct wl_display *
+wl_display_connect(const char *name);
+
+struct wl_display *
+wl_display_connect_to_fd(int fd);
+
+void
+wl_display_disconnect(struct wl_display *display);
+
+int
+wl_display_get_fd(struct wl_display *display);
+
+int
+wl_display_dispatch(struct wl_display *display);
+
+int
+wl_display_dispatch_queue(struct wl_display *display,
+ struct wl_event_queue *queue);
+
+int
+wl_display_dispatch_queue_pending(struct wl_display *display,
struct wl_event_queue *queue);
-int wl_display_prepare_read(struct wl_display *display);
-void wl_display_cancel_read(struct wl_display *display);
-int wl_display_read_events(struct wl_display *display);
-void wl_log_set_handler_client(wl_log_func_t handler);
+int
+wl_display_dispatch_pending(struct wl_display *display);
+
+int
+wl_display_get_error(struct wl_display *display);
+
+uint32_t
+wl_display_get_protocol_error(struct wl_display *display,
+ const struct wl_interface **interface,
+ uint32_t *id);
+
+int
+wl_display_flush(struct wl_display *display);
+
+int
+wl_display_roundtrip_queue(struct wl_display *display,
+ struct wl_event_queue *queue);
+
+int
+wl_display_roundtrip(struct wl_display *display);
+
+struct wl_event_queue *
+wl_display_create_queue(struct wl_display *display);
+
+int
+wl_display_prepare_read_queue(struct wl_display *display,
+ struct wl_event_queue *queue);
+
+int
+wl_display_prepare_read(struct wl_display *display);
+
+void
+wl_display_cancel_read(struct wl_display *display);
+
+int
+wl_display_read_events(struct wl_display *display);
+
+void
+wl_log_set_handler_client(wl_log_func_t handler);
#ifdef __cplusplus
}
diff --git a/src/wayland-client.c b/src/wayland-client.c
index 09c594a..ef12bfc 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -62,6 +62,7 @@ struct wl_proxy {
int refcount;
void *user_data;
wl_dispatcher_func_t dispatcher;
+ uint32_t version;
};
struct wl_global {
@@ -157,7 +158,7 @@ display_fatal_error(struct wl_display *display, int error)
/**
* This function is called for error events
* and indicates that in some object an error occurred.
- * Difference between this function and display_fatal_error()
+ * The difference between this function and display_fatal_error()
* is that this one handles errors that will come by wire,
* whereas display_fatal_error() is called for local errors.
*
@@ -309,7 +310,7 @@ wl_event_queue_destroy(struct wl_event_queue *queue)
* \return A new event queue associated with this display or NULL on
* failure.
*
- * \memberof wl_event_queue
+ * \memberof wl_display
*/
WL_EXPORT struct wl_event_queue *
wl_display_create_queue(struct wl_display *display)
@@ -326,21 +327,21 @@ wl_display_create_queue(struct wl_display *display)
}
static struct wl_proxy *
-proxy_create(struct wl_proxy *factory, const struct wl_interface *interface)
+proxy_create(struct wl_proxy *factory, const struct wl_interface *interface,
+ uint32_t version)
{
struct wl_proxy *proxy;
struct wl_display *display = factory->display;
- proxy = malloc(sizeof *proxy);
+ proxy = zalloc(sizeof *proxy);
if (proxy == NULL)
return NULL;
- memset(proxy, 0, sizeof *proxy);
-
proxy->object.interface = interface;
proxy->display = display;
proxy->queue = factory->queue;
proxy->refcount = 1;
+ proxy->version = version;
proxy->object.id = wl_map_insert_new(&display->objects, 0, proxy);
@@ -373,7 +374,7 @@ wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface)
struct wl_proxy *proxy;
pthread_mutex_lock(&display->mutex);
- proxy = proxy_create(factory, interface);
+ proxy = proxy_create(factory, interface, factory->version);
pthread_mutex_unlock(&display->mutex);
return proxy;
@@ -387,17 +388,16 @@ wl_proxy_create_for_id(struct wl_proxy *factory,
struct wl_proxy *proxy;
struct wl_display *display = factory->display;
- proxy = malloc(sizeof *proxy);
+ proxy = zalloc(sizeof *proxy);
if (proxy == NULL)
return NULL;
- memset(proxy, 0, sizeof *proxy);
-
proxy->object.interface = interface;
proxy->object.id = id;
proxy->display = display;
proxy->queue = factory->queue;
proxy->refcount = 1;
+ proxy->version = factory->version;
wl_map_insert_at(&display->objects, 0, id, proxy);
@@ -515,7 +515,7 @@ wl_proxy_add_dispatcher(struct wl_proxy *proxy,
const void *implementation, void *data)
{
if (proxy->object.implementation || proxy->dispatcher) {
- wl_log("proxy %p already has listener\n");
+ wl_log("proxy %p already has listener\n", proxy);
return -1;
}
@@ -529,7 +529,7 @@ wl_proxy_add_dispatcher(struct wl_proxy *proxy,
static struct wl_proxy *
create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
union wl_argument *args,
- const struct wl_interface *interface)
+ const struct wl_interface *interface, uint32_t version)
{
int i, count;
const char *signature;
@@ -543,7 +543,7 @@ create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
switch (arg.type) {
case 'n':
- new_proxy = proxy_create(proxy, interface);
+ new_proxy = proxy_create(proxy, interface, version);
if (new_proxy == NULL)
return NULL;
@@ -562,13 +562,14 @@ create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
* \param args Extra arguments for the given request
* \param interface The interface to use for the new proxy
*
- * Translates the request given by opcode and the extra arguments into the
- * wire format and write it to the connection buffer. This version takes an
- * array of the union type wl_argument.
+ * This function translates a request given an opcode, an interface and a
+ * wl_argument array to the wire format and writes it to the connection
+ * buffer.
*
* For new-id arguments, this function will allocate a new wl_proxy
* and send the ID to the server. The new wl_proxy will be returned
- * on success or NULL on errror with errno set accordingly.
+ * on success or NULL on errror with errno set accordingly. The newly
+ * created proxy will inherit their version from their parent.
*
* \note This is intended to be used by language bindings and not in
* non-generated code.
@@ -582,6 +583,43 @@ wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,
uint32_t opcode, union wl_argument *args,
const struct wl_interface *interface)
{
+ return wl_proxy_marshal_array_constructor_versioned(proxy, opcode,
+ args, interface,
+ proxy->version);
+}
+
+
+/** Prepare a request to be sent to the compositor
+ *
+ * \param proxy The proxy object
+ * \param opcode Opcode of the request to be sent
+ * \param args Extra arguments for the given request
+ * \param interface The interface to use for the new proxy
+ * \param version The protocol object version for the new proxy
+ *
+ * Translates the request given by opcode and the extra arguments into the
+ * wire format and write it to the connection buffer. This version takes an
+ * array of the union type wl_argument.
+ *
+ * For new-id arguments, this function will allocate a new wl_proxy
+ * and send the ID to the server. The new wl_proxy will be returned
+ * on success or NULL on errror with errno set accordingly. The newly
+ * created proxy will have the version specified.
+ *
+ * \note This is intended to be used by language bindings and not in
+ * non-generated code.
+ *
+ * \sa wl_proxy_marshal()
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT struct wl_proxy *
+wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
+ uint32_t opcode,
+ union wl_argument *args,
+ const struct wl_interface *interface,
+ uint32_t version)
+{
struct wl_closure *closure;
struct wl_proxy *new_proxy = NULL;
const struct wl_message *message;
@@ -591,24 +629,21 @@ wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,
message = &proxy->object.interface->methods[opcode];
if (interface) {
new_proxy = create_outgoing_proxy(proxy, message,
- args, interface);
+ args, interface,
+ version);
if (new_proxy == NULL)
goto err_unlock;
}
closure = wl_closure_marshal(&proxy->object, opcode, args, message);
- if (closure == NULL) {
- wl_log("Error marshalling request: %m\n");
- abort();
- }
+ if (closure == NULL)
+ wl_abort("Error marshalling request: %s\n", strerror(errno));
if (debug_client)
wl_closure_print(closure, &proxy->object, true);
- if (wl_closure_send(closure, proxy->display->connection)) {
- wl_log("Error sending request: %m\n");
- abort();
- }
+ if (wl_closure_send(closure, proxy->display->connection))
+ wl_abort("Error sending request: %s\n", strerror(errno));
wl_closure_destroy(closure);
@@ -656,12 +691,15 @@ wl_proxy_marshal(struct wl_proxy *proxy, uint32_t opcode, ...)
* \param ... Extra arguments for the given request
* \return A new wl_proxy for the new_id argument or NULL on error
*
- * Translates the request given by opcode and the extra arguments into the
- * wire format and write it to the connection buffer.
+ * This function translates a request given an opcode, an interface and extra
+ * arguments to the wire format and writes it to the connection buffer. The
+ * types of the extra arguments must correspond to the argument types of the
+ * method associated with the opcode in the interface.
*
* For new-id arguments, this function will allocate a new wl_proxy
* and send the ID to the server. The new wl_proxy will be returned
- * on success or NULL on errror with errno set accordingly.
+ * on success or NULL on errror with errno set accordingly. The newly
+ * created proxy will inherit their version from their parent.
*
* \note This should not normally be used by non-generated code.
*
@@ -683,6 +721,46 @@ wl_proxy_marshal_constructor(struct wl_proxy *proxy, uint32_t opcode,
args, interface);
}
+
+/** Prepare a request to be sent to the compositor
+ *
+ * \param proxy The proxy object
+ * \param opcode Opcode of the request to be sent
+ * \param interface The interface to use for the new proxy
+ * \param version The protocol object version of the new proxy
+ * \param ... Extra arguments for the given request
+ * \return A new wl_proxy for the new_id argument or NULL on error
+ *
+ * Translates the request given by opcode and the extra arguments into the
+ * wire format and write it to the connection buffer.
+ *
+ * For new-id arguments, this function will allocate a new wl_proxy
+ * and send the ID to the server. The new wl_proxy will be returned
+ * on success or NULL on errror with errno set accordingly. The newly
+ * created proxy will have the version specified.
+ *
+ * \note This should not normally be used by non-generated code.
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT struct wl_proxy *
+wl_proxy_marshal_constructor_versioned(struct wl_proxy *proxy, uint32_t opcode,
+ const struct wl_interface *interface,
+ uint32_t version, ...)
+{
+ union wl_argument args[WL_CLOSURE_MAX_ARGS];
+ va_list ap;
+
+ va_start(ap, version);
+ wl_argument_from_va_list(proxy->object.interface->methods[opcode].signature,
+ args, WL_CLOSURE_MAX_ARGS, ap);
+ va_end(ap);
+
+ return wl_proxy_marshal_array_constructor_versioned(proxy, opcode,
+ args, interface,
+ version);
+}
+
/** Prepare a request to be sent to the compositor
*
* \param proxy The proxy object
@@ -819,14 +897,12 @@ wl_display_connect_to_fd(int fd)
if (debug && (strstr(debug, "client") || strstr(debug, "1")))
debug_client = 1;
- display = malloc(sizeof *display);
+ display = zalloc(sizeof *display);
if (display == NULL) {
close(fd);
return NULL;
}
- memset(display, 0, sizeof *display);
-
display->fd = fd;
wl_map_init(&display->objects, WL_MAP_CLIENT_SIDE);
wl_event_queue_init(&display->default_queue, display);
@@ -847,6 +923,25 @@ wl_display_connect_to_fd(int fd)
display->proxy.flags = 0;
display->proxy.refcount = 1;
+ /* We set this version to 0 for backwards compatibility.
+ *
+ * If a client is using old versions of protocol headers,
+ * it will use unversioned API to create proxies. Those
+ * proxies will inherit this 0.
+ *
+ * A client could be passing these proxies into library
+ * code newer than the headers that checks proxy
+ * versions. When the proxy version is reported as 0
+ * the library will know that it can't reliably determine
+ * the proxy version, and should do whatever fallback is
+ * required.
+ *
+ * This trick forces wl_display to always report 0, but
+ * since it's a special object that we can't bind
+ * specific versions of anyway, this should be fine.
+ */
+ display->proxy.version = 0;
+
display->connection = wl_connection_create(display->fd);
if (display->connection == NULL)
goto err_connection;
@@ -960,16 +1055,19 @@ static const struct wl_callback_listener sync_listener = {
* \param queue The queue on which to run the roundtrip
* \return The number of dispatched events on success or -1 on failure
*
- * Blocks until the server processes all currently issued requests and
- * sends out pending events on the event queue.
+ * This function blocks until the server has processed all currently issued
+ * requests by sending a request to the display server and waiting for a
+ * reply before returning.
+ *
+ * This function uses wl_display_dispatch_queue() internally. It is not allowed
+ * to call this function while the thread is being prepared for reading events,
+ * and doing so will cause a dead lock.
*
- * \note This function uses wl_display_dispatch_queue() internally. If you
- * are using wl_display_read_events() from more threads, don't use this function
- * (or make sure that calling wl_display_roundtrip_queue() doesn't interfere
- * with calling wl_display_prepare_read() and wl_display_read_events())
+ * \note This function may dispatch other events being received on the given
+ * queue.
*
* \sa wl_display_roundtrip()
- * \memberof wl_event_queue
+ * \memberof wl_display
*/
WL_EXPORT int
wl_display_roundtrip_queue(struct wl_display *display, struct wl_event_queue *queue)
@@ -997,13 +1095,16 @@ wl_display_roundtrip_queue(struct wl_display *display, struct wl_event_queue *qu
* \param display The display context object
* \return The number of dispatched events on success or -1 on failure
*
- * Blocks until the server process all currently issued requests and
- * sends out pending events on the default event queue.
+ * This function blocks until the server has processed all currently issued
+ * requests by sending a request to the display server and waiting for a reply
+ * before returning.
+ *
+ * This function uses wl_display_dispatch_queue() internally. It is not allowed
+ * to call this function while the thread is being prepared for reading events,
+ * and doing so will cause a dead lock.
*
- * \note This function uses wl_display_dispatch_queue() internally. If you
- * are using wl_display_read_events() from more threads, don't use this function
- * (or make sure that calling wl_display_roundtrip() doesn't interfere
- * with calling wl_display_prepare_read() and wl_display_read_events())
+ * \note This function may dispatch other events being received on the default
+ * queue.
*
* \memberof wl_display
*/
@@ -1248,58 +1349,29 @@ cancel_read(struct wl_display *display)
* \return 0 on success or -1 on error. In case of error errno will
* be set accordingly
*
- * This will read events from the file descriptor for the display.
- * This function does not dispatch events, it only reads and queues
- * events into their corresponding event queues. If no data is
- * available on the file descriptor, wl_display_read_events() returns
- * immediately. To dispatch events that may have been queued, call
- * wl_display_dispatch_pending() or wl_display_dispatch_queue_pending().
- *
- * Before calling this function, wl_display_prepare_read() must be
- * called first. When running in more threads (which is the usual
- * case, since we'd use wl_display_dispatch() otherwise), every thread
- * must call wl_display_prepare_read() before calling this function.
- *
- * After calling wl_display_prepare_read() there can be some extra code
- * before calling wl_display_read_events(), for example poll() or alike.
- * Example of code in a thread:
- *
- * \code
- *
- * while (wl_display_prepare_read(display) < 0)
- * wl_display_dispatch_pending(display);
- * wl_display_flush(display);
- *
- * ... some code ...
+ * Calling this function will result in data available on the display file
+ * descriptor being read and read events will be queued on their corresponding
+ * event queues.
*
- * fds[0].fd = wl_display_get_fd(display);
- * fds[0].events = POLLIN;
- * poll(fds, 1, -1);
+ * Before calling this function, depending on what thread it is to be called
+ * from, wl_display_prepare_read_queue() or wl_display_prepare_read() needs to
+ * be called. See wl_display_prepare_read_queue() for more details.
*
- * if (!everything_ok()) {
- * wl_display_cancel_read(display);
- * handle_error();
- * }
- *
- * if (wl_display_read_events(display) < 0)
- * handle_error();
- *
- * ...
- * \endcode
+ * When being called at a point where other threads have been prepared to read
+ * (using wl_display_prepare_read_queue() or wl_display_prepare_read()) this
+ * function will sleep until all other prepared threads have either been
+ * cancelled (using wl_display_cancel_read()) or them self entered this
+ * function. The last thread that calls this function will then read and queue
+ * events on their corresponding event queues, and finally wake up all other
+ * wl_display_read_events() calls causing them to return.
*
- * After wl_display_prepare_read() succeeds, other threads that enter
- * wl_display_read_events() will sleep until the very last thread enters
- * it too or cancels. Therefore when the display fd becomes (or already
- * is) readable, wl_display_read_events() should be called as soon as
- * possible to unblock all threads. If wl_display_read_events() will not
- * be called, then wl_display_cancel_read() must be called instead to let
- * the other threads continue.
+ * If a thread cancels a read preparation when all other threads that have
+ * prepared to read has either called wl_display_cancel_read() or
+ * wl_display_read_events(), all reader threads will return without having read
+ * any data.
*
- * This function must not be called simultaneously with wl_display_dispatch().
- * It may lead to deadlock. If programmer wants, for some reason, use
- * wl_display_dispatch() in one thread and wl_display_prepare_read() with
- * wl_display_read_events() in another, extra care must be taken to serialize
- * these calls, i. e. use mutexes or similar (on whole prepare + read sequence)
+ * To dispatch events that may have been queued, call
+ * wl_display_dispatch_pending() or wl_display_dispatch_queue_pending().
*
* \sa wl_display_prepare_read(), wl_display_cancel_read(),
* wl_display_dispatch_pending(), wl_display_dispatch()
@@ -1359,20 +1431,59 @@ err:
return -1;
}
-/** Prepare to read events from the display to this queue
+/** Prepare to read events from the display's file descriptor to a queue
*
* \param display The display context object
* \param queue The event queue to use
* \return 0 on success or -1 if event queue was not empty
*
- * Atomically makes sure the queue is empty and stops any other thread
- * from placing events into this (or any) queue. Caller must
- * eventually call either wl_display_cancel_read() or
- * wl_display_read_events(), usually after waiting for the
- * display fd to become ready for reading, to release the lock.
+ * This function (or wl_display_prepare_read()) must be called before reading
+ * from the file descriptor using wl_display_read_events(). Calling
+ * wl_display_prepare_read_queue() announces the calling thread's intention to
+ * read and ensures that until the thread is ready to read and calls
+ * wl_display_read_events(), no other thread will read from the file descriptor.
+ * This only succeeds if the event queue is empty, and if not -1 is returned and
+ * errno set to EAGAIN.
*
- * \sa wl_display_prepare_read
- * \memberof wl_event_queue
+ * If a thread successfully calls wl_display_prepare_read_queue(), it must
+ * either call wl_display_read_events() when it's ready or cancel the read
+ * intention by calling wl_display_cancel_read().
+ *
+ * Use this function before polling on the display fd or integrate the fd into a
+ * toolkit event loop in a race-free way. A correct usage would be (with most
+ * error checking left out):
+ *
+ * \code
+ * while (wl_display_prepare_read_queue(display, queue) != 0)
+ * wl_display_dispatch_queue_pending(display, queue);
+ * wl_display_flush(display);
+ *
+ * ret = poll(fds, nfds, -1);
+ * if (has_error(ret))
+ * wl_display_cancel_read(display);
+ * else
+ * wl_display_read_events(display);
+ *
+ * wl_display_dispatch_queue_pending(display, queue);
+ * \endcode
+ *
+ * Here we call wl_display_prepare_read_queue(), which ensures that between
+ * returning from that call and eventually calling wl_display_read_events(), no
+ * other thread will read from the fd and queue events in our queue. If the call
+ * to wl_display_prepare_read_queue() fails, we dispatch the pending events and
+ * try again until we're successful.
+ *
+ * The wl_display_prepare_read_queue() function doesn't acquire exclusive access
+ * to the display's fd. It only registers that the thread calling this function
+ * has intention to read from fd. When all registered readers call
+ * wl_display_read_events(), only one (at random) eventually reads and queues
+ * the events and the others are sleeping meanwhile. This way we avoid races and
+ * still can read from more threads.
+ *
+ * \sa wl_display_cancel_read(), wl_display_read_events(),
+ * wl_display_prepare_read()
+ *
+ * \memberof wl_display
*/
WL_EXPORT int
wl_display_prepare_read_queue(struct wl_display *display,
@@ -1400,79 +1511,10 @@ wl_display_prepare_read_queue(struct wl_display *display,
* \param display The display context object
* \return 0 on success or -1 if event queue was not empty
*
- * This function must be called before reading from the file
- * descriptor using wl_display_read_events(). Calling
- * wl_display_prepare_read() announces the calling thread's intention
- * to read and ensures that until the thread is ready to read and
- * calls wl_display_read_events(), no other thread will read from the
- * file descriptor. This only succeeds if the event queue is empty
- * though, and if there are undispatched events in the queue, -1 is
- * returned and errno set to EAGAIN.
- *
- * If a thread successfully calls wl_display_prepare_read(), it must
- * either call wl_display_read_events() when it's ready or cancel the
- * read intention by calling wl_display_cancel_read().
- *
- * Use this function before polling on the display fd or to integrate
- * the fd into a toolkit event loop in a race-free way.
- * A correct usage would be (we left out most of error checking):
- *
- * \code
- * while (wl_display_prepare_read(display) != 0)
- * wl_display_dispatch_pending(display);
- * wl_display_flush(display);
- *
- * ret = poll(fds, nfds, -1);
- * if (has_error(ret))
- * wl_display_cancel_read(display);
- * else
- * wl_display_read_events(display);
- *
- * wl_display_dispatch_pending(display);
- * \endcode
- *
- * Here we call wl_display_prepare_read(), which ensures that between
- * returning from that call and eventually calling
- * wl_display_read_events(), no other thread will read from the fd and
- * queue events in our queue. If the call to wl_display_prepare_read() fails,
- * we dispatch the pending events and try again until we're successful.
- *
- * When using wl_display_dispatch() we'd have something like:
- *
- * \code
- * wl_display_dispatch_pending(display);
- * wl_display_flush(display);
- * poll(fds, nfds, -1);
- * wl_display_dispatch(display);
- * \endcode
- *
- * This sequence in not thread-safe. The race is immediately after poll(),
- * where one thread could preempt and read events before the other thread calls
- * wl_display_dispatch(). This call now blocks and starves the other
- * fds in the event loop.
- *
- * Another race would be when using more event queues.
- * When one thread calls wl_display_dispatch(_queue)(), then it
- * reads all events from display's fd and queues them in appropriate
- * queues. Then it dispatches only its own queue and the other events
- * are sitting in their queues, waiting for dispatching. If that happens
- * before the other thread managed to call poll(), it will
- * block with events queued.
- *
- * wl_display_prepare_read() function doesn't acquire exclusive access
- * to the display's fd. It only registers that the thread calling this function
- * has intention to read from fd.
- * When all registered readers call wl_display_read_events(),
- * only one (at random) eventually reads and queues the events and the
- * others are sleeping meanwhile. This way we avoid races and still
- * can read from more threads.
- *
- * If the relevant queue is not the default queue, then
- * wl_display_prepare_read_queue() and wl_display_dispatch_queue_pending()
- * need to be used instead.
- *
- * \sa wl_display_cancel_read(), wl_display_read_events()
+ * This function does the same thing as wl_display_prepare_read_queue()
+ * with the default queue passed as the queue.
*
+ * \sa wl_display_prepare_read_queue
* \memberof wl_display
*/
WL_EXPORT int
@@ -1503,25 +1545,48 @@ wl_display_cancel_read(struct wl_display *display)
pthread_mutex_unlock(&display->mutex);
}
+static int
+wl_display_poll(struct wl_display *display, short int events)
+{
+ int ret;
+ struct pollfd pfd[1];
+
+ pfd[0].fd = display->fd;
+ pfd[0].events = events;
+ do {
+ ret = poll(pfd, 1, -1);
+ } while (ret == -1 && errno == EINTR);
+
+ return ret;
+}
+
/** Dispatch events in an event queue
*
* \param display The display context object
* \param queue The event queue to dispatch
* \return The number of dispatched events on success or -1 on failure
*
- * Dispatch all incoming events for objects assigned to the given
- * event queue. On failure -1 is returned and errno set appropriately.
+ * Dispatch events on the given event queue.
+ *
+ * If the given event queue is empty, this function blocks until there are
+ * events to be read from the display fd. Events are read and queued on
+ * the appropriate event queues. Finally, events on given event queue are
+ * dispatched. On failure -1 is returned and errno set appropriately.
+ *
+ * In a multi threaded enviroment, do not manually wait using poll() (or
+ * equivalent) before calling this function, as doing so might cause a dead
+ * lock. If external reliance on poll() (or equivalent) is required, see
+ * wl_display_prepare_read_queue() of how to do so.
*
- * The behaviour of this function is exactly the same as the behaviour of
- * wl_display_dispatch(), but it dispatches events on given queue,
- * not on the default queue.
+ * This function is thread safe as long as it dispatches the right queue on the
+ * right thread. It is also compatible with the multi thread event reading
+ * preparation API (see wl_display_prepare_read_queue()), and uses the
+ * equivalent functionality internally. It is not allowed to call this function
+ * while the thread is being prepared for reading events, and doing so will
+ * cause a dead lock.
*
- * This function blocks if there are no events to dispatch (if there are,
- * it only dispatches these events and returns immediately).
- * When this function returns after blocking, it means that it read events
- * from display's fd and queued them to appropriate queues.
- * If among the incoming events were some events assigned to the given queue,
- * they are dispatched by this moment.
+ * It can be used as a helper function to ease the procedure of reading and
+ * dispatching events.
*
* \note Since Wayland 1.5 the display has an extra queue
* for its own events (i. e. delete_id). This queue is dispatched always,
@@ -1529,72 +1594,48 @@ wl_display_cancel_read(struct wl_display *display)
* That means that this function can return non-0 value even when it
* haven't dispatched any event for the given queue.
*
- * This function has the same constrains for using in multi-threaded apps
- * as \ref wl_display_dispatch().
- *
* \sa wl_display_dispatch(), wl_display_dispatch_pending(),
- * wl_display_dispatch_queue_pending()
+ * wl_display_dispatch_queue_pending(), wl_display_prepare_read_queue()
*
- * \memberof wl_event_queue
+ * \memberof wl_display
*/
WL_EXPORT int
wl_display_dispatch_queue(struct wl_display *display,
struct wl_event_queue *queue)
{
- struct pollfd pfd[2];
int ret;
- pthread_mutex_lock(&display->mutex);
+ if (wl_display_prepare_read_queue(display, queue) == -1)
+ return wl_display_dispatch_queue_pending(display, queue);
- ret = dispatch_queue(display, queue);
- if (ret == -1)
- goto err_unlock;
- if (ret > 0) {
- pthread_mutex_unlock(&display->mutex);
- return ret;
- }
+ while (true) {
+ ret = wl_display_flush(display);
- /* We ignore EPIPE here, so that we try to read events before
- * returning an error. When the compositor sends an error it
- * will close the socket, and if we bail out here we don't get
- * a chance to process the error. */
- ret = wl_connection_flush(display->connection);
- if (ret < 0 && errno != EAGAIN && errno != EPIPE) {
- display_fatal_error(display, errno);
- goto err_unlock;
- }
-
- display->reader_count++;
-
- pthread_mutex_unlock(&display->mutex);
+ if (ret != -1 || errno != EAGAIN)
+ break;
- pfd[0].fd = display->fd;
- pfd[0].events = POLLIN;
- do {
- ret = poll(pfd, 1, -1);
- } while (ret == -1 && errno == EINTR);
+ if (wl_display_poll(display, POLLOUT) == -1) {
+ wl_display_cancel_read(display);
+ return -1;
+ }
+ }
- if (ret == -1) {
+ /* Don't stop if flushing hits an EPIPE; continue so we can read any
+ * protocol error that may have triggered it. */
+ if (ret < 0 && errno != EPIPE) {
wl_display_cancel_read(display);
return -1;
}
- pthread_mutex_lock(&display->mutex);
-
- if (read_events(display) == -1)
- goto err_unlock;
-
- ret = dispatch_queue(display, queue);
- if (ret == -1)
- goto err_unlock;
-
- pthread_mutex_unlock(&display->mutex);
+ if (wl_display_poll(display, POLLIN) == -1) {
+ wl_display_cancel_read(display);
+ return -1;
+ }
- return ret;
+ if (wl_display_read_events(display) == -1)
+ return -1;
- err_unlock:
- pthread_mutex_unlock(&display->mutex);
- return -1;
+ return wl_display_dispatch_queue_pending(display, queue);
}
/** Dispatch pending events in an event queue
@@ -1607,7 +1648,7 @@ wl_display_dispatch_queue(struct wl_display *display,
* event queue. On failure -1 is returned and errno set appropriately.
* If there are no events queued, this function returns immediately.
*
- * \memberof wl_event_queue
+ * \memberof wl_display
* \since 1.0.2
*/
WL_EXPORT int
@@ -1630,17 +1671,24 @@ wl_display_dispatch_queue_pending(struct wl_display *display,
* \param display The display context object
* \return The number of dispatched events on success or -1 on failure
*
- * Dispatch the display's default event queue.
+ * Dispatch events on the default event queue.
*
* If the default event queue is empty, this function blocks until there are
* events to be read from the display fd. Events are read and queued on
* the appropriate event queues. Finally, events on the default event queue
- * are dispatched.
+ * are dispatched. On failure -1 is returned and errno set appropriately.
+ *
+ * In a multi threaded enviroment, do not manually wait using poll() (or
+ * equivalent) before calling this function, as doing so might cause a dead
+ * lock. If external reliance on poll() (or equivalent) is required, see
+ * wl_display_prepare_read_queue() of how to do so.
*
- * In multi-threaded environment, programmer may want to use
- * wl_display_read_events(). However, use of wl_display_read_events()
- * must not be mixed with wl_display_dispatch(). See wl_display_read_events()
- * and wl_display_prepare_read() for more details.
+ * This function is thread safe as long as it dispatches the right queue on the
+ * right thread. It is also compatible with the multi thread event reading
+ * preparation API (see wl_display_prepare_read_queue()), and uses the
+ * equivalent functionality internally. It is not allowed to call this function
+ * while the thread is being prepared for reading events, and doing so will
+ * cause a dead lock.
*
* \note It is not possible to check if there are events on the queue
* or not. For dispatching default queue events without blocking, see \ref
@@ -1666,28 +1714,6 @@ wl_display_dispatch(struct wl_display *display)
* attempt to read the display fd and simply returns zero if the main
* queue is empty, i.e., it doesn't block.
*
- * This is necessary when a client's main loop wakes up on some fd other
- * than the display fd (network socket, timer fd, etc) and calls \ref
- * wl_display_dispatch_queue() from that callback. This may queue up
- * events in other queues while reading all data from the display fd.
- * When the main loop returns from the handler, the display fd
- * no longer has data, causing a call to \em poll(2) (or similar
- * functions) to block indefinitely, even though there are events ready
- * to dispatch.
- *
- * To proper integrate the wayland display fd into a main loop, the
- * client should always call wl_display_dispatch_pending() and then
- * \ref wl_display_flush() prior to going back to sleep. At that point,
- * the fd typically doesn't have data so attempting I/O could block, but
- * events queued up on the default queue should be dispatched.
- *
- * A real-world example is a main loop that wakes up on a timerfd (or a
- * sound card fd becoming writable, for example in a video player), which
- * then triggers GL rendering and eventually eglSwapBuffers().
- * eglSwapBuffers() may call wl_display_dispatch_queue() if it didn't
- * receive the frame event for the previous frame, and as such queue
- * events in the default queue.
- *
* \sa wl_display_dispatch(), wl_display_dispatch_queue(),
* wl_display_flush()
*
@@ -1775,10 +1801,10 @@ wl_display_get_protocol_error(struct wl_display *display,
* \param display The display context object
* \return The number of bytes sent on success or -1 on failure
*
- * Send all buffered data on the client side to the server. Clients
- * should call this function before blocking. On success, the number
- * of bytes sent to the server is returned. On failure, this
- * function returns -1 and errno is set appropriately.
+ * Send all buffered data on the client side to the server. Clients should
+ * always call this function before blocking on input from the display fd.
+ * On success, the number of bytes sent to the server is returned. On
+ * failure, this function returns -1 and errno is set appropriately.
*
* wl_display_flush() never blocks. It will write as much data as
* possible, but if all data could not be written, errno will be set
@@ -1798,8 +1824,12 @@ wl_display_flush(struct wl_display *display)
errno = display->last_error;
ret = -1;
} else {
+ /* We don't make EPIPE a fatal error here, so that we may try to
+ * read events after the failed flush. When the compositor sends
+ * an error it will close the socket, and if we make EPIPE fatal
+ * here we don't get a chance to process the error. */
ret = wl_connection_flush(display->connection);
- if (ret < 0 && errno != EAGAIN)
+ if (ret < 0 && errno != EAGAIN && errno != EPIPE)
display_fatal_error(display, errno);
}
@@ -1837,6 +1867,28 @@ wl_proxy_get_user_data(struct wl_proxy *proxy)
return proxy->user_data;
}
+/** Get the protocol object version of a proxy object
+ *
+ * \param proxy The proxy object
+ * \return The protocol object version of the proxy or 0
+ *
+ * Gets the protocol object version of a proxy object, or 0
+ * if the proxy was created with unversioned API.
+ *
+ * A returned value of 0 means that no version information is
+ * available, so the caller must make safe assumptions about
+ * the object's real version.
+ *
+ * wl_display's version will always return 0.
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT uint32_t
+wl_proxy_get_version(struct wl_proxy *proxy)
+{
+ return proxy->version;
+}
+
/** Get the id of a proxy object
*
* \param proxy The proxy object
diff --git a/src/wayland-private.h b/src/wayland-private.h
index 17c507c..994bc45 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -29,6 +29,7 @@
#define WAYLAND_PRIVATE_H
#include <stdarg.h>
+#include <stdlib.h>
#define WL_HIDE_DEPRECATED 1
@@ -71,34 +72,72 @@ struct wl_map {
typedef void (*wl_iterator_func_t)(void *element, void *data);
-void wl_map_init(struct wl_map *map, uint32_t side);
-void wl_map_release(struct wl_map *map);
-uint32_t wl_map_insert_new(struct wl_map *map, uint32_t flags, void *data);
-int wl_map_insert_at(struct wl_map *map, uint32_t flags, uint32_t i, void *data);
-int wl_map_reserve_new(struct wl_map *map, uint32_t i);
-void wl_map_remove(struct wl_map *map, uint32_t i);
-void *wl_map_lookup(struct wl_map *map, uint32_t i);
-uint32_t wl_map_lookup_flags(struct wl_map *map, uint32_t i);
-void wl_map_for_each(struct wl_map *map, wl_iterator_func_t func, void *data);
+void
+wl_map_init(struct wl_map *map, uint32_t side);
+
+void
+wl_map_release(struct wl_map *map);
+
+uint32_t
+wl_map_insert_new(struct wl_map *map, uint32_t flags, void *data);
+
+int
+wl_map_insert_at(struct wl_map *map, uint32_t flags, uint32_t i, void *data);
+
+int
+wl_map_reserve_new(struct wl_map *map, uint32_t i);
+
+void
+wl_map_remove(struct wl_map *map, uint32_t i);
+
+void *
+wl_map_lookup(struct wl_map *map, uint32_t i);
+
+uint32_t
+wl_map_lookup_flags(struct wl_map *map, uint32_t i);
+
+void
+wl_map_for_each(struct wl_map *map, wl_iterator_func_t func, void *data);
struct wl_connection;
struct wl_closure;
struct wl_proxy;
-int wl_interface_equal(const struct wl_interface *iface1,
- const struct wl_interface *iface2);
+int
+wl_interface_equal(const struct wl_interface *iface1,
+ const struct wl_interface *iface2);
-struct wl_connection *wl_connection_create(int fd);
-int wl_connection_destroy(struct wl_connection *connection);
-void wl_connection_copy(struct wl_connection *connection, void *data, size_t size);
-void wl_connection_consume(struct wl_connection *connection, size_t size);
+struct wl_connection *
+wl_connection_create(int fd);
-int wl_connection_flush(struct wl_connection *connection);
-int wl_connection_read(struct wl_connection *connection);
+int
+wl_connection_destroy(struct wl_connection *connection);
+
+void
+wl_connection_copy(struct wl_connection *connection, void *data, size_t size);
-int wl_connection_write(struct wl_connection *connection, const void *data, size_t count);
-int wl_connection_queue(struct wl_connection *connection,
- const void *data, size_t count);
+void
+wl_connection_consume(struct wl_connection *connection, size_t size);
+
+int
+wl_connection_flush(struct wl_connection *connection);
+
+uint32_t
+wl_connection_pending_input(struct wl_connection *connection);
+
+int
+wl_connection_read(struct wl_connection *connection);
+
+int
+wl_connection_write(struct wl_connection *connection,
+ const void *data, size_t count);
+
+int
+wl_connection_queue(struct wl_connection *connection,
+ const void *data, size_t count);
+
+int
+wl_connection_get_fd(struct wl_connection *connection);
struct wl_closure {
int count;
@@ -133,6 +172,7 @@ struct wl_closure *
wl_closure_marshal(struct wl_object *sender,
uint32_t opcode, union wl_argument *args,
const struct wl_message *message);
+
struct wl_closure *
wl_closure_vmarshal(struct wl_object *sender,
uint32_t opcode, va_list ap,
@@ -155,25 +195,38 @@ enum wl_closure_invoke_flag {
void
wl_closure_invoke(struct wl_closure *closure, uint32_t flags,
struct wl_object *target, uint32_t opcode, void *data);
+
void
wl_closure_dispatch(struct wl_closure *closure, wl_dispatcher_func_t dispatcher,
struct wl_object *target, uint32_t opcode);
+
int
wl_closure_send(struct wl_closure *closure, struct wl_connection *connection);
+
int
wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection);
+
void
-wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send);
+wl_closure_print(struct wl_closure *closure,
+ struct wl_object *target, int send);
+
void
wl_closure_destroy(struct wl_closure *closure);
extern wl_log_func_t wl_log_handler;
void wl_log(const char *fmt, ...);
+void wl_abort(const char *fmt, ...);
struct wl_display;
struct wl_array *
wl_display_get_additional_shm_formats(struct wl_display *display);
+static inline void *
+zalloc(size_t s)
+{
+ return calloc(1, s);
+}
+
#endif
diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h
index e605432..e8e1e9c 100644
--- a/src/wayland-server-core.h
+++ b/src/wayland-server-core.h
@@ -49,34 +49,56 @@ typedef int (*wl_event_loop_timer_func_t)(void *data);
typedef int (*wl_event_loop_signal_func_t)(int signal_number, void *data);
typedef void (*wl_event_loop_idle_func_t)(void *data);
-struct wl_event_loop *wl_event_loop_create(void);
-void wl_event_loop_destroy(struct wl_event_loop *loop);
-struct wl_event_source *wl_event_loop_add_fd(struct wl_event_loop *loop,
- int fd, uint32_t mask,
- wl_event_loop_fd_func_t func,
- void *data);
-int wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask);
-struct wl_event_source *wl_event_loop_add_timer(struct wl_event_loop *loop,
- wl_event_loop_timer_func_t func,
- void *data);
+struct wl_event_loop *
+wl_event_loop_create(void);
+
+void
+wl_event_loop_destroy(struct wl_event_loop *loop);
+
+struct wl_event_source *
+wl_event_loop_add_fd(struct wl_event_loop *loop,
+ int fd, uint32_t mask,
+ wl_event_loop_fd_func_t func,
+ void *data);
+
+int
+wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask);
+
+struct wl_event_source *
+wl_event_loop_add_timer(struct wl_event_loop *loop,
+ wl_event_loop_timer_func_t func,
+ void *data);
+
struct wl_event_source *
wl_event_loop_add_signal(struct wl_event_loop *loop,
int signal_number,
wl_event_loop_signal_func_t func,
void *data);
-int wl_event_source_timer_update(struct wl_event_source *source,
- int ms_delay);
-int wl_event_source_remove(struct wl_event_source *source);
-void wl_event_source_check(struct wl_event_source *source);
+int
+wl_event_source_timer_update(struct wl_event_source *source,
+ int ms_delay);
+int
+wl_event_source_remove(struct wl_event_source *source);
-int wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout);
-void wl_event_loop_dispatch_idle(struct wl_event_loop *loop);
-struct wl_event_source *wl_event_loop_add_idle(struct wl_event_loop *loop,
- wl_event_loop_idle_func_t func,
- void *data);
-int wl_event_loop_get_fd(struct wl_event_loop *loop);
+void
+wl_event_source_check(struct wl_event_source *source);
+
+
+int
+wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout);
+
+void
+wl_event_loop_dispatch_idle(struct wl_event_loop *loop);
+
+struct wl_event_source *
+wl_event_loop_add_idle(struct wl_event_loop *loop,
+ wl_event_loop_idle_func_t func,
+ void *data);
+
+int
+wl_event_loop_get_fd(struct wl_event_loop *loop);
struct wl_client;
struct wl_display;
@@ -85,51 +107,95 @@ struct wl_resource;
struct wl_global;
typedef void (*wl_notify_func_t)(struct wl_listener *listener, void *data);
-void wl_event_loop_add_destroy_listener(struct wl_event_loop *loop,
- struct wl_listener * listener);
-struct wl_listener *wl_event_loop_get_destroy_listener(
- struct wl_event_loop *loop,
- wl_notify_func_t notify);
-
-struct wl_display *wl_display_create(void);
-void wl_display_destroy(struct wl_display *display);
-struct wl_event_loop *wl_display_get_event_loop(struct wl_display *display);
-int wl_display_add_socket(struct wl_display *display, const char *name);
-const char *wl_display_add_socket_auto(struct wl_display *display);
-void wl_display_terminate(struct wl_display *display);
-void wl_display_run(struct wl_display *display);
-void wl_display_flush_clients(struct wl_display *display);
+void
+wl_event_loop_add_destroy_listener(struct wl_event_loop *loop,
+ struct wl_listener * listener);
+
+struct wl_listener *
+wl_event_loop_get_destroy_listener(
+ struct wl_event_loop *loop,
+ wl_notify_func_t notify);
+
+struct wl_display *
+wl_display_create(void);
+
+void
+wl_display_destroy(struct wl_display *display);
+
+struct wl_event_loop *
+wl_display_get_event_loop(struct wl_display *display);
+
+int
+wl_display_add_socket(struct wl_display *display, const char *name);
+
+const char *
+wl_display_add_socket_auto(struct wl_display *display);
+
+int
+wl_display_add_socket_fd(struct wl_display *display, int sock_fd);
+
+void
+wl_display_terminate(struct wl_display *display);
+
+void
+wl_display_run(struct wl_display *display);
+
+void
+wl_display_flush_clients(struct wl_display *display);
typedef void (*wl_global_bind_func_t)(struct wl_client *client, void *data,
uint32_t version, uint32_t id);
-uint32_t wl_display_get_serial(struct wl_display *display);
-uint32_t wl_display_next_serial(struct wl_display *display);
+uint32_t
+wl_display_get_serial(struct wl_display *display);
+
+uint32_t
+wl_display_next_serial(struct wl_display *display);
+
+void
+wl_display_add_destroy_listener(struct wl_display *display,
+ struct wl_listener *listener);
+
+struct wl_listener *
+wl_display_get_destroy_listener(struct wl_display *display,
+ wl_notify_func_t notify);
+
+struct wl_global *
+wl_global_create(struct wl_display *display,
+ const struct wl_interface *interface,
+ int version,
+ void *data, wl_global_bind_func_t bind);
+
+void
+wl_global_destroy(struct wl_global *global);
+
+struct wl_client *
+wl_client_create(struct wl_display *display, int fd);
+
+void
+wl_client_destroy(struct wl_client *client);
+
+void
+wl_client_flush(struct wl_client *client);
-void wl_display_add_destroy_listener(struct wl_display *display,
- struct wl_listener *listener);
-struct wl_listener *wl_display_get_destroy_listener(struct wl_display *display,
- wl_notify_func_t notify);
+void
+wl_client_get_credentials(struct wl_client *client,
+ pid_t *pid, uid_t *uid, gid_t *gid);
-struct wl_global *wl_global_create(struct wl_display *display,
- const struct wl_interface *interface,
- int version,
- void *data, wl_global_bind_func_t bind);
-void wl_global_destroy(struct wl_global *global);
+int
+wl_client_get_fd(struct wl_client *client);
-struct wl_client *wl_client_create(struct wl_display *display, int fd);
-void wl_client_destroy(struct wl_client *client);
-void wl_client_flush(struct wl_client *client);
-void wl_client_get_credentials(struct wl_client *client,
- pid_t *pid, uid_t *uid, gid_t *gid);
+void
+wl_client_add_destroy_listener(struct wl_client *client,
+ struct wl_listener *listener);
-void wl_client_add_destroy_listener(struct wl_client *client,
- struct wl_listener *listener);
-struct wl_listener *wl_client_get_destroy_listener(struct wl_client *client,
- wl_notify_func_t notify);
+struct wl_listener *
+wl_client_get_destroy_listener(struct wl_client *client,
+ wl_notify_func_t notify);
struct wl_resource *
wl_client_get_object(struct wl_client *client, uint32_t id);
+
void
wl_client_post_no_memory(struct wl_client *client);
@@ -273,7 +339,7 @@ typedef void (*wl_resource_destroy_func_t)(struct wl_resource *resource);
* parameters, in the order they appear in the protocol XML specification.
*
* The variable arguments' types are:
- * - type=uint: uint32_t
+ * - type=uint: uint32_t
* - type=int: int32_t
* - type=fixed: wl_fixed_t
* - type=string: (const char *) to a nil-terminated string
@@ -282,19 +348,27 @@ typedef void (*wl_resource_destroy_func_t)(struct wl_resource *resource);
* - type=new_id: (struct wl_object *) or (struct wl_resource *)
* - type=object: (struct wl_object *) or (struct wl_resource *)
*/
-void wl_resource_post_event(struct wl_resource *resource,
- uint32_t opcode, ...);
-void wl_resource_post_event_array(struct wl_resource *resource,
- uint32_t opcode, union wl_argument *args);
-void wl_resource_queue_event(struct wl_resource *resource,
- uint32_t opcode, ...);
+void
+wl_resource_post_event(struct wl_resource *resource,
+ uint32_t opcode, ...);
+
+void
+wl_resource_post_event_array(struct wl_resource *resource,
+ uint32_t opcode, union wl_argument *args);
+
+void
+wl_resource_queue_event(struct wl_resource *resource,
+ uint32_t opcode, ...);
+
void wl_resource_queue_event_array(struct wl_resource *resource,
uint32_t opcode, union wl_argument *args);
/* msg is a printf format string, variable args are its args. */
-void wl_resource_post_error(struct wl_resource *resource,
- uint32_t code, const char *msg, ...)
+void
+wl_resource_post_error(struct wl_resource *resource,
+ uint32_t code, const char *msg, ...)
__attribute__ ((format (printf, 3, 4)));
+
void wl_resource_post_no_memory(struct wl_resource *resource);
struct wl_display *
@@ -362,6 +436,7 @@ wl_resource_get_destroy_listener(struct wl_resource *resource,
resource = tmp, \
tmp = wl_resource_from_link(wl_resource_get_link(resource)->next))
+struct wl_shm_pool;
struct wl_shm_buffer;
void
@@ -388,6 +463,12 @@ wl_shm_buffer_get_width(struct wl_shm_buffer *buffer);
int32_t
wl_shm_buffer_get_height(struct wl_shm_buffer *buffer);
+struct wl_shm_pool *
+wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer);
+
+void
+wl_shm_pool_unref(struct wl_shm_pool *pool);
+
int
wl_display_init_shm(struct wl_display *display);
@@ -397,7 +478,7 @@ wl_display_add_shm_format(struct wl_display *display, uint32_t format);
struct wl_shm_buffer *
wl_shm_buffer_create(struct wl_client *client,
uint32_t id, int32_t width, int32_t height,
- int32_t stride, uint32_t format);
+ int32_t stride, uint32_t format) WL_DEPRECATED;
void wl_log_set_handler_server(wl_log_func_t handler);
diff --git a/src/wayland-server.c b/src/wayland-server.c
index 0f04f66..6654cd7 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -42,7 +42,6 @@
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
-#include <ffi.h>
#include "wayland-private.h"
#include "wayland-server.h"
@@ -314,7 +313,6 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
closure = wl_connection_demarshal(client->connection, size,
&client->objects, message);
- len -= size;
if (closure == NULL && errno == ENOMEM) {
wl_resource_post_no_memory(resource);
@@ -347,6 +345,8 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
if (client->error)
break;
+
+ len = wl_connection_pending_input(connection);
}
if (client->error)
@@ -416,11 +416,10 @@ wl_client_create(struct wl_display *display, int fd)
struct wl_client *client;
socklen_t len;
- client = malloc(sizeof *client);
+ client = zalloc(sizeof *client);
if (client == NULL)
return NULL;
- memset(client, 0, sizeof *client);
client->display = display;
client->source = wl_event_loop_add_fd(display->loop, fd,
WL_EVENT_READABLE,
@@ -492,6 +491,41 @@ wl_client_get_credentials(struct wl_client *client,
*gid = client->ucred.gid;
}
+/** Get the file descriptor for the client
+ *
+ * \param client The display object
+ * \return The file descriptor to use for the connection
+ *
+ * This function returns the file descriptor for the given client.
+ *
+ * Be sure to use the file descriptor from the client for inspection only.
+ * If the caller does anything to the file descriptor that changes its state,
+ * it will likely cause problems.
+ *
+ * See also wl_client_get_credentials().
+ * It is recommended that you evaluate whether wl_client_get_credentials()
+ * can be applied to your use case instead of this function.
+ *
+ * If you would like to distinguish just between the client and the compositor
+ * itself from the client's request, it can be done by getting the client
+ * credentials and by checking the PID of the client and the compositor's PID.
+ * Regarding the case in which the socketpair() is being used, you need to be
+ * careful. Please note the documentation for wl_client_get_credentials().
+ *
+ * This function can be used for a compositor to validate a request from
+ * a client if there are additional information provided from the client's
+ * file descriptor. For instance, suppose you can get the security contexts
+ * from the client's file descriptor. The compositor can validate the client's
+ * request with the contexts and make a decision whether it permits or deny it.
+ *
+ * \memberof wl_client
+ */
+WL_EXPORT int
+wl_client_get_fd(struct wl_client *client)
+{
+ return wl_connection_get_fd(client->connection);
+}
+
/** Look up an object in the client name space
*
* \param client The client object
@@ -780,7 +814,8 @@ bind_display(struct wl_client *client, struct wl_display *display)
client->display_resource =
wl_resource_create(client, &wl_display_interface, 1, 1);
if (client->display_resource == NULL) {
- wl_client_post_no_memory(client);
+ /* DON'T send no-memory error to client - it has no
+ * resource to which it could post the event */
return -1;
}
@@ -855,11 +890,10 @@ wl_socket_alloc(void)
{
struct wl_socket *s;
- s = malloc(sizeof *s);
+ s = zalloc(sizeof *s);
if (!s)
return NULL;
- memset(s, 0, sizeof *s);
s->fd = -1;
s->fd_lock = -1;
@@ -909,9 +943,17 @@ wl_global_create(struct wl_display *display,
struct wl_global *global;
struct wl_resource *resource;
- if (interface->version < version) {
- wl_log("wl_global_create: implemented version higher "
- "than interface version%m\n");
+ if (version < 1) {
+ wl_log("wl_global_create: failing to create interface "
+ "'%s' with version %d because it is less than 1\n",
+ interface->name, version);
+ return NULL;
+ }
+
+ if (version > interface->version) {
+ wl_log("wl_global_create: implemented version for '%s' "
+ "higher than interface version (%d > %d)\n",
+ interface->name, version, interface->version);
return NULL;
}
@@ -1199,6 +1241,49 @@ wl_display_add_socket_auto(struct wl_display *display)
return NULL;
}
+/** Add a socket with an existing fd to Wayland display for the clients to connect.
+ *
+ * \param display Wayland display to which the socket should be added.
+ * \param sock_fd The existing socket file descriptor to be used
+ * \return 0 if success. -1 if failed.
+ *
+ * The existing socket fd must already be created, opened, and locked.
+ * The fd must be properly set to CLOEXEC and bound to a socket file
+ * with both bind() and listen() already called.
+ *
+ * \memberof wl_display
+ */
+WL_EXPORT int
+wl_display_add_socket_fd(struct wl_display *display, int sock_fd)
+{
+ struct wl_socket *s;
+ struct stat buf;
+
+ /* Require a valid fd or fail */
+ if (sock_fd < 0 || fstat(sock_fd, &buf) < 0 || !S_ISSOCK(buf.st_mode)) {
+ return -1;
+ }
+
+ s = wl_socket_alloc();
+ if (s == NULL)
+ return -1;
+
+ /* Reuse the existing fd */
+ s->fd = sock_fd;
+
+ s->source = wl_event_loop_add_fd(display->loop, s->fd,
+ WL_EVENT_READABLE,
+ socket_data, display);
+ if (s->source == NULL) {
+ wl_log("failed to establish event source\n");
+ return -1;
+ }
+
+ wl_list_insert(display->socket_list.prev, &s->link);
+
+ return 0;
+}
+
/** Add a socket to Wayland display for the clients to connect.
*
* \param display Wayland display to which the socket should be added.
diff --git a/src/wayland-shm.c b/src/wayland-shm.c
index 5c419fa..8e7adc9 100644
--- a/src/wayland-shm.c
+++ b/src/wayland-shm.c
@@ -315,41 +315,6 @@ wl_display_init_shm(struct wl_display *display)
}
WL_EXPORT struct wl_shm_buffer *
-wl_shm_buffer_create(struct wl_client *client,
- uint32_t id, int32_t width, int32_t height,
- int32_t stride, uint32_t format)
-{
- struct wl_shm_buffer *buffer;
-
- if (!format_is_supported(client, format))
- return NULL;
-
- buffer = malloc(sizeof *buffer + stride * height);
- if (buffer == NULL)
- return NULL;
-
- buffer->width = width;
- buffer->height = height;
- buffer->format = format;
- buffer->stride = stride;
- buffer->offset = 0;
- buffer->pool = NULL;
-
- buffer->resource =
- wl_resource_create(client, &wl_buffer_interface, 1, id);
- if (buffer->resource == NULL) {
- free(buffer);
- return NULL;
- }
-
- wl_resource_set_implementation(buffer->resource,
- &shm_buffer_interface,
- buffer, destroy_buffer);
-
- return buffer;
-}
-
-WL_EXPORT struct wl_shm_buffer *
wl_shm_buffer_get(struct wl_resource *resource)
{
if (resource == NULL)
@@ -388,10 +353,12 @@ wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer)
WL_EXPORT void *
wl_shm_buffer_get_data(struct wl_shm_buffer *buffer)
{
- if (buffer->pool)
- return buffer->pool->data + buffer->offset;
- else
- return buffer + 1;
+ assert(buffer->pool);
+
+ if (!buffer->pool)
+ return NULL;
+
+ return buffer->pool->data + buffer->offset;
}
WL_EXPORT uint32_t
@@ -412,6 +379,48 @@ wl_shm_buffer_get_height(struct wl_shm_buffer *buffer)
return buffer->height;
}
+/** Get a reference to a shm_buffer's shm_pool
+ *
+ * \param buffer The buffer object
+ *
+ * Returns a pointer to a buffer's shm_pool and increases the
+ * shm_pool refcount.
+ *
+ * The compositor must remember to call wl_shm_pool_unref when
+ * it no longer needs the reference to ensure proper destruction
+ * of the pool.
+ *
+ * \memberof wl_shm_buffer
+ * \sa wl_shm_pool_unref
+ */
+WL_EXPORT struct wl_shm_pool *
+wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer)
+{
+ assert(buffer->pool->refcount);
+
+ buffer->pool->refcount++;
+ return buffer->pool;
+}
+
+/** Unreference a shm_pool
+ *
+ * \param pool The pool object
+ *
+ * Drops a reference to a wl_shm_pool object.
+ *
+ * This is only necessary if the compositor has explicitly
+ * taken a reference with wl_shm_buffer_ref_pool(), otherwise
+ * the pool will be automatically destroyed when appropriate.
+ *
+ * \memberof wl_shm_pool
+ * \sa wl_shm_buffer_ref_pool
+ */
+WL_EXPORT void
+wl_shm_pool_unref(struct wl_shm_pool *pool)
+{
+ shm_pool_unref(pool);
+}
+
static void
reraise_sigbus(void)
{
@@ -527,12 +536,10 @@ wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer)
sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
if (sigbus_data == NULL) {
- sigbus_data = malloc(sizeof *sigbus_data);
+ sigbus_data = zalloc(sizeof *sigbus_data);
if (sigbus_data == NULL)
return;
- memset(sigbus_data, 0, sizeof *sigbus_data);
-
pthread_setspecific(wl_shm_sigbus_data_key, sigbus_data);
}
@@ -573,3 +580,20 @@ wl_shm_buffer_end_access(struct wl_shm_buffer *buffer)
sigbus_data->current_pool = NULL;
}
}
+
+/** \cond */ /* Deprecated functions below. */
+
+WL_EXPORT struct wl_shm_buffer *
+wl_shm_buffer_create(struct wl_client *client,
+ uint32_t id, int32_t width, int32_t height,
+ int32_t stride, uint32_t format)
+{
+ return NULL;
+}
+
+/** \endcond */
+
+/* Functions at the end of this file are deprecated. Instead of adding new
+ * code here, add it before the comment above that states:
+ * Deprecated functions below.
+ */
diff --git a/src/wayland-util.c b/src/wayland-util.c
index 00265e9..e782309 100644
--- a/src/wayland-util.c
+++ b/src/wayland-util.c
@@ -385,3 +385,15 @@ wl_log(const char *fmt, ...)
wl_log_handler(fmt, argp);
va_end(argp);
}
+
+void
+wl_abort(const char *fmt, ...)
+{
+ va_list argp;
+
+ va_start(argp, fmt);
+ wl_log_handler(fmt, argp);
+ va_end(argp);
+
+ abort();
+}
diff --git a/src/wayland-util.h b/src/wayland-util.h
index 3d04bdd..35b50ea 100644
--- a/src/wayland-util.h
+++ b/src/wayland-util.h
@@ -121,12 +121,23 @@ struct wl_list {
struct wl_list *next;
};
-void wl_list_init(struct wl_list *list);
-void wl_list_insert(struct wl_list *list, struct wl_list *elm);
-void wl_list_remove(struct wl_list *elm);
-int wl_list_length(const struct wl_list *list);
-int wl_list_empty(const struct wl_list *list);
-void wl_list_insert_list(struct wl_list *list, struct wl_list *other);
+void
+wl_list_init(struct wl_list *list);
+
+void
+wl_list_insert(struct wl_list *list, struct wl_list *elm);
+
+void
+wl_list_remove(struct wl_list *elm);
+
+int
+wl_list_length(const struct wl_list *list);
+
+int
+wl_list_empty(const struct wl_list *list);
+
+void
+wl_list_insert_list(struct wl_list *list, struct wl_list *other);
/**
* Retrieves a pointer to the containing struct of a given member item.
@@ -206,10 +217,17 @@ struct wl_array {
(const char *) pos < ((const char *) (array)->data + (array)->size); \
(pos)++)
-void wl_array_init(struct wl_array *array);
-void wl_array_release(struct wl_array *array);
-void *wl_array_add(struct wl_array *array, size_t size);
-int wl_array_copy(struct wl_array *array, struct wl_array *source);
+void
+wl_array_init(struct wl_array *array);
+
+void
+wl_array_release(struct wl_array *array);
+
+void *
+wl_array_add(struct wl_array *array, size_t size);
+
+int
+wl_array_copy(struct wl_array *array, struct wl_array *source);
typedef int32_t wl_fixed_t;
@@ -239,11 +257,14 @@ wl_fixed_from_double(double d)
return u.i;
}
-static inline int wl_fixed_to_int(wl_fixed_t f)
+static inline int
+wl_fixed_to_int(wl_fixed_t f)
{
return f / 256;
}
-static inline wl_fixed_t wl_fixed_from_int(int i)
+
+static inline wl_fixed_t
+wl_fixed_from_int(int i)
{
return i * 256;
}
diff --git a/tests/connection-test.c b/tests/connection-test.c
index e9832b7..5d97fd9 100644
--- a/tests/connection-test.c
+++ b/tests/connection-test.c
@@ -614,7 +614,7 @@ TEST(closure_leaks)
{
struct display *d = display_create();
- client_create(d, leak_closure);
+ client_create_noarg(d, leak_closure);
display_run(d);
display_destroy(d);
@@ -645,7 +645,7 @@ TEST(closure_leaks_after_error)
struct display *d = display_create();
struct client_info *cl;
- cl = client_create(d, leak_after_error);
+ cl = client_create_noarg(d, leak_after_error);
display_run(d);
wl_client_post_no_memory(cl->wl_client);
diff --git a/tests/display-test.c b/tests/display-test.c
index 161a402..1a6c345 100644
--- a/tests/display-test.c
+++ b/tests/display-test.c
@@ -24,6 +24,7 @@
* SOFTWARE.
*/
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -35,6 +36,8 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
+#include <poll.h>
+#include <stdbool.h>
#include "wayland-private.h"
#include "wayland-server.h"
@@ -95,21 +98,53 @@ empty_client(void)
TEST(tc_leaks_tests)
{
struct display *d = display_create();
- client_create(d, empty_client);
+ client_create_noarg(d, empty_client);
display_run(d);
display_destroy(d);
}
+/* This is how pre proxy-version registry binds worked,
+ * this should create a proxy that shares the display's
+ * version number: 0 */
+static void *
+old_registry_bind(struct wl_registry *wl_registry,
+ uint32_t name,
+ const struct wl_interface *interface,
+ uint32_t version)
+{
+ struct wl_proxy *id;
+
+ id = wl_proxy_marshal_constructor(
+ (struct wl_proxy *) wl_registry, WL_REGISTRY_BIND,
+ interface, name, interface->name, version, NULL);
+
+ return (void *) id;
+}
+
+struct handler_info {
+ struct wl_seat *seat;
+ uint32_t bind_version;
+ bool use_unversioned;
+};
+
static void
registry_handle_globals(void *data, struct wl_registry *registry,
uint32_t id, const char *intf, uint32_t ver)
{
- struct wl_seat **seat = data;
+ struct handler_info *hi = data;
+
+ /* This is only for the proxy version test */
+ if (hi->bind_version)
+ ver = hi->bind_version;
if (strcmp(intf, "wl_seat") == 0) {
- *seat = wl_registry_bind(registry, id,
- &wl_seat_interface, ver);
- assert(*seat);
+ if (hi->use_unversioned)
+ hi->seat = old_registry_bind(registry, id,
+ &wl_seat_interface, ver);
+ else
+ hi->seat = wl_registry_bind(registry, id,
+ &wl_seat_interface, ver);
+ assert(hi->seat);
}
}
@@ -119,31 +154,40 @@ static const struct wl_registry_listener registry_listener = {
};
static struct wl_seat *
-client_get_seat(struct client *c)
+client_get_seat_with_info(struct client *c, struct handler_info *hi)
{
- struct wl_seat *seat;
struct wl_registry *reg = wl_display_get_registry(c->wl_display);
assert(reg);
- wl_registry_add_listener(reg, &registry_listener, &seat);
+ assert(hi);
+ hi->seat = NULL;
+ wl_registry_add_listener(reg, &registry_listener, hi);
wl_display_roundtrip(c->wl_display);
- assert(seat);
+ assert(hi->seat);
wl_registry_destroy(reg);
- return seat;
+ return hi->seat;
+}
+
+static struct wl_seat *
+client_get_seat(struct client *c)
+{
+ struct handler_info hi;
+
+ hi.use_unversioned = false;
+ hi.bind_version = 0;
+
+ return client_get_seat_with_info(c, &hi);
}
static void
-check_for_error(struct client *c, struct wl_proxy *proxy)
+check_pending_error(struct client *c, struct wl_proxy *proxy)
{
uint32_t ec, id;
int err;
const struct wl_interface *intf;
- /* client should be disconnected */
- assert(wl_display_roundtrip(c->wl_display) == -1);
-
err = wl_display_get_error(c->wl_display);
assert(err == EPROTO);
@@ -154,20 +198,40 @@ check_for_error(struct client *c, struct wl_proxy *proxy)
}
static void
-bind_seat(struct wl_client *client, void *data,
- uint32_t vers, uint32_t id)
+check_for_error(struct client *c, struct wl_proxy *proxy)
+{
+ /* client should be disconnected */
+ assert(wl_display_roundtrip(c->wl_display) == -1);
+
+ check_pending_error(c, proxy);
+}
+
+static struct client_info *
+find_client_info(struct display *d, struct wl_client *client)
{
- struct display *d = data;
struct client_info *ci;
- struct wl_resource *res;
/* find the right client_info struct and save the
* resource as its data, so that we can use it later */
wl_list_for_each(ci, &d->clients, link) {
if (ci->wl_client == client)
- break;
+ return ci;
}
+ return NULL;
+}
+
+static void
+bind_seat(struct wl_client *client, void *data,
+ uint32_t vers, uint32_t id)
+{
+ struct display *d = data;
+ struct client_info *ci;
+ struct wl_resource *res;
+
+ ci = find_client_info(d, client);
+ assert(ci);
+
res = wl_resource_create(client, &wl_seat_interface, vers, id);
assert(res);
@@ -209,7 +273,7 @@ TEST(post_error_to_one_client)
wl_global_create(d->wl_display, &wl_seat_interface,
1, d, bind_seat);
- cl = client_create(d, post_error_main);
+ cl = client_create_noarg(d, post_error_main);
display_run(d);
/* the display was stopped by client, so it can
@@ -264,8 +328,8 @@ TEST(post_error_to_one_from_two_clients)
wl_global_create(d->wl_display, &wl_seat_interface,
1, d, bind_seat);
- client_create(d, post_error_main2);
- cl = client_create(d, post_error_main3);
+ client_create_noarg(d, post_error_main2);
+ cl = client_create_noarg(d, post_error_main3);
display_run(d);
/* post error only to the second client */
@@ -289,8 +353,8 @@ TEST(post_error_to_two_clients)
wl_global_create(d->wl_display, &wl_seat_interface,
1, d, bind_seat);
- cl = client_create(d, post_error_main3);
- cl2 = client_create(d, post_error_main3);
+ cl = client_create_noarg(d, post_error_main3);
+ cl2 = client_create_noarg(d, post_error_main3);
display_run(d);
@@ -331,7 +395,7 @@ TEST(post_nomem_tst)
wl_global_create(d->wl_display, &wl_seat_interface,
1, d, bind_seat);
- cl = client_create(d, post_nomem_main);
+ cl = client_create_noarg(d, post_nomem_main);
display_run(d);
assert(cl->data);
@@ -340,7 +404,7 @@ TEST(post_nomem_tst)
/* first client terminated. Run it again,
* but post no memory to client */
- cl = client_create(d, post_nomem_main);
+ cl = client_create_noarg(d, post_nomem_main);
display_run(d);
assert(cl->data);
@@ -447,7 +511,7 @@ TEST(threading_errors_tst)
{
struct display *d = display_create();
- client_create(d, threading_post_err);
+ client_create_noarg(d, threading_post_err);
display_run(d);
display_destroy(d);
@@ -502,7 +566,7 @@ TEST(threading_cancel_read_tst)
{
struct display *d = display_create();
- client_create(d, threading_cancel_read);
+ client_create_noarg(d, threading_cancel_read);
display_run(d);
display_destroy(d);
@@ -542,7 +606,7 @@ threading_read_eagain(void)
TEST(threading_read_eagain_tst)
{
struct display *d = display_create();
- client_create(d, threading_read_eagain);
+ client_create_noarg(d, threading_read_eagain);
display_run(d);
@@ -604,8 +668,211 @@ TEST(threading_read_after_error_tst)
{
struct display *d = display_create();
- client_create(d, threading_read_after_error);
+ client_create_noarg(d, threading_read_after_error);
display_run(d);
display_destroy(d);
}
+
+static void
+wait_for_error_using_dispatch(struct client *c, struct wl_proxy *proxy)
+{
+ int ret;
+
+ while (true) {
+ /* Dispatching should eventually hit the protocol error before
+ * any other error. */
+ ret = wl_display_dispatch(c->wl_display);
+ if (ret == 0) {
+ continue;
+ } else {
+ assert(errno == EPROTO);
+ break;
+ }
+ }
+
+ check_pending_error(c, proxy);
+}
+
+static void
+wait_for_error_using_prepare_read(struct client *c, struct wl_proxy *proxy)
+{
+ int ret = 0;
+ struct pollfd pfd[2];
+
+ while (true) {
+ while (wl_display_prepare_read(c->wl_display) != 0 &&
+ errno == EAGAIN) {
+ assert(wl_display_dispatch_pending(c->wl_display) >= 0);
+ }
+
+ /* Flush may fail due to EPIPE if the connection is broken, but
+ * this must not set a fatal display error because that would
+ * result in it being impossible to read a potential protocol
+ * error. */
+ do {
+ ret = wl_display_flush(c->wl_display);
+ } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+ assert(ret >= 0 || errno == EPIPE);
+ assert(wl_display_get_error(c->wl_display) == 0);
+
+ pfd[0].fd = wl_display_get_fd(c->wl_display);
+ pfd[0].events = POLLIN;
+ do {
+ ret = poll(pfd, 1, -1);
+ } while (ret == -1 && errno == EINTR);
+ assert(ret != -1);
+
+ /* We should always manage to read the error before the EPIPE
+ * comes this way. */
+ assert(wl_display_read_events(c->wl_display) == 0);
+
+ /* Dispatching should eventually hit the protocol error before
+ * any other error. */
+ ret = wl_display_dispatch_pending(c->wl_display);
+ if (ret == 0) {
+ continue;
+ } else {
+ assert(errno == EPROTO);
+ break;
+ }
+ }
+
+ check_pending_error(c, proxy);
+}
+
+static void
+check_error_after_epipe(void *data)
+{
+ bool use_dispatch_helpers = *(bool *) data;
+ struct client *client;
+ struct wl_seat *seat;
+ struct wl_callback *callback;
+
+ client = client_connect();
+
+ /* This will, according to the implementation below, cause the server
+ * to post an error. */
+ seat = client_get_seat(client);
+ wl_display_flush(client->wl_display);
+
+ /* The server will not actually destroy the client until it receives
+ * input, so send something to trigger the client destruction. */
+ callback = wl_display_sync(client->wl_display);
+ wl_callback_destroy(callback);
+
+ /* Sleep some to give the server a chance to react and destroy the
+ * client. */
+ test_usleep(200000);
+
+ /* Wait for the protocol error and check that we reached it before
+ * EPIPE. */
+ if (use_dispatch_helpers) {
+ wait_for_error_using_dispatch(client, (struct wl_proxy *) seat);
+ } else {
+ wait_for_error_using_prepare_read(client,
+ (struct wl_proxy *) seat);
+ }
+
+ wl_seat_destroy(seat);
+ client_disconnect_nocheck(client);
+}
+
+static void
+bind_seat_and_post_error(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id)
+{
+ struct display *d = data;
+ struct client_info *ci;
+ struct wl_resource *resource;
+
+ ci = find_client_info(d, client);
+ assert(ci);
+
+ resource = wl_resource_create(client, &wl_seat_interface, version, id);
+ assert(resource);
+ ci->data = resource;
+
+ wl_resource_post_error(ci->data, 23, "Dummy error");
+}
+
+TEST(error_code_after_epipe)
+{
+ struct display *d = display_create();
+ bool use_dispatch_helpers;
+
+ wl_global_create(d->wl_display, &wl_seat_interface,
+ 1, d, bind_seat_and_post_error);
+
+ use_dispatch_helpers = true;
+ client_create(d, check_error_after_epipe, &use_dispatch_helpers);
+ display_run(d);
+
+ use_dispatch_helpers = false;
+ client_create(d, check_error_after_epipe, &use_dispatch_helpers);
+ display_run(d);
+
+ display_destroy(d);
+}
+
+static void
+check_seat_versions(struct wl_seat *seat, uint32_t ev)
+{
+ struct wl_pointer *pointer;
+
+ assert(wl_proxy_get_version((struct wl_proxy *) seat) == ev);
+ assert(wl_seat_get_version(seat) == ev);
+
+ pointer = wl_seat_get_pointer(seat);
+ assert(wl_pointer_get_version(pointer) == ev);
+ assert(wl_proxy_get_version((struct wl_proxy *) pointer) == ev);
+ wl_proxy_destroy((struct wl_proxy *) pointer);
+}
+
+/* Normal client with proxy versions available. */
+static void
+seat_version(void *data)
+{
+ struct handler_info *hi = data;
+ struct client *c = client_connect();
+ struct wl_seat *seat;
+
+ /* display proxy should always be version 0 */
+ assert(wl_proxy_get_version((struct wl_proxy *) c->wl_display) == 0);
+
+ seat = client_get_seat_with_info(c, hi);
+ if (hi->use_unversioned)
+ check_seat_versions(seat, 0);
+ else
+ check_seat_versions(seat, hi->bind_version);
+
+ wl_proxy_destroy((struct wl_proxy *) seat);
+
+ client_disconnect_nocheck(c);
+}
+
+TEST(versions)
+{
+ struct display *d = display_create();
+ struct wl_global *global;
+ int i;
+
+ global = wl_global_create(d->wl_display, &wl_seat_interface,
+ 5, d, bind_seat);
+
+ for (i = 1; i <= 5; i++) {
+ struct handler_info hi;
+
+ hi.bind_version = i;
+ hi.use_unversioned = false;
+ client_create(d, seat_version, &hi);
+ hi.use_unversioned = true;
+ client_create(d, seat_version, &hi);
+ }
+
+ display_run(d);
+
+ wl_global_destroy(global);
+
+ display_destroy(d);
+}
diff --git a/tests/queue-test.c b/tests/queue-test.c
index dc1a01d..02865ae 100644
--- a/tests/queue-test.c
+++ b/tests/queue-test.c
@@ -230,7 +230,7 @@ TEST(queue_proxy_destroy)
test_set_timeout(2);
- client_create(d, client_test_proxy_destroy);
+ client_create_noarg(d, client_test_proxy_destroy);
display_run(d);
display_destroy(d);
@@ -242,7 +242,7 @@ TEST(queue_multiple_queues)
test_set_timeout(2);
- client_create(d, client_test_multiple_queues);
+ client_create_noarg(d, client_test_multiple_queues);
display_run(d);
display_destroy(d);
@@ -254,7 +254,7 @@ TEST(queue_roundtrip)
test_set_timeout(2);
- client_create(d, client_test_queue_roundtrip);
+ client_create_noarg(d, client_test_queue_roundtrip);
display_run(d);
display_destroy(d);
diff --git a/tests/sanity-test.c b/tests/sanity-test.c
index 3f589b5..7a93da3 100644
--- a/tests/sanity-test.c
+++ b/tests/sanity-test.c
@@ -31,8 +31,8 @@
#include "test-runner.h"
#include "wayland-util.h"
+#include "wayland-private.h"
-#define WL_HIDE_DEPRECATED
#include "test-compositor.h"
extern int leak_check_enabled;
@@ -56,6 +56,11 @@ FAIL_TEST(fail_abort)
abort();
}
+FAIL_TEST(fail_wl_abort)
+{
+ wl_abort("Abort the program\n");
+}
+
FAIL_TEST(fail_kill)
{
kill(getpid(), SIGTERM);
@@ -114,7 +119,7 @@ FAIL_TEST(sanity_malloc_indirect)
FAIL_TEST(tc_client_memory_leaks)
{
struct display *d = display_create();
- client_create(d, sanity_malloc_direct);
+ client_create_noarg(d, sanity_malloc_direct);
display_run(d);
display_destroy(d);
}
@@ -122,7 +127,7 @@ FAIL_TEST(tc_client_memory_leaks)
FAIL_TEST(tc_client_memory_leaks2)
{
struct display *d = display_create();
- client_create(d, sanity_malloc_indirect);
+ client_create_noarg(d, sanity_malloc_indirect);
display_run(d);
display_destroy(d);
}
@@ -190,11 +195,11 @@ TEST(tc_client_no_fd_leaks)
struct display *d = display_create();
/* Client which does not consume the WAYLAND_DISPLAY socket. */
- client_create(d, sanity_fd_no_leak);
+ client_create_noarg(d, sanity_fd_no_leak);
display_run(d);
/* Client which does consume the WAYLAND_DISPLAY socket. */
- client_create(d, sanity_client_no_leak);
+ client_create_noarg(d, sanity_client_no_leak);
display_run(d);
display_destroy(d);
@@ -204,7 +209,7 @@ FAIL_TEST(tc_client_fd_leaks)
{
struct display *d = display_create();
- client_create(d, sanity_fd_leak);
+ client_create_noarg(d, sanity_fd_leak);
display_run(d);
display_destroy(d);
@@ -214,7 +219,7 @@ FAIL_TEST(tc_client_fd_leaks_exec)
{
struct display *d = display_create();
- client_create(d, sanity_fd_leak);
+ client_create_noarg(d, sanity_fd_leak);
display_run(d);
display_destroy(d);
@@ -258,7 +263,7 @@ TEST(timeout_turnoff)
FAIL_TEST(tc_timeout_tst)
{
struct display *d = display_create();
- client_create(d, timeout_tst);
+ client_create_noarg(d, timeout_tst);
display_run(d);
display_destroy(d);
}
@@ -266,7 +271,7 @@ FAIL_TEST(tc_timeout_tst)
FAIL_TEST(tc_timeout2_tst)
{
struct display *d = display_create();
- client_create(d, timeout_reset_tst);
+ client_create_noarg(d, timeout_reset_tst);
display_run(d);
display_destroy(d);
}
@@ -275,10 +280,10 @@ TEST(tc_timeout3_tst)
{
struct display *d = display_create();
- client_create(d, timeout2_tst);
+ client_create_noarg(d, timeout2_tst);
display_run(d);
- client_create(d, timeout_turnoff);
+ client_create_noarg(d, timeout_turnoff);
display_run(d);
display_destroy(d);
diff --git a/tests/socket-test.c b/tests/socket-test.c
index c53f972..bb034f4 100644
--- a/tests/socket-test.c
+++ b/tests/socket-test.c
@@ -47,8 +47,7 @@ static const char *
require_xdg_runtime_dir(void)
{
char *val = getenv("XDG_RUNTIME_DIR");
- if (!val)
- assert(0 && "set $XDG_RUNTIME_DIR to run this test");
+ assert(val && "set $XDG_RUNTIME_DIR to run this test");
return val;
}
@@ -107,11 +106,11 @@ TEST(add_existing_socket)
ret = wl_display_add_socket(d, name);
assert(ret == 0);
- /* this on should fail */
+ /* this one should fail */
ret = wl_display_add_socket(d, name);
assert(ret < 0);
- /* the original socket should still exists,
+ /* the original socket should still exist.
* this was a bug introduced in e2c0d47b0c77f18cd90e9c6eabb358c4d89681c8 */
len = snprintf(path, sizeof example_sockaddr_un.sun_path, "%s/%s",
xdg_runtime_dir, name);
@@ -120,7 +119,7 @@ TEST(add_existing_socket)
assert(access(path, F_OK) != -1);
- /* still should exists the original socket */
+ /* the original socket should still exist */
ret = wl_display_add_socket(d, name);
assert(ret < 0);
@@ -129,7 +128,9 @@ TEST(add_existing_socket)
TEST(add_socket_auto)
{
- /* the number of auto sockets is currently 32 */
+ /* the number of auto sockets is currently 32,
+ * set in wayland-server.c.
+ */
const int MAX_SOCKETS = 32;
char path[sizeof example_sockaddr_un.sun_path];
@@ -153,7 +154,7 @@ TEST(add_socket_auto)
assert(len < sizeof example_sockaddr_un.sun_path
&& "Bug in test. Path too long");
- /* was the socket? */
+ /* was the socket created correctly? */
assert(access(path, F_OK) != -1);
/* is the name sequential? */
diff --git a/tests/test-compositor.c b/tests/test-compositor.c
index 296db3b..b01e8af 100644
--- a/tests/test-compositor.c
+++ b/tests/test-compositor.c
@@ -91,17 +91,13 @@ get_socket_name(void)
return retval;
}
-/**
- * Check client's state and terminate display when all clients exited
- */
static void
-client_destroyed(struct wl_listener *listener, void *data)
+handle_client_destroy(void *data)
{
+ struct client_info *ci = data;
struct display *d;
- struct client_info *ci;
siginfo_t status;
- ci = wl_container_of(listener, ci, destroy_listener);
d = ci->display;
assert(waitid(P_PID, ci->pid, &status, WEXITED) != -1);
@@ -132,11 +128,31 @@ client_destroyed(struct wl_listener *listener, void *data)
* clients. In the case that the test would go through
* the clients list manually, zero out the wl_client as a sign
* that the client is not running anymore */
+}
+
+/**
+ * Check client's state and terminate display when all clients exited
+ */
+static void
+client_destroyed(struct wl_listener *listener, void *data)
+{
+ struct client_info *ci;
+ struct display *d;
+ struct wl_event_loop *loop;
+
+ /* Wait for client in an idle handler to avoid blocking the actual
+ * client destruction (fd close etc. */
+ ci = wl_container_of(listener, ci, destroy_listener);
+ d = ci->display;
+ loop = wl_display_get_event_loop(d->wl_display);
+ wl_event_loop_add_idle(loop, handle_client_destroy, ci);
+
ci->wl_client = NULL;
}
static void
-run_client(void (*client_main)(void), int wayland_sock, int client_pipe)
+run_client(void (*client_main)(void *data), void *data,
+ int wayland_sock, int client_pipe)
{
char s[8];
int cur_alloc, cur_fds;
@@ -155,7 +171,7 @@ run_client(void (*client_main)(void), int wayland_sock, int client_pipe)
cur_alloc = get_current_alloc_num();
cur_fds = count_open_fds();
- client_main();
+ client_main(data);
/* Clients using wl_display_connect() will end up closing the socket
* passed in through the WAYLAND_SOCKET environment variable. When
@@ -170,7 +186,8 @@ run_client(void (*client_main)(void), int wayland_sock, int client_pipe)
static struct client_info *
display_create_client(struct display *d,
- void (*client_main)(void),
+ void (*client_main)(void *data),
+ void *data,
const char *name)
{
int pipe_cli[2];
@@ -190,7 +207,7 @@ display_create_client(struct display *d,
close(sock_wayl[1]);
close(pipe_cli[1]);
- run_client(client_main, sock_wayl[0], pipe_cli[0]);
+ run_client(client_main, data, sock_wayl[0], pipe_cli[0]);
close(sock_wayl[0]);
close(pipe_cli[0]);
@@ -231,11 +248,14 @@ display_create_client(struct display *d,
}
struct client_info *
-client_create_with_name(struct display *d, void (*client_main)(void),
+client_create_with_name(struct display *d,
+ void (*client_main)(void *data), void *data,
const char *name)
{
int can_continue = 1;
- struct client_info *cl = display_create_client(d, client_main, name);
+ struct client_info *cl = display_create_client(d,
+ client_main, data,
+ name);
/* let the show begin! */
assert(write(cl->pipe, &can_continue, sizeof(int)) == sizeof(int));
diff --git a/tests/test-compositor.h b/tests/test-compositor.h
index c97eb96..526e912 100644
--- a/tests/test-compositor.h
+++ b/tests/test-compositor.h
@@ -78,8 +78,8 @@ int stop_display(struct client *, int);
* wl_global_create(d->wl_display, ...);
* ... other setups ...
*
- * client_create(d, client_main);
- * client_create(d, client_main2);
+ * client_create(d, client_main, data);
+ * client_create(d, client_main2, data);
*
* display_run(d);
* display_destroy(d);
@@ -95,6 +95,9 @@ void display_run(struct display *d);
void display_resume(struct display *d);
struct client_info *client_create_with_name(struct display *d,
- void (*client_main)(void),
+ void (*client_main)(void *data),
+ void *data,
const char *name);
-#define client_create(d, c) client_create_with_name((d), (c), (#c))
+#define client_create(d, c, data) client_create_with_name((d), (c), data, (#c))
+#define client_create_noarg(d, c) \
+ client_create_with_name((d), (void(*)(void *)) (c), NULL, (#c))