add pthred_unlock
[platform/core/uifw/libtdm.git] / src / tdm_event_loop.c
1 /**************************************************************************
2  *
3  * libtdm
4  *
5  * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6  *
7  * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8  *          JinYoung Jeon <jy0.jeon@samsung.com>,
9  *          Taeheon Kim <th908.kim@samsung.com>,
10  *          YoungJun Cho <yj44.cho@samsung.com>,
11  *          SooChan Lim <sc1.lim@samsung.com>,
12  *          Boram Park <boram1288.park@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "tdm_private.h"
41
42 typedef struct _tdm_event_loop_source_base {
43         struct list_head link;
44         tdm_private_display *private_display;
45         struct wl_event_source *wl_source;
46 } tdm_event_loop_source_base;
47
48 typedef struct _tdm_event_loop_source_fd {
49         tdm_event_loop_source_base base;
50         tdm_event_loop_fd_handler func;
51         void *user_data;
52 } tdm_event_loop_source_fd;
53
54 typedef struct _tdm_event_loop_source_timer {
55         tdm_event_loop_source_base base;
56         tdm_event_loop_timer_handler func;
57         void *user_data;
58 } tdm_event_loop_source_timer;
59
60 static tdm_private_loop *keep_private_loop;
61
62 static tdm_error
63 _tdm_event_loop_main_fd_handler(int fd, tdm_event_loop_mask mask, void *user_data)
64 {
65         tdm_private_module *private_module = (tdm_private_module*)user_data;
66         tdm_func_display *func_display;
67         tdm_error ret;
68
69         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
70         TDM_RETURN_VAL_IF_FAIL(private_module != NULL, TDM_ERROR_OPERATION_FAILED);
71
72         if (tdm_debug_module & TDM_DEBUG_EVENT)
73                 TDM_INFO("backend fd(%d) event happens", private_module->fd);
74
75         func_display = &private_module->func_display;
76         if (!func_display->display_handle_events)
77                 return TDM_ERROR_NONE;
78
79         ret = func_display->display_handle_events(private_module->bdata);
80
81         return ret;
82 }
83
84 INTERN tdm_error
85 tdm_event_loop_init(tdm_private_display *private_display)
86 {
87         tdm_private_loop *private_loop;
88         tdm_error ret;
89
90         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
91
92         if (private_display->private_loop)
93                 return TDM_ERROR_NONE;
94
95         private_loop = calloc(1, sizeof * private_loop);
96         if (!private_loop) {
97                 TDM_ERR("alloc failed");
98                 return TDM_ERROR_OUT_OF_MEMORY;
99         }
100
101         private_loop->wl_display = wl_display_create();
102         if (!private_loop->wl_display) {
103                 TDM_ERR("creating a wayland display failed");
104                 free(private_loop);
105                 return TDM_ERROR_OUT_OF_MEMORY;
106         }
107         private_loop->wl_loop = wl_display_get_event_loop(private_loop->wl_display);
108
109         ret = tdm_server_init(private_loop);
110         if (ret != TDM_ERROR_NONE) {
111                 TDM_ERR("server init failed");
112                 wl_display_destroy(private_loop->wl_display);
113                 free(private_loop);
114                 return TDM_ERROR_OPERATION_FAILED;
115         }
116
117         LIST_INITHEAD(&private_loop->source_list);
118
119         private_loop->dpy = private_display;
120         private_display->private_loop = private_loop;
121
122         ret = tdm_thread_init(private_loop);
123         if (ret != TDM_ERROR_NONE) {
124                 TDM_ERR("thread init failed");
125                 tdm_server_deinit(private_loop);
126                 wl_display_destroy(private_loop->wl_display);
127                 free(private_loop);
128                 return TDM_ERROR_OPERATION_FAILED;
129         }
130
131         keep_private_loop = private_loop;
132
133         TDM_INFO("event loop fd(%d)", wl_event_loop_get_fd(private_loop->wl_loop));
134
135         return TDM_ERROR_NONE;
136 }
137
138 INTERN void
139 tdm_event_loop_deinit(tdm_private_display *private_display)
140 {
141         tdm_private_module *private_module = NULL;
142         tdm_event_loop_source_base *source = NULL, *ss = NULL;
143
144         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
145
146         if (!private_display->private_loop)
147                 return;
148
149         if (tdm_thread_is_running())
150                 TDM_ERR("thread is still running. tdm_event_loop_stop SHOULD be called");
151
152         tdm_server_deinit(private_display->private_loop);
153
154         LIST_FOR_EACH_ENTRY(private_module, &private_display->module_list, link) {
155                 if (private_module->event_source)
156                         tdm_event_loop_source_remove(private_module->event_source);
157                 private_module->event_source = NULL;
158                 private_module->fd = -1;
159         }
160
161         LIST_FOR_EACH_ENTRY_SAFE(source, ss, &private_display->private_loop->source_list, link) {
162                 tdm_event_loop_source_remove(source);
163         }
164
165 #if WAYLAND_VERSION_MAJOR >= 1 && WAYLAND_VERSION_MINOR >= 15
166         wl_display_destroy_clients(private_display->private_loop->wl_display);
167 #endif
168
169         wl_display_destroy(private_display->private_loop->wl_display);
170
171         free(private_display->private_loop);
172         private_display->private_loop = NULL;
173         keep_private_loop = NULL;
174
175         TDM_INFO("event loop deinit done");
176 }
177
178 INTERN void
179 tdm_event_loop_stop(tdm_private_display *private_display)
180 {
181         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
182
183         /* after tdm_thread_deinit, we don't worry about thread things because it's finalized */
184         tdm_thread_deinit(private_display->private_loop);
185 }
186
187 INTERN void
188 tdm_event_loop_create_backend_source(tdm_private_display *private_display)
189 {
190         tdm_private_loop *private_loop = private_display->private_loop;
191         tdm_private_module *private_module = NULL;
192         tdm_error ret;
193         int fd = -1;
194
195         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
196         TDM_RETURN_IF_FAIL(private_loop != NULL);
197
198         LIST_FOR_EACH_ENTRY(private_module, &private_display->module_list, link) {
199                 tdm_func_display *func_display = &private_module->func_display;
200
201                 if (!func_display->display_get_fd) {
202                         TDM_INFO("TDM backend module won't offer a display fd");
203                         private_module->event_source = NULL;
204                         private_module->fd = -1;
205                         continue;
206                 }
207
208                 ret = func_display->display_get_fd(private_module->bdata, &fd);
209
210                 assert(ret == TDM_ERROR_NONE && fd >= 0);
211                 assert(func_display->display_handle_events);
212
213                 private_module->event_source =
214                         tdm_event_loop_add_fd_handler(private_display, fd,
215                                                                                   TDM_EVENT_LOOP_READABLE,
216                                                                                   _tdm_event_loop_main_fd_handler,
217                                                                                   private_module, &ret);
218                 if (!private_module->event_source) {
219                         TDM_ERR("no backend fd(%d) source", fd);
220                         return;
221                 }
222
223                 private_module->fd = fd;
224
225                 TDM_INFO("backend fd(%d) source created", private_module->fd);
226         }
227 }
228
229 INTERN int
230 tdm_event_loop_get_fd(tdm_private_display *private_display)
231 {
232         tdm_private_loop *private_loop = private_display->private_loop;
233
234         /* DON'T check TDM_MUTEX_IS_LOCKED here */
235
236         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_loop != NULL, -1);
237
238         return wl_event_loop_get_fd(private_loop->wl_loop);
239 }
240
241 INTERN tdm_error
242 tdm_event_loop_dispatch(tdm_private_display *private_display)
243 {
244         tdm_private_loop *private_loop = private_display->private_loop;
245
246         /* DON'T check TDM_MUTEX_IS_LOCKED here */
247
248         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_loop != NULL, TDM_ERROR_OPERATION_FAILED);
249
250         if (tdm_debug_module & TDM_DEBUG_EVENT)
251                 TDM_INFO("dispatch");
252
253         if (tdm_thread_is_running() &&
254                 tdm_thread_in_display_thread(syscall(SYS_gettid))) {
255                 TDM_NEVER_GET_HERE();
256                 return TDM_ERROR_OPERATION_FAILED;
257         }
258
259         /* Don't set timeout to -1. It can make deadblock by two mutex locks.
260          * If need to set -1, use poll() and call tdm_event_loop_dispatch() after
261          * escaping polling.
262          */
263         if (wl_event_loop_dispatch(private_loop->wl_loop, 0) < 0)
264                 TDM_ERR("dispatch failed");
265
266         wl_display_flush_clients(private_loop->wl_display);
267
268         return TDM_ERROR_NONE;
269 }
270
271
272 INTERN void
273 tdm_event_loop_flush(tdm_private_display *private_display)
274 {
275         tdm_private_loop *private_loop = private_display->private_loop;
276
277         /* DON'T check TDM_MUTEX_IS_LOCKED here */
278
279         TDM_RETURN_IF_FAIL(private_loop->wl_display != NULL);
280
281         if (tdm_thread_is_running() &&
282                 tdm_thread_in_display_thread(syscall(SYS_gettid))) {
283                 TDM_NEVER_GET_HERE();
284                 return;
285         }
286
287         wl_display_flush_clients(private_loop->wl_display);
288 }
289
290 static int
291 _tdm_event_loop_fd_func(int fd, uint32_t wl_mask, void *data)
292 {
293         tdm_event_loop_source_fd *fd_source = (tdm_event_loop_source_fd*)data;
294         tdm_private_display *private_display;
295         tdm_event_loop_mask mask = 0;
296
297         /* DON'T check TDM_MUTEX_IS_LOCKED here */
298
299         TDM_RETURN_VAL_IF_FAIL(fd_source, 1);
300         TDM_RETURN_VAL_IF_FAIL(fd_source->func, 1);
301
302         private_display = fd_source->base.private_display;
303
304         if (wl_mask & WL_EVENT_READABLE)
305                 mask |= TDM_EVENT_LOOP_READABLE;
306         if (wl_mask & WL_EVENT_WRITABLE)
307                 mask |= TDM_EVENT_LOOP_WRITABLE;
308         if (wl_mask & WL_EVENT_HANGUP)
309                 mask |= TDM_EVENT_LOOP_HANGUP;
310         if (wl_mask & WL_EVENT_ERROR)
311                 mask |= TDM_EVENT_LOOP_ERROR;
312
313         _pthread_mutex_lock(&private_display->lock);
314         fd_source->func(fd, mask, fd_source->user_data);
315         _pthread_mutex_unlock(&private_display->lock);
316
317         return 1;
318 }
319
320 EXTERN tdm_event_loop_source *
321 tdm_event_loop_add_fd_handler(tdm_display *dpy, int fd, tdm_event_loop_mask mask,
322                                                           tdm_event_loop_fd_handler func, void *user_data,
323                                                           tdm_error *error)
324 {
325         tdm_private_display *private_display;
326         tdm_private_loop *private_loop;
327         tdm_event_loop_source_fd *fd_source;
328         uint32_t wl_mask = 0;
329         tdm_error ret;
330
331         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED, NULL);
332         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy, TDM_ERROR_INVALID_PARAMETER, NULL);
333         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(fd >= 0, TDM_ERROR_INVALID_PARAMETER, NULL);
334         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(func, TDM_ERROR_INVALID_PARAMETER, NULL);
335
336         private_display = (tdm_private_display*)dpy;
337         private_loop = private_display->private_loop;
338         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(private_loop, TDM_ERROR_INVALID_PARAMETER, NULL);
339         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(private_loop->wl_loop, TDM_ERROR_INVALID_PARAMETER, NULL);
340
341         fd_source = calloc(1, sizeof(tdm_event_loop_source_fd));
342         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(fd_source, TDM_ERROR_OUT_OF_MEMORY, NULL);
343
344         if (mask & TDM_EVENT_LOOP_READABLE)
345                 wl_mask |= WL_EVENT_READABLE;
346         if (mask & TDM_EVENT_LOOP_WRITABLE)
347                 wl_mask |= WL_EVENT_WRITABLE;
348
349         fd_source->func = func;
350         fd_source->user_data = user_data;
351
352         fd_source->base.private_display = private_display;
353         fd_source->base.wl_source =
354                 wl_event_loop_add_fd(private_loop->wl_loop,
355                                                          fd, wl_mask, _tdm_event_loop_fd_func, fd_source);
356         if (!fd_source->base.wl_source) {
357                 if (error)
358                         *error = TDM_ERROR_OUT_OF_MEMORY;
359                 free(fd_source);
360                 return NULL;
361         }
362
363         LIST_ADDTAIL(&fd_source->base.link, &private_loop->source_list);
364
365         if (error)
366                 *error = TDM_ERROR_NONE;
367
368         return (tdm_event_loop_source *)fd_source;
369 }
370
371 EXTERN tdm_error
372 tdm_event_loop_source_fd_update(tdm_event_loop_source *source, tdm_event_loop_mask mask)
373 {
374         tdm_event_loop_source_fd *fd_source = source;
375         uint32_t wl_mask = 0;
376
377         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
378         TDM_RETURN_VAL_IF_FAIL(fd_source, TDM_ERROR_INVALID_PARAMETER);
379
380         if (mask & TDM_EVENT_LOOP_READABLE)
381                 wl_mask |= WL_EVENT_READABLE;
382         if (mask & TDM_EVENT_LOOP_WRITABLE)
383                 wl_mask |= WL_EVENT_WRITABLE;
384
385         if (wl_event_source_fd_update(fd_source->base.wl_source, wl_mask) < 0) {
386                 TDM_ERR("source update failed: %m");
387                 return TDM_ERROR_OPERATION_FAILED;
388         }
389
390         return TDM_ERROR_NONE;
391 }
392
393 static int
394 _tdm_event_loop_timer_func(void *data)
395 {
396         tdm_event_loop_source_timer *timer_source = (tdm_event_loop_source_timer*)data;
397         tdm_private_display *private_display;
398
399         /* DON'T check TDM_MUTEX_IS_LOCKED here */
400
401         TDM_RETURN_VAL_IF_FAIL(timer_source, 1);
402         TDM_RETURN_VAL_IF_FAIL(timer_source->func, 1);
403
404         private_display = timer_source->base.private_display;
405
406         /* TDM event_loop function is actually for TDM backend module. When we call the
407          * backend's functions, we have to lock the mutex. TDM backend shouldn't consider
408          * mutex things.
409          */
410         _pthread_mutex_lock(&private_display->lock);
411         timer_source->func(timer_source->user_data);
412         _pthread_mutex_unlock(&private_display->lock);
413
414         return 1;
415 }
416
417 EXTERN tdm_event_loop_source *
418 tdm_event_loop_add_timer_handler(tdm_display *dpy, tdm_event_loop_timer_handler func,
419                                                                  void *user_data, tdm_error *error)
420 {
421         tdm_private_display *private_display;
422         tdm_private_loop *private_loop;
423         tdm_event_loop_source_timer *timer_source;
424         tdm_error ret;
425
426         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED, NULL);
427         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy, TDM_ERROR_INVALID_PARAMETER, NULL);
428         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(func, TDM_ERROR_INVALID_PARAMETER, NULL);
429
430         private_display = (tdm_private_display*)dpy;
431         private_loop = private_display->private_loop;
432         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(private_loop, TDM_ERROR_INVALID_PARAMETER, NULL);
433         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(private_loop->wl_loop, TDM_ERROR_INVALID_PARAMETER, NULL);
434
435         timer_source = calloc(1, sizeof(tdm_event_loop_source_timer));
436         TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(timer_source, TDM_ERROR_OUT_OF_MEMORY, NULL);
437
438         timer_source->func = func;
439         timer_source->user_data = user_data;
440
441         timer_source->base.private_display = private_display;
442         timer_source->base.wl_source =
443                 wl_event_loop_add_timer(private_loop->wl_loop,
444                                                                 _tdm_event_loop_timer_func, timer_source);
445         if (!timer_source->base.wl_source) {
446                 if (error)
447                         *error = TDM_ERROR_OUT_OF_MEMORY;
448                 free(timer_source);
449                 return NULL;
450         }
451
452         LIST_ADDTAIL(&timer_source->base.link, &private_loop->source_list);
453
454         if (error)
455                 *error = TDM_ERROR_NONE;
456
457         return (tdm_event_loop_source *)timer_source;
458 }
459
460 EXTERN tdm_error
461 tdm_event_loop_source_timer_update(tdm_event_loop_source *source, unsigned int ms_delay)
462 {
463         tdm_event_loop_source_timer *timer_source = source;
464
465         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED);
466         TDM_RETURN_VAL_IF_FAIL(timer_source, TDM_ERROR_INVALID_PARAMETER);
467
468         if (wl_event_source_timer_update(timer_source->base.wl_source, ms_delay) < 0) {
469                 TDM_ERR("source update failed: %m");
470                 return TDM_ERROR_OPERATION_FAILED;
471         }
472
473         return TDM_ERROR_NONE;
474 }
475
476 EXTERN void
477 tdm_event_loop_source_remove(tdm_event_loop_source *source)
478 {
479         tdm_event_loop_source_base *base = (tdm_event_loop_source_base*)source;
480
481         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
482
483         if (!base || !keep_private_loop)
484                 return;
485
486         LIST_DEL(&base->link);
487
488         wl_event_source_remove(base->wl_source);
489
490         free(source);
491 }
492
493 /* LCOV_EXCL_START */
494 static void
495 _trace_cb_client_destroy(struct wl_listener *listener, void *data)
496 {
497         struct wl_client *client = (struct wl_client *) data;
498         struct timespec tp;
499         unsigned int time;
500         pid_t pid = -1;
501         const char *proc_name;
502         char temp[512] = { 0, }, *p = temp;
503         int len = sizeof(temp), *l = &len;
504
505         wl_client_get_credentials(client, &pid, NULL, NULL);
506         proc_name = tdm_server_get_client_name(pid);
507
508         clock_gettime(CLOCK_MONOTONIC, &tp);
509         time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
510
511         TDM_SNPRINTF(p, l, "[%10.3f] Server           [PID:%d] client destroying", time / 1000.0, pid);
512         TDM_SNPRINTF(p, l, ", cmd: %s", proc_name ? proc_name : "Unknown");
513
514         TDM_INFO("%s", temp);
515
516         wl_list_remove(&listener->link);
517         free(listener);
518 }
519
520 static void
521 _trace_reg_client_destroy_listener(struct wl_client *client)
522 {
523         struct wl_listener *listener;
524
525         listener = wl_client_get_destroy_listener(client, _trace_cb_client_destroy);
526         if (listener)
527                 return;
528
529         listener = calloc(1, sizeof(struct wl_listener));
530         TDM_RETURN_IF_FAIL(listener != NULL);
531
532         listener->notify = _trace_cb_client_destroy;
533         wl_client_add_destroy_listener(client, listener);
534 }
535
536 static const char *
537 _trace_get_next_argument(const char *signature,
538                                                  struct argument_details *details)
539 {
540         details->nullable = 0;
541         for (; *signature; ++signature) {
542                 switch (*signature) {
543                 case 'i':
544                 case 'u':
545                 case 'f':
546                 case 's':
547                 case 'o':
548                 case 'n':
549                 case 'a':
550                 case 'h':
551                         details->type = *signature;
552                         return signature + 1;
553                 case '?':
554                         details->nullable = 1;
555                         break;
556                 default:
557                         return NULL;
558                 }
559         }
560         details->type = '\0';
561         return signature;
562 }
563
564 static struct wl_protocol_logger *_trace_protocol_logger;
565
566 static void
567 _trace_protocol_logger_cb(void *user_data,
568                                                   enum wl_protocol_logger_type direction,
569                                                   const struct wl_protocol_logger_message *message)
570 {
571         int i;
572         struct argument_details arg;
573         struct wl_client *client = wl_resource_get_client(message->resource);
574         const char *signature = message->message->signature;
575         struct timespec tp;
576         unsigned int time;
577         pid_t pid = -1;
578         const char *proc_name;
579         char temp[512] = { 0, }, *p = temp;
580         int len = sizeof(temp), *l = &len;
581         int send;
582
583         if (client) {
584                 _trace_reg_client_destroy_listener(client);
585                 wl_client_get_credentials(client, &pid, NULL, NULL);
586         }
587
588         proc_name = tdm_server_get_client_name(pid);
589
590         clock_gettime(CLOCK_MONOTONIC, &tp);
591         time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
592
593         send = (direction == WL_PROTOCOL_LOGGER_EVENT) ? 1 : 0;
594
595         TDM_SNPRINTF(p, l, "[%10.3f] %s%d%s%s@%u.%s(",
596                                  time / 1000.0,
597                                  send ? "Server -> Client [PID:" : "Server <- Client [PID:",
598                                  pid, "] ",
599                                  wl_resource_get_class(message->resource),
600                                  wl_resource_get_id(message->resource),
601                                  message->message->name);
602
603         for (i = 0; i < message->arguments_count; i++) {
604                 signature = _trace_get_next_argument(signature, &arg);
605                 TDM_RETURN_IF_FAIL(signature != NULL);
606
607                 if (i > 0)
608                         TDM_SNPRINTF(p, l, ", ");
609
610                 switch (arg.type) {
611                 case 'u':
612                         TDM_SNPRINTF(p, l, "%u", message->arguments[i].u);
613                         break;
614                 case 'i':
615                         TDM_SNPRINTF(p, l, "%d", message->arguments[i].i);
616                         break;
617                 case 'f':
618                         TDM_SNPRINTF(p, l, "%f",
619                                                  wl_fixed_to_double(message->arguments[i].f));
620                         break;
621                 case 's':
622                         TDM_SNPRINTF(p, l, "\"%s\"", message->arguments[i].s);
623                         break;
624                 case 'o':
625                         if (message->arguments[i].o)
626                                 TDM_SNPRINTF(p, l, "%s@%u",
627                                                          wl_resource_get_class((struct wl_resource *) message->arguments[i].o),
628                                                          wl_resource_get_id((struct wl_resource *) message->arguments[i].o));
629                         else
630                                 TDM_SNPRINTF(p, l, "nil");
631                         break;
632                 case 'n':
633                         TDM_SNPRINTF(p, l, "new id %s@",
634                                                  (message->message->types[i]) ? message->message->types[i]->name : "[unknown]");
635                         if (message->arguments[i].n != 0)
636                                 TDM_SNPRINTF(p, l, "%u", message->arguments[i].n);
637                         else
638                                 TDM_SNPRINTF(p, l, "nil");
639                         break;
640                 case 'a':
641                         TDM_SNPRINTF(p, l, "array");
642                         break;
643                 case 'h':
644                         TDM_SNPRINTF(p, l, "fd %d", message->arguments[i].h);
645                         break;
646                 default:
647                         return;
648                 }
649         }
650
651         TDM_SNPRINTF(p, l, "), cmd: %s", proc_name ? proc_name : "Unknown");
652
653         TDM_INFO("%s", temp);
654 }
655
656 INTERN tdm_error
657 tdm_event_loop_trace_enable(tdm_private_display * private_display,
658                                                         unsigned int enable)
659 {
660         tdm_private_loop *private_loop = private_display->private_loop;
661
662         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display != NULL, TDM_ERROR_NONE);
663
664         if (!enable) {
665                 if (_trace_protocol_logger) {
666                         wl_protocol_logger_destroy(_trace_protocol_logger);
667                         _trace_protocol_logger = NULL;
668                 }
669                 return TDM_ERROR_NONE;
670         }
671
672         if (_trace_protocol_logger)
673                 wl_protocol_logger_destroy(_trace_protocol_logger);
674
675         _trace_protocol_logger =
676                 wl_display_add_protocol_logger(private_loop->wl_display, _trace_protocol_logger_cb, NULL);
677
678         return TDM_ERROR_NONE;
679 }
680 /* LCOV_EXCL_STOP */