diff --git a/include/libweston/weston-log.h b/include/libweston/weston-log.h index 21e9ea02..90bce786 100644 --- a/include/libweston/weston-log.h +++ b/include/libweston/weston-log.h @@ -37,6 +37,7 @@ extern "C" { struct weston_compositor; struct weston_log_context; struct wl_display; +struct weston_log_subscriber; void weston_compositor_enable_debug_protocol(struct weston_compositor *); @@ -90,6 +91,11 @@ char * weston_log_scope_timestamp(struct weston_log_scope *scope, char *buf, size_t len); +void +weston_log_subscribe(struct weston_log_context *log_ctx, + struct weston_log_subscriber *subscriber, + const char *scope_name); + #ifdef __cplusplus } #endif diff --git a/libweston/weston-log.c b/libweston/weston-log.c index a023d328..566a4fac 100644 --- a/libweston/weston-log.c +++ b/libweston/weston-log.c @@ -107,6 +107,63 @@ struct weston_log_subscription { weston_log_context::pending_subscription_list */ }; +static struct weston_log_subscription * +find_pending_subscription(struct weston_log_context *log_ctx, + const char *scope_name) +{ + struct weston_log_subscription *sub; + + wl_list_for_each(sub, &log_ctx->pending_subscription_list, source_link) + if (!strncmp(sub->scope_name, scope_name, strlen(scope_name))) + return sub; + + return NULL; +} + +/** Create a pending subscription and add it the list of pending subscriptions + * + * @param owner a subscriber represented by weston_log_subscriber object + * @param scope_name the name of the scope (which we don't have in the list of scopes) + * @param log_ctx the logging context used to add the pending subscription + * + * @memberof weston_log_subscription + */ +static void +weston_log_subscription_create_pending(struct weston_log_subscriber *owner, + const char *scope_name, + struct weston_log_context *log_ctx) +{ + assert(owner); + assert(scope_name); + struct weston_log_subscription *sub = zalloc(sizeof(*sub)); + + if (!sub) + return; + + sub->scope_name = strdup(scope_name); + sub->owner = owner; + + wl_list_insert(&log_ctx->pending_subscription_list, &sub->source_link); +} + +/** Destroys the pending subscription created previously with + * weston_log_subscription_create_pending() + * + * @param sub the weston_log_subscription object to remove from the list + * of subscriptions and to destroy the subscription + * + * @memberof weston_log_subscription + */ +static void +weston_log_subscription_destroy_pending(struct weston_log_subscription *sub) +{ + assert(sub); + /* pending subsriptions do not have a source */ + wl_list_remove(&sub->source_link); + free(sub->scope_name); + free(sub); +} + /** Creates a new subscription using the subscriber by \c owner. * * The subscription created is added to the \c owner subscription list. @@ -128,8 +185,8 @@ weston_log_subscription_create(struct weston_log_subscriber *owner, const char *scope_name) { struct weston_log_subscription *sub; - assert(owner); assert(scope_name); + assert(owner); sub = zalloc(sizeof(*sub)); if (!sub) @@ -302,6 +359,7 @@ weston_log_ctx_compositor_create(void) return NULL; wl_list_init(&log_ctx->scope_list); + wl_list_init(&log_ctx->pending_subscription_list); return log_ctx; } @@ -318,6 +376,7 @@ weston_log_ctx_compositor_destroy(struct weston_compositor *compositor) { struct weston_log_context *log_ctx = compositor->weston_log_ctx; struct weston_log_scope *scope; + struct weston_log_subscription *pending_sub, *pending_sub_tmp; if (log_ctx->global) wl_global_destroy(log_ctx->global); @@ -329,6 +388,14 @@ weston_log_ctx_compositor_destroy(struct weston_compositor *compositor) /* Remove head to not crash if scope removed later. */ wl_list_remove(&log_ctx->scope_list); + /* Remove any pending subscription(s) which nobody subscribed to */ + wl_list_for_each_safe(pending_sub, pending_sub_tmp, + &log_ctx->pending_subscription_list, source_link) { + weston_log_subscription_destroy_pending(pending_sub); + } + + /* pending_subscription_list should be empty at this point */ + free(log_ctx); compositor->weston_log_ctx = NULL; @@ -380,39 +447,49 @@ weston_compositor_is_debug_protocol_enabled(struct weston_compositor *wc) return wc->weston_log_ctx->global != NULL; } -/** Register a new debug stream name, creating a log scope - * - * \param log_ctx The weston_log_context where to add. - * \param name The debug stream/scope name; must not be NULL. - * \param description The debug scope description for humans; must not be NULL. - * \param begin_cb Optional callback when a client subscribes to this scope. - * \param user_data Optional user data pointer for the callback. - * \return A valid pointer on success, NULL on failure. - * - * This function is used to create a debug scope. All debug message printing - * happens for a scope, which allows clients to subscribe to the kind of - * debug messages they want by \c name. - * - * \c name must be unique in the \c weston_compositor instance. \c name and - * \c description must both be provided. The description is printed when a - * client asks for a list of supported debug scopes. - * - * \c begin_cb, if not NULL, is called when a client subscribes to the - * debug scope creating a debug stream. This is for debug scopes that need - * to print messages as a response to a client appearing, e.g. printing a - * list of windows on demand or a static preamble. The argument \c user_data - * is passed in to the callback and is otherwise unused. +/** Register a new stream name, creating a log scope. + * + * @param log_ctx The weston_log_context where to add. + * @param name The debug stream/scope name; must not be NULL. + * @param description The log scope description for humans; must not be NULL. + * @param begin_cb Optional callback when a client subscribes to this scope. + * @param user_data Optional user data pointer for the callback. + * @returns A valid pointer on success, NULL on failure. + * + * This function is used to create a log scope. All debug message printing + * happens for a scope, which allows clients to subscribe to the kind of debug + * messages they want by \c name. For the weston-debug protocol, + * subscription for the scope will happen automatically but for other types of + * streams, weston_log_subscribe() should be called as to create a subscription + * and tie it to the scope created by this function. + * + * \p name must be unique in the weston_compositor instance. \p name + * and \p description must both be provided. In case of the weston-debug + * protocol, the description is printed when a client asks for a list of + * supported log scopes. + * + * \p begin_cb, if not NULL, is called when a client subscribes to the log + * scope creating a debug stream. This is for log scopes that need to print + * messages as a response to a client appearing, e.g. printing a list of + * windows on demand or a static preamble. The argument \p user_data is + * passed in to the callback and is otherwise unused. * * For one-shot debug streams, \c begin_cb should finally call - * weston_debug_stream_complete() to close the stream and tell the client - * the printing is complete. Otherwise the client expects more to be written - * to its file descriptor. + * weston_log_scope_complete() to close the stream and tell the client the + * printing is complete. Otherwise the client expects more data to be written. + * The complete callback in weston_log_subscriber should be installed to + * trigger it and it is set-up automatically for the weston-debug protocol. + * + * As subscription can take place before creating the scope, any pending + * subscriptions to scope added by weston_log_subscribe(), will be checked + * against the scope being created and if found will be added to the scope's + * subscription list. * - * The debug scope must be destroyed before destroying the - * \c weston_compositor. + * The log scope must be destroyed using weston_compositor_log_scope_destroy() + * before destroying the weston_compositor. * - * \memberof weston_log_scope - * \sa weston_debug_stream, weston_log_scope_cb + * @memberof weston_log_scope + * @sa weston_log_scope_cb, weston_log_subscribe */ WL_EXPORT struct weston_log_scope * weston_compositor_add_log_scope(struct weston_log_context *log_ctx, @@ -422,6 +499,7 @@ weston_compositor_add_log_scope(struct weston_log_context *log_ctx, void *user_data) { struct weston_log_scope *scope; + struct weston_log_subscription *pending_sub = NULL; if (!name || !description) { weston_log("Error: cannot add a debug scope without name or description.\n"); @@ -464,17 +542,31 @@ weston_compositor_add_log_scope(struct weston_log_context *log_ctx, wl_list_insert(log_ctx->scope_list.prev, &scope->compositor_link); + /* check if there are any pending subscriptions to this scope */ + while ((pending_sub = find_pending_subscription(log_ctx, scope->name)) != NULL) { + struct weston_log_subscription *sub = + weston_log_subscription_create(pending_sub->owner, + scope->name); + + weston_log_subscription_add(scope, sub); + weston_log_run_begin_cb(scope); + + /* remove it from pending */ + weston_log_subscription_destroy_pending(pending_sub); + } + + return scope; } /** Destroy a log scope * - * \param scope The log scope to destroy; may be NULL. + * @param scope The log scope to destroy; may be NULL. * - * Destroys the log scope, closing all open streams subscribed to it and - * sending them each a \c weston_debug_stream_v1.failure event. + * Destroys the log scope, calling each stream's destroy callback if one was + * installed/created. * - * \memberof weston_log_scope + * @memberof weston_log_scope */ WL_EXPORT void weston_compositor_log_scope_destroy(struct weston_log_scope *scope) @@ -529,6 +621,18 @@ weston_log_scope_is_enabled(struct weston_log_scope *scope) return !wl_list_empty(&scope->subscription_list); } +/** Close the log scope. + * + * @param scope The log scope to complete; may be NULL. + * + * Complete the log scope, calling each stream's complete callback if one was + * installed/created. This can be useful to signal the reading end that the + * data has been transmited and should no longer expect that written over the + * stream. Particularly useful for the weston-debug protocol. + * + * @memberof weston_log_scope + * @sa weston_compositor_add_log_scope, weston_compositor_log_scope_destroy + */ WL_EXPORT void weston_log_scope_complete(struct weston_log_scope *scope) { @@ -663,3 +767,44 @@ weston_log_scope_timestamp(struct weston_log_scope *scope, return buf; } + +/** Subscribe to a scope + * + * Creates a subscription which is used to subscribe the \p subscriber + * to the scope \c scope_name. + * + * If \c scope_name has already been created (using + * weston_compositor_add_log_scope) the subscription will take place + * immediately, otherwise we store the subscription into a pending list. See + * also weston_compositor_add_log_scope(). + * + * @param log_ctx the log context, used for accessing pending list + * @param subscriber the subscriber, which has to be created before + * @param scope_name the scope name. In case the scope is not created + * we temporarily store the subscription in the pending list. + */ +WL_EXPORT void +weston_log_subscribe(struct weston_log_context *log_ctx, + struct weston_log_subscriber *subscriber, + const char *scope_name) +{ + assert(log_ctx); + assert(subscriber); + assert(scope_name); + + struct weston_log_scope *scope; + struct weston_log_subscription *sub; + + scope = weston_log_get_scope(log_ctx, scope_name); + if (scope) { + sub = weston_log_subscription_create(subscriber, scope_name); + weston_log_subscription_add(scope, sub); + weston_log_run_begin_cb(scope); + } else { + /* + * if we don't have already as scope for it, add it to pending + * subscription list + */ + weston_log_subscription_create_pending(subscriber, scope_name, log_ctx); + } +}