aboutsummaryrefslogtreecommitdiff
path: root/src/wayland-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/wayland-client.c')
-rw-r--r--src/wayland-client.c172
1 files changed, 151 insertions, 21 deletions
diff --git a/src/wayland-client.c b/src/wayland-client.c
index ef12bfc..03c087a 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -51,7 +51,8 @@
enum wl_proxy_flag {
WL_PROXY_FLAG_ID_DELETED = (1 << 0),
- WL_PROXY_FLAG_DESTROYED = (1 << 1)
+ WL_PROXY_FLAG_DESTROYED = (1 << 1),
+ WL_PROXY_FLAG_WRAPPER = (1 << 2),
};
struct wl_proxy {
@@ -177,7 +178,7 @@ display_protocol_error(struct wl_display *display, uint32_t code,
return;
/* set correct errno */
- if (wl_interface_equal(intf, &wl_display_interface)) {
+ if (intf && wl_interface_equal(intf, &wl_display_interface)) {
switch (code) {
case WL_DISPLAY_ERROR_INVALID_OBJECT:
case WL_DISPLAY_ERROR_INVALID_METHOD:
@@ -252,9 +253,6 @@ decrease_closure_args_refcount(struct wl_closure *closure)
}
}
-void
-proxy_destroy(struct wl_proxy *proxy);
-
static void
wl_event_queue_release(struct wl_event_queue *queue)
{
@@ -404,7 +402,7 @@ wl_proxy_create_for_id(struct wl_proxy *factory,
return proxy;
}
-void
+static void
proxy_destroy(struct wl_proxy *proxy)
{
if (proxy->flags & WL_PROXY_FLAG_ID_DELETED)
@@ -428,6 +426,8 @@ proxy_destroy(struct wl_proxy *proxy)
*
* \param proxy The proxy to be destroyed
*
+ * \c proxy must not be a proxy wrapper.
+ *
* \memberof wl_proxy
*/
WL_EXPORT void
@@ -435,6 +435,9 @@ wl_proxy_destroy(struct wl_proxy *proxy)
{
struct wl_display *display = proxy->display;
+ if (proxy->flags & WL_PROXY_FLAG_WRAPPER)
+ wl_abort("Tried to destroy wrapper with wl_proxy_destroy()\n");
+
pthread_mutex_lock(&display->mutex);
proxy_destroy(proxy);
pthread_mutex_unlock(&display->mutex);
@@ -455,12 +458,17 @@ wl_proxy_destroy(struct wl_proxy *proxy)
* \c n, \c implementation[n] should point to the handler of \c n for
* the given object.
*
+ * \c proxy must not be a proxy wrapper.
+ *
* \memberof wl_proxy
*/
WL_EXPORT int
wl_proxy_add_listener(struct wl_proxy *proxy,
void (**implementation)(void), void *data)
{
+ if (proxy->flags & WL_PROXY_FLAG_WRAPPER)
+ wl_abort("Proxy %p is a wrapper\n", proxy);
+
if (proxy->object.implementation || proxy->dispatcher) {
wl_log("proxy %p already has listener\n", proxy);
return -1;
@@ -507,6 +515,8 @@ wl_proxy_get_listener(struct wl_proxy *proxy)
* The exact details of dispatcher_data depend on the dispatcher used. This
* function is intended to be used by language bindings, not user code.
*
+ * \c proxy must not be a proxy wrapper.
+ *
* \memberof wl_proxy
*/
WL_EXPORT int
@@ -514,6 +524,9 @@ wl_proxy_add_dispatcher(struct wl_proxy *proxy,
wl_dispatcher_func_t dispatcher,
const void *implementation, void *data)
{
+ if (proxy->flags & WL_PROXY_FLAG_WRAPPER)
+ wl_abort("Proxy %p is a wrapper\n", proxy);
+
if (proxy->object.implementation || proxy->dispatcher) {
wl_log("proxy %p already has listener\n", proxy);
return -1;
@@ -568,7 +581,7 @@ create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
*
* 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
+ * on success or NULL on error 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
@@ -603,7 +616,7 @@ wl_proxy_marshal_array_constructor(struct wl_proxy *proxy,
*
* 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
+ * on success or NULL on error 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
@@ -698,7 +711,7 @@ wl_proxy_marshal(struct wl_proxy *proxy, uint32_t opcode, ...)
*
* 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
+ * on success or NULL on error 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.
@@ -736,7 +749,7 @@ wl_proxy_marshal_constructor(struct wl_proxy *proxy, uint32_t opcode,
*
* 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
+ * on success or NULL on error with errno set accordingly. The newly
* created proxy will have the version specified.
*
* \note This should not normally be used by non-generated code.
@@ -790,12 +803,26 @@ display_handle_error(void *data,
uint32_t code, const char *message)
{
struct wl_proxy *proxy = object;
+ uint32_t object_id;
+ const struct wl_interface *interface;
+
+ if (proxy) {
+ wl_log("%s@%u: error %d: %s\n",
+ proxy->object.interface->name,
+ proxy->object.id,
+ code, message);
- wl_log("%s@%u: error %d: %s\n",
- proxy->object.interface->name, proxy->object.id, code, message);
+ object_id = proxy->object.id;
+ interface = proxy->object.interface;
+ } else {
+ wl_log("[destroyed object]: error %d: %s\n",
+ code, message);
- display_protocol_error(display, code, proxy->object.id,
- proxy->object.interface);
+ object_id = 0;
+ interface = NULL;
+ }
+
+ display_protocol_error(display, code, object_id, interface);
}
static void
@@ -1072,14 +1099,23 @@ static const struct wl_callback_listener sync_listener = {
WL_EXPORT int
wl_display_roundtrip_queue(struct wl_display *display, struct wl_event_queue *queue)
{
+ struct wl_display *display_wrapper;
struct wl_callback *callback;
int done, ret = 0;
done = 0;
- callback = wl_display_sync(display);
+
+ display_wrapper = wl_proxy_create_wrapper(display);
+ if (!display_wrapper)
+ return -1;
+
+ wl_proxy_set_queue((struct wl_proxy *) display_wrapper, queue);
+ callback = wl_display_sync(display_wrapper);
+ wl_proxy_wrapper_destroy(display_wrapper);
+
if (callback == NULL)
return -1;
- wl_proxy_set_queue((struct wl_proxy *) callback, queue);
+
wl_callback_add_listener(callback, &sync_listener, &done);
while (!done && ret >= 0)
ret = wl_display_dispatch_queue(display, queue);
@@ -1573,7 +1609,7 @@ wl_display_poll(struct wl_display *display, short int events)
* 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
+ * In a multi threaded environment, 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.
@@ -1678,7 +1714,7 @@ wl_display_dispatch_queue_pending(struct wl_display *display,
* the appropriate event queues. Finally, events on the default 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
+ * In a multi threaded environment, 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.
@@ -1756,10 +1792,12 @@ wl_display_get_error(struct wl_display *display)
/** Retrieves the information about a protocol error:
*
* \param display The Wayland display
- * \param interface if not NULL, stores the interface where the error occurred
+ * \param interface if not NULL, stores the interface where the error occurred,
+ * or NULL, if unknown.
* \param id if not NULL, stores the object id that generated
- * the error. There's no guarantee the object is
- * still valid; the client must know if it deleted the object.
+ * the error, or 0, if the object id is unknown. There's no
+ * guarantee the object is still valid; the client must know
+ * if it deleted the object.
* \return The error code as defined in the interface specification.
*
* \code
@@ -1939,6 +1977,98 @@ wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue)
proxy->queue = &proxy->display->default_queue;
}
+/** Create a proxy wrapper for making queue assignments thread-safe
+ *
+ * \param proxy The proxy object to be wrapped
+ * \return A proxy wrapper for the given proxy or NULL on failure
+ *
+ * A proxy wrapper is type of 'struct wl_proxy' instance that can be used when
+ * sending requests instead of using the original proxy. A proxy wrapper does
+ * not have an implementation or dispatcher, and events received on the
+ * object is still emitted on the original proxy. Trying to set an
+ * implementation or dispatcher will have no effect but result in a warning
+ * being logged.
+ *
+ * Setting the proxy queue of the proxy wrapper will make new objects created
+ * using the proxy wrapper use the set proxy queue.
+ * Even though there is no implementation nor dispatcher, the proxy queue can
+ * be changed. This will affect the default queue of new objects created by
+ * requests sent via the proxy wrapper.
+ *
+ * A proxy wrapper can only be destroyed using wl_proxy_wrapper_destroy().
+ *
+ * A proxy wrapper must be destroyed before the proxy it was created from.
+ *
+ * If a user reads and dispatches events on more than one thread, it is
+ * necessary to use a proxy wrapper when sending requests on objects when the
+ * intention is that a newly created proxy is to use a proxy queue different
+ * from the proxy the request was sent on, as creating the new proxy and then
+ * setting the queue is not thread safe.
+ *
+ * For example, a module that runs using its own proxy queue that needs to
+ * do display roundtrip must wrap the wl_display proxy object before sending
+ * the wl_display.sync request. For example:
+ *
+ * \code
+ *
+ * struct wl_event_queue *queue = ...;
+ * struct wl_display *wrapped_display;
+ * struct wl_callback *callback;
+ *
+ * wrapped_display = wl_proxy_create_wrapper(display);
+ * wl_proxy_set_queue((struct wl_proxy *) wrapped_display, queue);
+ * callback = wl_display_sync(wrapped_display);
+ * wl_proxy_wrapper_destroy(wrapped_display);
+ * wl_callback_add_listener(callback, ...);
+ *
+ * \endcode
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT void *
+wl_proxy_create_wrapper(void *proxy)
+{
+ struct wl_proxy *wrapped_proxy = proxy;
+ struct wl_proxy *wrapper;
+
+ wrapper = zalloc(sizeof *wrapper);
+ if (!wrapper)
+ return NULL;
+
+ pthread_mutex_lock(&wrapped_proxy->display->mutex);
+
+ wrapper->object.interface = wrapped_proxy->object.interface;
+ wrapper->object.id = wrapped_proxy->object.id;
+ wrapper->version = wrapped_proxy->version;
+ wrapper->display = wrapped_proxy->display;
+ wrapper->queue = wrapped_proxy->queue;
+ wrapper->flags = WL_PROXY_FLAG_WRAPPER;
+ wrapper->refcount = 1;
+
+ pthread_mutex_unlock(&wrapped_proxy->display->mutex);
+
+ return wrapper;
+}
+
+/** Destroy a proxy wrapper
+ * \param proxy_wrapper The proxy wrapper to be destroyed
+ *
+ * \memberof wl_proxy
+ */
+WL_EXPORT void
+wl_proxy_wrapper_destroy(void *proxy_wrapper)
+{
+ struct wl_proxy *wrapper = proxy_wrapper;
+
+ if (!(wrapper->flags & WL_PROXY_FLAG_WRAPPER))
+ wl_abort("Tried to destroy non-wrapper proxy with "
+ "wl_proxy_wrapper_destroy\n");
+
+ assert(wrapper->refcount == 1);
+
+ free(wrapper);
+}
+
WL_EXPORT void
wl_log_set_handler_client(wl_log_func_t handler)
{