/**************************************************************************
-
-libtdm
-
-Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
-
-Contact: Eunchul Kim <chulspro.kim@samsung.com>,
- JinYoung Jeon <jy0.jeon@samsung.com>,
- Taeheon Kim <th908.kim@samsung.com>,
- YoungJun Cho <yj44.cho@samsung.com>,
- SooChan Lim <sc1.lim@samsung.com>,
- Boram Park <sc1.lim@samsung.com>
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sub license, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice (including the
-next paragraph) shall be included in all copies or substantial portions
-of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
-IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
+ *
+ * libtdm
+ *
+ * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
+ * JinYoung Jeon <jy0.jeon@samsung.com>,
+ * Taeheon Kim <th908.kim@samsung.com>,
+ * YoungJun Cho <yj44.cho@samsung.com>,
+ * SooChan Lim <sc1.lim@samsung.com>,
+ * Boram Park <sc1.lim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
**************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include "tdm.h"
#include "tdm_private.h"
-#include "tdm_list.h"
-#include <wayland-server-core.h>
-
-typedef struct _tdm_event_loop_source_base
-{
+typedef struct _tdm_event_loop_source_base {
struct wl_event_source *wl_source;
} tdm_event_loop_source_base;
-typedef struct _tdm_event_loop_source_fd
-{
+typedef struct _tdm_event_loop_source_fd {
tdm_event_loop_source_base base;
tdm_private_display *private_display;
tdm_event_loop_fd_handler func;
void *user_data;
} tdm_event_loop_source_fd;
-typedef struct _tdm_event_loop_source_timer
-{
+typedef struct _tdm_event_loop_source_timer {
tdm_event_loop_source_base base;
tdm_private_display *private_display;
tdm_event_loop_timer_handler func;
static tdm_error
_tdm_event_loop_main_fd_handler(int fd, tdm_event_loop_mask mask, void *user_data)
{
- tdm_private_display *private_display = (tdm_private_display*)user_data;
- tdm_private_loop *private_loop;
+ tdm_private_module *private_module = (tdm_private_module*)user_data;
tdm_func_display *func_display;
tdm_error ret;
- TDM_RETURN_VAL_IF_FAIL(private_display != NULL, TDM_ERROR_OPERATION_FAILED);
- TDM_RETURN_VAL_IF_FAIL(private_display->private_loop != NULL, TDM_ERROR_OPERATION_FAILED);
+ TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
+ TDM_RETURN_VAL_IF_FAIL(private_module != NULL, TDM_ERROR_OPERATION_FAILED);
- private_loop = private_display->private_loop;
-
- if (tdm_debug_thread)
- TDM_INFO("backend fd(%d) event happens", private_loop->backend_fd);
+ if (tdm_debug_module & TDM_DEBUG_EVENT)
+ TDM_INFO("backend fd(%d) event happens", private_module->fd);
- func_display = &private_display->func_display;
+ func_display = &private_module->func_display;
if (!func_display->display_handle_events)
return TDM_ERROR_NONE;
- ret = func_display->display_handle_events(private_display->bdata);
+ ret = func_display->display_handle_events(private_module->bdata);
return ret;
}
tdm_private_loop *private_loop;
tdm_error ret;
+ TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
+
if (private_display->private_loop)
return TDM_ERROR_NONE;
- private_loop = calloc(1, sizeof *private_loop);
+ private_loop = calloc(1, sizeof * private_loop);
if (!private_loop) {
TDM_ERR("alloc failed");
return TDM_ERROR_OUT_OF_MEMORY;
}
- private_loop->backend_fd = -1;
-
private_loop->wl_display = wl_display_create();
if (!private_loop->wl_display) {
TDM_ERR("creating a wayland display failed");
INTERN void
tdm_event_loop_deinit(tdm_private_display *private_display)
{
+ tdm_private_module *private_module = NULL;
+
+ TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
+
if (!private_display->private_loop)
return;
- tdm_thread_deinit(private_display->private_loop);
+ if (tdm_thread_is_running())
+ TDM_ERR("thread is still running. tdm_event_loop_stop SHOULD be called");
+
tdm_server_deinit(private_display->private_loop);
- if (private_display->private_loop->backend_source)
- tdm_event_loop_source_remove(private_display->private_loop->backend_source);
+ LIST_FOR_EACH_ENTRY(private_module, &private_display->module_list, link) {
+ if (private_module->event_source)
+ tdm_event_loop_source_remove(private_module->event_source);
+ private_module->event_source = NULL;
+ private_module->fd = -1;
+ }
if (private_display->private_loop->wl_display)
wl_display_destroy(private_display->private_loop->wl_display);
}
INTERN void
+tdm_event_loop_stop(tdm_private_display *private_display)
+{
+ TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
+
+ /* after tdm_thread_deinit, we don't worry about thread things because it's finalized */
+ tdm_thread_deinit(private_display->private_loop);
+}
+
+INTERN void
tdm_event_loop_create_backend_source(tdm_private_display *private_display)
{
tdm_private_loop *private_loop = private_display->private_loop;
- tdm_func_display *func_display;
+ tdm_private_module *private_module = NULL;
tdm_error ret;
int fd = -1;
+ TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
TDM_RETURN_IF_FAIL(private_loop != NULL);
- func_display = &private_display->func_display;
- if (!func_display->display_get_fd) {
- TDM_INFO("TDM backend module won't offer a display fd");
- return;
- }
+ LIST_FOR_EACH_ENTRY(private_module, &private_display->module_list, link) {
+ tdm_func_display *func_display = &private_module->func_display;
- ret = func_display->display_get_fd(private_display->bdata, &fd);
- if (fd < 0) {
- TDM_WRN("TDM backend module returns fd(%d)", fd);
- return;
- }
+ if (!func_display->display_get_fd) {
+ TDM_INFO("TDM backend module won't offer a display fd");
+ private_module->event_source = NULL;
+ private_module->fd = -1;
+ continue;
+ }
- if (!func_display->display_handle_events) {
- TDM_ERR("no display_handle_events function");
- return;
- }
+ ret = func_display->display_get_fd(private_module->bdata, &fd);
- private_loop->backend_source =
- tdm_event_loop_add_fd_handler(private_display, fd,
- TDM_EVENT_LOOP_READABLE,
- _tdm_event_loop_main_fd_handler,
- private_display, &ret);
- if (!private_loop->backend_source) {
- TDM_ERR("no backend fd(%d) source", fd);
- return;
- }
+ assert(ret == TDM_ERROR_NONE && fd >= 0);
+ assert(func_display->display_handle_events);
+
+ private_module->event_source =
+ tdm_event_loop_add_fd_handler(private_display, fd,
+ TDM_EVENT_LOOP_READABLE,
+ _tdm_event_loop_main_fd_handler,
+ private_module, &ret);
+ if (!private_module->event_source) {
+ TDM_ERR("no backend fd(%d) source", fd);
+ return;
+ }
- private_loop->backend_fd = fd;
+ private_module->fd = fd;
- TDM_INFO("backend fd(%d) source created", private_loop->backend_fd);
+ TDM_INFO("backend fd(%d) source created", private_module->fd);
+ }
}
INTERN int
{
tdm_private_loop *private_loop = private_display->private_loop;
+ /* DON'T check TDM_MUTEX_IS_LOCKED here */
+
TDM_RETURN_VAL_IF_FAIL(private_loop->wl_loop != NULL, -1);
return wl_event_loop_get_fd(private_loop->wl_loop);
{
tdm_private_loop *private_loop = private_display->private_loop;
+ /* DON'T check TDM_MUTEX_IS_LOCKED here */
+
TDM_RETURN_VAL_IF_FAIL(private_loop->wl_loop != NULL, TDM_ERROR_OPERATION_FAILED);
- if (tdm_debug_thread)
+ if (tdm_debug_module & TDM_DEBUG_EVENT)
TDM_INFO("dispatch");
+ if (tdm_thread_is_running() &&
+ tdm_thread_in_display_thread(syscall(SYS_gettid))) {
+ TDM_NEVER_GET_HERE();
+ return TDM_ERROR_OPERATION_FAILED;
+ }
+
/* Don't set timeout to -1. It can make deadblock by two mutex locks.
* If need to set -1, use poll() and call tdm_event_loop_dispatch() after
* escaping polling.
if (wl_event_loop_dispatch(private_loop->wl_loop, 0) < 0)
TDM_ERR("dispatch failed");
+ wl_display_flush_clients(private_loop->wl_display);
+
return TDM_ERROR_NONE;
}
{
tdm_private_loop *private_loop = private_display->private_loop;
+ /* DON'T check TDM_MUTEX_IS_LOCKED here */
+
TDM_RETURN_IF_FAIL(private_loop->wl_display != NULL);
+ if (tdm_thread_is_running() &&
+ tdm_thread_in_display_thread(syscall(SYS_gettid))) {
+ TDM_NEVER_GET_HERE();
+ return;
+ }
+
wl_display_flush_clients(private_loop->wl_display);
}
_tdm_event_loop_fd_func(int fd, uint32_t wl_mask, void *data)
{
tdm_event_loop_source_fd *fd_source = (tdm_event_loop_source_fd*)data;
+ tdm_private_display *private_display;
tdm_event_loop_mask mask = 0;
+ /* DON'T check TDM_MUTEX_IS_LOCKED here */
+
TDM_RETURN_VAL_IF_FAIL(fd_source, 1);
TDM_RETURN_VAL_IF_FAIL(fd_source->func, 1);
+ private_display = fd_source->private_display;
+
if (wl_mask & WL_EVENT_READABLE)
mask |= TDM_EVENT_LOOP_READABLE;
if (wl_mask & WL_EVENT_WRITABLE)
if (wl_mask & WL_EVENT_ERROR)
mask |= TDM_EVENT_LOOP_ERROR;
+ _pthread_mutex_lock(&private_display->lock);
fd_source->func(fd, mask, fd_source->user_data);
+ _pthread_mutex_unlock(&private_display->lock);
return 1;
}
-EXTERN tdm_event_loop_source*
+EXTERN tdm_event_loop_source *
tdm_event_loop_add_fd_handler(tdm_display *dpy, int fd, tdm_event_loop_mask mask,
- tdm_event_loop_fd_handler func, void *user_data,
- tdm_error *error)
+ tdm_event_loop_fd_handler func, void *user_data,
+ tdm_error *error)
{
tdm_private_display *private_display;
tdm_private_loop *private_loop;
uint32_t wl_mask = 0;
tdm_error ret;
+ TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED, NULL);
TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy, TDM_ERROR_INVALID_PARAMETER, NULL);
TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(fd >= 0, TDM_ERROR_INVALID_PARAMETER, NULL);
TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(func, TDM_ERROR_INVALID_PARAMETER, NULL);
if (mask & TDM_EVENT_LOOP_WRITABLE)
wl_mask |= WL_EVENT_WRITABLE;
+ fd_source->private_display = private_display;
+ fd_source->func = func;
+ fd_source->user_data = user_data;
+
fd_source->base.wl_source =
wl_event_loop_add_fd(private_loop->wl_loop,
- fd, wl_mask, _tdm_event_loop_fd_func, fd_source);
+ fd, wl_mask, _tdm_event_loop_fd_func, fd_source);
if (!fd_source->base.wl_source) {
if (error)
*error = TDM_ERROR_OUT_OF_MEMORY;
return NULL;
}
- fd_source->private_display = private_display;
- fd_source->func = func;
- fd_source->user_data = user_data;
-
if (error)
*error = TDM_ERROR_NONE;
- return (tdm_event_loop_source*)fd_source;
+ return (tdm_event_loop_source *)fd_source;
}
EXTERN tdm_error
tdm_event_loop_source_fd *fd_source = source;
uint32_t wl_mask = 0;
+ TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
TDM_RETURN_VAL_IF_FAIL(fd_source, TDM_ERROR_INVALID_PARAMETER);
if (mask & TDM_EVENT_LOOP_READABLE)
_tdm_event_loop_timer_func(void *data)
{
tdm_event_loop_source_timer *timer_source = (tdm_event_loop_source_timer*)data;
+ tdm_private_display *private_display;
+
+ /* DON'T check TDM_MUTEX_IS_LOCKED here */
TDM_RETURN_VAL_IF_FAIL(timer_source, 1);
TDM_RETURN_VAL_IF_FAIL(timer_source->func, 1);
+ private_display = timer_source->private_display;
+
+ /* TDM event_loop function is actually for TDM backend module. When we call the
+ * backend's functions, we have to lock the mutex. TDM backend shouldn't consider
+ * mutex things.
+ */
+ _pthread_mutex_lock(&private_display->lock);
timer_source->func(timer_source->user_data);
+ _pthread_mutex_unlock(&private_display->lock);
return 1;
}
-EXTERN tdm_event_loop_source*
+EXTERN tdm_event_loop_source *
tdm_event_loop_add_timer_handler(tdm_display *dpy, tdm_event_loop_timer_handler func,
- void *user_data, tdm_error *error)
+ void *user_data, tdm_error *error)
{
tdm_private_display *private_display;
tdm_private_loop *private_loop;
tdm_event_loop_source_timer *timer_source;
tdm_error ret;
+ TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED, NULL);
TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy, TDM_ERROR_INVALID_PARAMETER, NULL);
TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(func, TDM_ERROR_INVALID_PARAMETER, NULL);
timer_source = calloc(1, sizeof(tdm_event_loop_source_timer));
TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(timer_source, TDM_ERROR_OUT_OF_MEMORY, NULL);
+ timer_source->private_display = private_display;
+ timer_source->func = func;
+ timer_source->user_data = user_data;
+
timer_source->base.wl_source =
wl_event_loop_add_timer(private_loop->wl_loop,
- _tdm_event_loop_timer_func, timer_source);
+ _tdm_event_loop_timer_func, timer_source);
if (!timer_source->base.wl_source) {
if (error)
*error = TDM_ERROR_OUT_OF_MEMORY;
return NULL;
}
- timer_source->private_display = private_display;
- timer_source->func = func;
- timer_source->user_data = user_data;
-
if (error)
*error = TDM_ERROR_NONE;
- return (tdm_event_loop_source*)timer_source;
+ return (tdm_event_loop_source *)timer_source;
}
EXTERN tdm_error
-tdm_event_loop_source_timer_update(tdm_event_loop_source *source, int ms_delay)
+tdm_event_loop_source_timer_update(tdm_event_loop_source *source, unsigned int ms_delay)
{
tdm_event_loop_source_timer *timer_source = source;
+ TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
TDM_RETURN_VAL_IF_FAIL(timer_source, TDM_ERROR_INVALID_PARAMETER);
if (wl_event_source_timer_update(timer_source->base.wl_source, ms_delay) < 0) {
{
tdm_event_loop_source_base *base = (tdm_event_loop_source_base*)source;
+ TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
+
if (!base)
return;
free(source);
}
+
+/* LCOV_EXCL_START */
+static void
+_trace_cb_client_destroy(struct wl_listener *listener, void *data)
+{
+ struct wl_client *client = (struct wl_client *) data;
+ struct timespec tp;
+ unsigned int time;
+ pid_t pid = -1;
+ const char *proc_name;
+ char temp[512] = { 0, }, *p = temp;
+ int len = sizeof(temp), *l = &len;
+
+ wl_client_get_credentials(client, &pid, NULL, NULL);
+ proc_name = tdm_server_get_client_name(pid);
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+ TDM_SNPRINTF(p, l, "[%10.3f] Server [PID:%d] client destroying", time / 1000.0, pid);
+ TDM_SNPRINTF(p, l, ", cmd: %s", proc_name ? proc_name : "Unknown");
+
+ TDM_INFO("%s", temp);
+
+ wl_list_remove(&listener->link);
+ free(listener);
+}
+
+static void
+_trace_reg_client_destroy_listener(struct wl_client *client)
+{
+ struct wl_listener *listener;
+
+ listener = wl_client_get_destroy_listener(client, _trace_cb_client_destroy);
+ if (listener)
+ return;
+
+ listener = calloc(1, sizeof(struct wl_listener));
+ TDM_RETURN_IF_FAIL(listener != NULL);
+
+ listener->notify = _trace_cb_client_destroy;
+ wl_client_add_destroy_listener(client, listener);
+}
+
+static const char *
+_trace_get_next_argument(const char *signature,
+ struct argument_details *details)
+{
+ details->nullable = 0;
+ for (; *signature; ++signature) {
+ switch (*signature) {
+ case 'i':
+ case 'u':
+ case 'f':
+ case 's':
+ case 'o':
+ case 'n':
+ case 'a':
+ case 'h':
+ details->type = *signature;
+ return signature + 1;
+ case '?':
+ details->nullable = 1;
+ break;
+ default:
+ return NULL;
+ }
+ }
+ details->type = '\0';
+ return signature;
+}
+
+static struct wl_protocol_logger *_trace_protocol_logger;
+
+static void
+_trace_protocol_logger_cb(void *user_data,
+ enum wl_protocol_logger_type direction,
+ const struct wl_protocol_logger_message *message)
+{
+ int i;
+ struct argument_details arg;
+ struct wl_client *client = wl_resource_get_client(message->resource);
+ const char *signature = message->message->signature;
+ struct timespec tp;
+ unsigned int time;
+ pid_t pid = -1;
+ const char *proc_name;
+ char temp[512] = { 0, }, *p = temp;
+ int len = sizeof(temp), *l = &len;
+ int send;
+
+ if (client) {
+ _trace_reg_client_destroy_listener(client);
+ wl_client_get_credentials(client, &pid, NULL, NULL);
+ }
+
+ proc_name = tdm_server_get_client_name(pid);
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+ send = (direction == WL_PROTOCOL_LOGGER_EVENT) ? 1 : 0;
+
+ TDM_SNPRINTF(p, l, "[%10.3f] %s%d%s%s@%u.%s(",
+ time / 1000.0,
+ send ? "Server -> Client [PID:" : "Server <- Client [PID:",
+ pid, "] ",
+ wl_resource_get_class(message->resource),
+ wl_resource_get_id(message->resource),
+ message->message->name);
+
+ for (i = 0; i < message->arguments_count; i++) {
+ signature = _trace_get_next_argument(signature, &arg);
+ TDM_RETURN_IF_FAIL(signature != NULL);
+
+ if (i > 0)
+ TDM_SNPRINTF(p, l, ", ");
+
+ switch (arg.type) {
+ case 'u':
+ TDM_SNPRINTF(p, l, "%u", message->arguments[i].u);
+ break;
+ case 'i':
+ TDM_SNPRINTF(p, l, "%d", message->arguments[i].i);
+ break;
+ case 'f':
+ TDM_SNPRINTF(p, l, "%f",
+ wl_fixed_to_double(message->arguments[i].f));
+ break;
+ case 's':
+ TDM_SNPRINTF(p, l, "\"%s\"", message->arguments[i].s);
+ break;
+ case 'o':
+ if (message->arguments[i].o)
+ TDM_SNPRINTF(p, l, "%s@%u",
+ wl_resource_get_class((struct wl_resource *) message->arguments[i].o),
+ wl_resource_get_id((struct wl_resource *) message->arguments[i].o));
+ else
+ TDM_SNPRINTF(p, l, "nil");
+ break;
+ case 'n':
+ TDM_SNPRINTF(p, l, "new id %s@",
+ (message->message->types[i]) ? message->message->types[i]->name : "[unknown]");
+ if (message->arguments[i].n != 0)
+ TDM_SNPRINTF(p, l, "%u", message->arguments[i].n);
+ else
+ TDM_SNPRINTF(p, l, "nil");
+ break;
+ case 'a':
+ TDM_SNPRINTF(p, l, "array");
+ break;
+ case 'h':
+ TDM_SNPRINTF(p, l, "fd %d", message->arguments[i].h);
+ break;
+ default:
+ return;
+ }
+ }
+
+ TDM_SNPRINTF(p, l, "), cmd: %s", proc_name ? proc_name : "Unknown");
+
+ TDM_INFO("%s", temp);
+}
+
+INTERN tdm_error
+tdm_event_loop_trace_enable(tdm_private_display * private_display,
+ unsigned int enable)
+{
+ tdm_private_loop *private_loop = private_display->private_loop;
+
+ TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display != NULL, TDM_ERROR_NONE);
+
+ if (!enable) {
+ if (_trace_protocol_logger) {
+ wl_protocol_logger_destroy(_trace_protocol_logger);
+ _trace_protocol_logger = NULL;
+ }
+ return TDM_ERROR_NONE;
+ }
+
+ if (_trace_protocol_logger)
+ wl_protocol_logger_destroy(_trace_protocol_logger);
+
+ _trace_protocol_logger =
+ wl_display_add_protocol_logger(private_loop->wl_display, _trace_protocol_logger_cb, NULL);
+
+ return TDM_ERROR_NONE;
+}
+/* LCOV_EXCL_STOP */