diff options
Diffstat (limited to 'src/wayland-client.c')
-rw-r--r-- | src/wayland-client.c | 172 |
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) { |