client: let a server know the watch status
[platform/core/uifw/libtdm.git] / client / tdm_client.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 <sc1.lim@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 <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <time.h>
44 #include <strings.h>
45
46 #include <tdm-client-protocol.h>
47
48 #include "tdm_client.h"
49 #include "tdm_log.h"
50 #include "tdm_macro.h"
51 #include "tdm_list.h"
52 #include "tdm.h"
53 #include "tdm_private.h"
54
55 typedef struct _tdm_private_client_vblank tdm_private_client_vblank;
56
57 typedef struct _tdm_private_client {
58         pthread_mutex_t lock;
59
60         struct wl_display *display;
61         struct wl_event_queue *queue;
62         struct wl_registry *registry;
63         struct wl_tdm *tdm;
64         struct list_head output_list;
65
66         unsigned int enable_ttrace;
67         unsigned int stamp;
68
69         tdm_private_client_vblank *temp_vblank;
70 } tdm_private_client;
71
72 typedef struct _tdm_private_client_output {
73         tdm_private_client *private_client;
74
75         char name[TDM_NAME_LEN];
76         struct wl_tdm_output *output;
77         int width;
78         int height;
79         int refresh;
80         tdm_output_conn_status connection;
81         tdm_output_dpms dpms;
82         struct list_head vblank_list;
83         struct list_head change_handler_list;
84
85         unsigned int req_id;
86         unsigned int watch_output_changes;
87
88         struct list_head link;
89 } tdm_private_client_output;
90
91 struct _tdm_private_client_vblank {
92         tdm_private_client_output *private_output;
93
94         struct wl_tdm_vblank *vblank;
95         struct list_head wait_list;
96
97         char name[TDM_NAME_LEN];
98         unsigned int sync;
99         unsigned int fps;
100         int offset;
101         unsigned int enable_fake;
102         unsigned int enable_ttrace;
103
104         unsigned int started;
105         unsigned int stamp;
106
107         double req_time;
108         double last_time;
109
110         struct list_head link;
111 };
112
113 typedef struct _tdm_client_output_handler_info {
114         tdm_private_client_output *private_output;
115
116         tdm_client_output_change_handler func;
117         void *user_data;
118
119         struct list_head link;
120         struct list_head call_link;
121 } tdm_client_output_handler_info;
122
123 typedef struct _tdm_client_wait_info {
124         tdm_private_client_vblank *private_vblank;
125
126         tdm_client_vblank_handler func;
127         void *user_data;
128
129         unsigned int req_id;
130         double req_time;
131         int need_free;
132
133         struct list_head link;
134         struct list_head call_link;
135 } tdm_client_wait_info;
136
137 static unsigned int
138 _tdm_client_check_wl_error(tdm_private_client *private_client, const char *func, int line)
139 {
140         uint32_t ec, id;
141         const struct wl_interface *intf;
142         int err;
143
144         err = wl_display_get_error(private_client->display);
145         if (!err)
146                 return false;
147
148         if (err == EINVAL || err == ENOMEM || err == EFAULT || err == EPROTO) {
149                 ec = wl_display_get_protocol_error(private_client->display, &intf, &id);
150                 TDM_ERR("[%s,%d] errno(%d) Got protocol error '%u' on interface '%s' (object '%u')",
151                                 func, line, err, ec, (intf) ? intf->name : "destroyed", id);
152         } else {
153                 TDM_ERR("[%s,%d] errno(%d)", func, line, err);
154         }
155
156         return true;
157 }
158
159 #define CHECK_WL_PROTOCOL_ERROR(pc)  _tdm_client_check_wl_error(pc, __FUNCTION__, __LINE__)
160
161 static void
162 _tdm_client_vblank_cb_stamp(void *data, struct wl_tdm_vblank *wl_tdm_vblank, uint32_t stamp)
163 {
164         tdm_private_client_vblank *private_vblank = data;
165         tdm_private_client *private_client;
166
167         TDM_RETURN_IF_FAIL(private_vblank != NULL);
168
169         private_vblank->stamp = stamp;
170
171         TDM_RETURN_IF_FAIL(private_vblank->private_output != NULL);
172         private_client = private_vblank->private_output->private_client;
173
174         private_client->stamp = stamp;
175 }
176
177 /* LCOV_EXCL_START */
178 static void
179 _tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank,
180                                                    uint32_t req_id, uint32_t sequence, uint32_t tv_sec,
181                                                    uint32_t tv_usec, uint32_t error)
182 {
183         tdm_private_client_vblank *private_vblank = data;
184         tdm_private_client *private_client;
185         tdm_client_wait_info *w = NULL, *wait_info = NULL;
186
187         TDM_RETURN_IF_FAIL(private_vblank != NULL);
188
189         private_client = private_vblank->private_output->private_client;
190
191         private_vblank->last_time = TDM_TIME(tv_sec, tv_usec);
192
193         TDM_DBG("vblank(%p) req_id(%u) sequence(%u) time(%.6f)",
194                         private_vblank, req_id, sequence, TDM_TIME(tv_sec, tv_usec));
195
196         LIST_FOR_EACH_ENTRY(w, &private_vblank->wait_list, link) {
197                 if (w->req_id != req_id)
198                         continue;
199
200                 wait_info = w;
201                 break;
202         }
203
204         if (!wait_info) {
205                 TDM_ERR("no wait infomation for req_id(%d)", req_id);
206                 return;
207         }
208
209         if (private_vblank->enable_ttrace)
210                 TDM_TRACE_ASYNC_END((int)wait_info->req_time, "TDM_Client_Vblank:%u", private_vblank->stamp);
211
212         if (wait_info->req_time >= private_vblank->last_time)
213                 TDM_WRN("'req(%.6f) < last(%.6f)' failed. error(%d)", wait_info->req_time, private_vblank->last_time, error);
214
215         if (wait_info->need_free)
216                 LIST_DEL(&wait_info->link);
217
218         if (wait_info->func) {
219                 pthread_mutex_unlock(&private_client->lock);
220                 wait_info->func(private_vblank, error, sequence, tv_sec, tv_usec, wait_info->user_data);
221                 pthread_mutex_lock(&private_client->lock);
222         }
223
224         if (wait_info->need_free)
225                 free(w);
226         else
227                 wait_info->need_free = 1;
228 }
229 /* LCOV_EXCL_STOP */
230
231 /* LCOV_EXCL_START */
232 static void
233 _tdm_client_vblank_cb_ttrace(void *data, struct wl_tdm_vblank *wl_tdm_vblank, uint32_t enable)
234 {
235         tdm_private_client_vblank *private_vblank = data;
236         tdm_private_client *private_client;
237
238         TDM_RETURN_IF_FAIL(private_vblank != NULL);
239
240         private_vblank->enable_ttrace = enable;
241
242         TDM_RETURN_IF_FAIL(private_vblank->private_output != NULL);
243         private_client = private_vblank->private_output->private_client;
244
245         private_client->enable_ttrace = enable;
246 }
247 /* LCOV_EXCL_STOP */
248
249 static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = {
250         _tdm_client_vblank_cb_stamp,
251         _tdm_client_vblank_cb_done,
252         _tdm_client_vblank_cb_ttrace,
253 };
254
255 static void
256 _tdm_client_output_destroy(tdm_private_client_output *private_output)
257 {
258         tdm_private_client_vblank *v = NULL, *vv = NULL;
259         tdm_client_output_handler_info *h = NULL, *hh = NULL;
260
261         LIST_DEL(&private_output->link);
262
263         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_list, link) {
264                 TDM_ERR("vblanks SHOULD be destroyed first!");
265                 LIST_DEL(&v->link);
266                 v->private_output = NULL;
267         }
268
269         LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_output->change_handler_list, link) {
270                 LIST_DEL(&h->link);
271                 free(h);
272         }
273
274         wl_tdm_output_destroy(private_output->output);
275
276         free(private_output);
277 }
278
279 static void
280 _tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output,
281                                                    uint32_t width, uint32_t height, uint32_t refresh, uint32_t error)
282 {
283         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
284
285         TDM_RETURN_IF_FAIL(private_output != NULL);
286
287         private_output->width = width;
288         private_output->height = height;
289         private_output->refresh = refresh;
290
291         if (error != TDM_ERROR_NONE)
292                 TDM_INFO("mode event error: %d", error);
293
294         TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)",
295                         private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output),
296                         width, height, refresh);
297 }
298
299 static void
300 _tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value, uint32_t error)
301 {
302         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
303         tdm_private_client *private_client;
304         tdm_client_output_handler_info *h = NULL, *hh = NULL;
305         tdm_value v;
306         struct list_head call_list;
307
308         TDM_RETURN_IF_FAIL(private_output != NULL);
309
310         private_client = private_output->private_client;
311
312         if (private_output->connection == value)
313                 return;
314
315         private_output->connection = value;
316
317         if (error != TDM_ERROR_NONE)
318                 TDM_INFO("connection event error: %d", error);
319
320         TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)",
321                         private_output,
322                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
323                         value);
324
325         LIST_INITHEAD(&call_list);
326
327         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
328                 LIST_ADDTAIL(&h->call_link, &call_list);
329         }
330
331         v.u32 = value;
332         pthread_mutex_unlock(&private_client->lock);
333         LIST_FOR_EACH_ENTRY_SAFE(h, hh, &call_list, call_link) {
334                 if (h->func)
335                         h->func(private_output, TDM_OUTPUT_CHANGE_CONNECTION, v, h->user_data);
336         }
337         pthread_mutex_lock(&private_client->lock);
338 }
339
340 static void
341 _tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value, uint32_t error)
342 {
343         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
344         tdm_private_client *private_client;
345         tdm_client_output_handler_info *h = NULL, *hh = NULL;
346         tdm_value v;
347         struct list_head call_list;
348
349         TDM_RETURN_IF_FAIL(private_output != NULL);
350
351         private_client = private_output->private_client;
352
353         /* If value is extended value, we handle it as DPMS on in client side
354          * The extended DPMS value is valid only in server side.
355          * Or, need to export to client side also?
356          */
357         if (value > TDM_OUTPUT_DPMS_OFF)
358                 value = TDM_OUTPUT_DPMS_ON;
359
360         if (private_output->dpms == value)
361                 return;
362
363         private_output->dpms = value;
364
365         if (error != TDM_ERROR_NONE)
366                 TDM_INFO("dpms event error: %d", error);
367
368         TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)",
369                         private_output,
370                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
371                         value);
372
373         LIST_INITHEAD(&call_list);
374
375         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
376                 LIST_ADDTAIL(&h->call_link, &call_list);
377         }
378
379         v.u32 = value;
380         pthread_mutex_unlock(&private_client->lock);
381         LIST_FOR_EACH_ENTRY_SAFE(h, hh, &call_list, call_link) {
382                 if (h->func)
383                         h->func(private_output, TDM_OUTPUT_CHANGE_DPMS, v, h->user_data);
384         }
385         pthread_mutex_lock(&private_client->lock);
386 }
387
388 static const struct wl_tdm_output_listener tdm_client_output_listener = {
389         _tdm_client_output_cb_mode,
390         _tdm_client_output_cb_connection,
391         _tdm_client_output_cb_dpms,
392 };
393
394 static void
395 _tdm_client_cb_global(void *data, struct wl_registry *registry,
396                                           uint32_t name, const char *interface,
397                                           uint32_t version)
398 {
399         tdm_private_client *private_client = data;
400
401         if (strncmp(interface, "wl_tdm", 6) == 0) {
402                 private_client->tdm =
403                         wl_registry_bind(registry, name, &wl_tdm_interface, version);
404                 TDM_RETURN_IF_FAIL(private_client->tdm != NULL);
405
406                 wl_display_flush(private_client->display);
407         }
408 }
409
410 /* LCOV_EXCL_START */
411 static void
412 _tdm_client_cb_global_remove(void *data, struct wl_registry *registry, uint32_t name)
413 {
414 }
415 /* LCOV_EXCL_STOP */
416
417 static const struct wl_registry_listener tdm_client_registry_listener = {
418         _tdm_client_cb_global,
419         _tdm_client_cb_global_remove
420 };
421
422 tdm_client*
423 tdm_client_create(tdm_error *error)
424 {
425         tdm_private_client *private_client;
426
427         private_client = calloc(1, sizeof *private_client);
428         if (!private_client) {
429                 /* LCOV_EXCL_START */
430
431                 TDM_ERR("alloc failed");
432                 if (error)
433                         *error = TDM_ERROR_OUT_OF_MEMORY;
434                 return NULL;
435
436                 /* LCOV_EXCL_STOP */
437         }
438
439         if (pthread_mutex_init(&private_client->lock, NULL)) {
440                 TDM_ERR("mutex init failed: %m");
441                 free(private_client);
442                 if (error)
443                         *error = TDM_ERROR_OUT_OF_MEMORY;
444                 return NULL;
445         }
446
447         LIST_INITHEAD(&private_client->output_list);
448
449         private_client->display = wl_display_connect("tdm-socket");
450         TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed);
451
452         private_client->queue = wl_display_create_queue(private_client->display);
453         TDM_GOTO_IF_FAIL(private_client->queue != NULL, create_failed);
454
455         private_client->registry = wl_display_get_registry(private_client->display);
456         TDM_GOTO_IF_FAIL(private_client->registry != NULL, create_failed);
457
458         wl_registry_add_listener(private_client->registry,
459                                                          &tdm_client_registry_listener, private_client);
460         wl_display_roundtrip(private_client->display);
461
462         if (CHECK_WL_PROTOCOL_ERROR(private_client))
463                 goto create_failed;
464
465         /* check global objects */
466         TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed);
467
468         if (error)
469                 *error = TDM_ERROR_NONE;
470
471         return (tdm_client*)private_client;
472 create_failed:
473         tdm_client_destroy((tdm_client*)private_client);
474         if (error)
475                 *error = TDM_ERROR_OPERATION_FAILED;
476         return NULL;
477 }
478
479 void
480 tdm_client_destroy(tdm_client *client)
481 {
482         tdm_private_client *private_client = (tdm_private_client*)client;
483         tdm_private_client_output *o = NULL, *oo = NULL;
484
485         if (!private_client)
486                 return;
487
488         pthread_mutex_lock(&private_client->lock);
489
490         if (private_client->temp_vblank) {
491                 pthread_mutex_unlock(&private_client->lock);
492                 tdm_client_vblank_destroy(private_client->temp_vblank);
493                 pthread_mutex_lock(&private_client->lock);
494         }
495
496         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) {
497                 _tdm_client_output_destroy(o);
498         }
499
500         if (private_client->tdm)
501                 wl_tdm_destroy(private_client->tdm);
502         if (private_client->registry)
503                 wl_registry_destroy(private_client->registry);
504         if (private_client->queue)
505                 wl_event_queue_destroy(private_client->queue);
506         if (private_client->display)
507                 wl_display_disconnect(private_client->display);
508
509         pthread_mutex_unlock(&private_client->lock);
510         pthread_mutex_destroy(&private_client->lock);
511
512         free(private_client);
513 }
514
515 tdm_error
516 tdm_client_get_fd(tdm_client *client, int *fd)
517 {
518         tdm_private_client *private_client;
519
520         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
521         TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_ERROR_INVALID_PARAMETER);
522
523         private_client = (tdm_private_client*)client;
524
525         pthread_mutex_lock(&private_client->lock);
526
527         *fd = wl_display_get_fd(private_client->display);
528
529         pthread_mutex_unlock(&private_client->lock);
530
531         if (*fd < 0)
532                 return TDM_ERROR_OPERATION_FAILED;
533
534         return TDM_ERROR_NONE;
535 }
536
537 tdm_error
538 tdm_client_handle_events(tdm_client *client)
539 {
540         tdm_private_client *private_client;
541
542         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
543
544         /* LCOV_EXCL_START */
545         private_client = (tdm_private_client*)client;
546
547         pthread_mutex_lock(&private_client->lock);
548
549         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
550                 pthread_mutex_unlock(&private_client->lock);
551                 return TDM_ERROR_PROTOCOL_ERROR;
552         }
553
554         if (private_client->enable_ttrace)
555                 TDM_TRACE_ASYNC_BEGIN((int)private_client->stamp, "TDM_Client_Events:%u", (unsigned int)private_client->stamp);
556
557         wl_display_dispatch(private_client->display);
558
559         if (private_client->enable_ttrace)
560                 TDM_TRACE_ASYNC_END((int)private_client->stamp, "TDM_Client_Events:%u", (unsigned int)private_client->stamp);
561
562         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
563                 pthread_mutex_unlock(&private_client->lock);
564                 return TDM_ERROR_PROTOCOL_ERROR;
565         }
566
567         pthread_mutex_unlock(&private_client->lock);
568
569         return TDM_ERROR_NONE;
570         /* LCOV_EXCL_STOP */
571 }
572
573 typedef struct _tdm_client_vblank_temp {
574         tdm_client_vblank_handler2 func;
575         void *user_data;
576 } tdm_client_vblank_temp;
577
578 /* LCOV_EXCL_START */
579 static void
580 _tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
581                                                                 unsigned int tv_sec, unsigned int tv_usec, void *user_data)
582 {
583         tdm_client_vblank_temp *vblank_temp = user_data;
584
585         TDM_RETURN_IF_FAIL(vblank_temp != NULL);
586         TDM_RETURN_IF_FAIL(vblank != NULL);
587
588         if (vblank_temp->func)
589                 vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data);
590
591         free(vblank_temp);
592 }
593 /* LCOV_EXCL_STOP */
594
595 /* LCOV_EXCL_START */ /* deprecated */
596 tdm_error
597 tdm_client_wait_vblank(tdm_client *client, char *name,
598                                            int sw_timer, int interval, int sync,
599                                            tdm_client_vblank_handler2 func, void *user_data)
600 {
601         tdm_private_client *private_client = (tdm_private_client*)client;
602         tdm_client_output *output;
603         tdm_client_vblank_temp *vblank_temp;
604         tdm_error ret;
605
606         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
607         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
608         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
609
610         if (CHECK_WL_PROTOCOL_ERROR(private_client))
611                 return TDM_ERROR_PROTOCOL_ERROR;
612
613         if (!private_client->temp_vblank) {
614                 output = tdm_client_get_output(client, name, &ret);
615                 TDM_RETURN_VAL_IF_FAIL(output != NULL, ret);
616
617                 private_client->temp_vblank = tdm_client_output_create_vblank(output, &ret);
618                 TDM_RETURN_VAL_IF_FAIL(private_client->temp_vblank != NULL, ret);
619         }
620
621         tdm_client_vblank_set_enable_fake(private_client->temp_vblank, sw_timer);
622         tdm_client_vblank_set_sync(private_client->temp_vblank, sync);
623
624         vblank_temp = calloc(1, sizeof *vblank_temp);
625         TDM_RETURN_VAL_IF_FAIL(vblank_temp != NULL, TDM_ERROR_OUT_OF_MEMORY);
626
627         vblank_temp->func = func;
628         vblank_temp->user_data = user_data;
629
630         return tdm_client_vblank_wait(private_client->temp_vblank, interval, _tdm_client_vblank_handler_temp, vblank_temp);
631 }
632 /* LCOV_EXCL_STOP */
633
634 tdm_client_output*
635 tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
636 {
637         tdm_private_client *private_client;
638         tdm_private_client_output *private_output = NULL;
639         struct wl_proxy *wrapper;
640
641         if (error)
642                 *error = TDM_ERROR_NONE;
643
644         if (!client) {
645                 TDM_ERR("'!client' failed");
646                 if (error)
647                         *error = TDM_ERROR_INVALID_PARAMETER;
648                 return NULL;
649         }
650
651         private_client = (tdm_private_client*)client;
652
653         pthread_mutex_lock(&private_client->lock);
654
655         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
656                 if (error)
657                         *error = TDM_ERROR_PROTOCOL_ERROR;
658                 pthread_mutex_unlock(&private_client->lock);
659                 return NULL;
660         }
661
662         if (!name) {
663                 name = "primary";
664         } else if (strncmp(name, "primary", 7) && strncmp(name, "default", 7)) {
665                 if (error)
666                         *error = TDM_ERROR_INVALID_PARAMETER;
667                 pthread_mutex_unlock(&private_client->lock);
668                 return NULL;
669         }
670
671         LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) {
672                 if (!strncmp(private_output->name, name, TDM_NAME_LEN)) {
673                         pthread_mutex_unlock(&private_client->lock);
674                         return (tdm_client_output*)private_output;
675                 }
676         }
677
678         wrapper = wl_proxy_create_wrapper(private_client->tdm);
679         if (!wrapper) {
680                 TDM_ERR("create output_wrapper failed");
681                 if (error)
682                         *error = TDM_ERROR_OUT_OF_MEMORY;
683                 pthread_mutex_unlock(&private_client->lock);
684                 return NULL;
685         }
686
687         wl_proxy_set_queue(wrapper, private_client->queue);
688
689         private_output = calloc(1, sizeof *private_output);
690         if (!private_output) {
691                 /* LCOV_EXCL_START */
692                 wl_proxy_wrapper_destroy(wrapper);
693                 TDM_ERR("alloc failed");
694                 if (error)
695                         *error = TDM_ERROR_OUT_OF_MEMORY;
696                 pthread_mutex_unlock(&private_client->lock);
697                 return NULL;
698
699                 /* LCOV_EXCL_STOP */
700         }
701
702         private_output->private_client = private_client;
703
704         snprintf(private_output->name, TDM_NAME_LEN, "%s", name);
705         private_output->output = wl_tdm_create_output((struct wl_tdm *)wrapper, private_output->name);
706         wl_proxy_wrapper_destroy(wrapper);
707         if (!private_output->output) {
708                 /* LCOV_EXCL_START */
709
710                 TDM_ERR("couldn't create output resource");
711                 free(private_output);
712                 if (error)
713                         *error = TDM_ERROR_OUT_OF_MEMORY;
714                 pthread_mutex_unlock(&private_client->lock);
715                 return NULL;
716
717                 /* LCOV_EXCL_STOP */
718         }
719
720         LIST_INITHEAD(&private_output->vblank_list);
721         LIST_INITHEAD(&private_output->change_handler_list);
722
723         wl_tdm_output_add_listener(private_output->output,
724                                                            &tdm_client_output_listener, private_output);
725         wl_display_roundtrip_queue(private_client->display, private_client->queue);
726
727         wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
728
729         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
730                 wl_tdm_output_destroy(private_output->output);
731                 free(private_output);
732                 if (error)
733                         *error = TDM_ERROR_PROTOCOL_ERROR;
734                 pthread_mutex_unlock(&private_client->lock);
735                 return NULL;
736         }
737
738         LIST_ADDTAIL(&private_output->link, &private_client->output_list);
739
740         pthread_mutex_unlock(&private_client->lock);
741
742         return (tdm_client_output*)private_output;
743 }
744
745 tdm_error
746 tdm_client_output_add_change_handler(tdm_client_output *output,
747                                                                          tdm_client_output_change_handler func,
748                                                                          void *user_data)
749 {
750         tdm_private_client_output *private_output;
751         tdm_private_client *private_client;
752         tdm_client_output_handler_info *h;
753
754         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
755         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
756
757         private_output = (tdm_private_client_output*)output;
758         private_client = private_output->private_client;
759
760         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
761                 if (h->func == func && h->user_data == user_data) {
762                         TDM_ERR("can't add twice");
763                         return TDM_ERROR_BAD_REQUEST;
764                 }
765         }
766
767         h = calloc(1, sizeof *h);
768         TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY);
769
770         pthread_mutex_lock(&private_client->lock);
771
772         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
773                 free(h);
774                 pthread_mutex_unlock(&private_client->lock);
775                 return TDM_ERROR_PROTOCOL_ERROR;
776         }
777
778         if (LIST_IS_EMPTY(&private_output->change_handler_list)) {
779                 wl_tdm_output_watch_output_changes(private_output->output, 1);
780                 wl_display_roundtrip_queue(private_client->display, private_client->queue);
781
782                 /* TODO: this is very tricky.
783                  * If a client adds the change_handler, we might be able to guess that
784                  * the client will watch the tdm client's fd and handle tdm events in
785                  * event loop. Otherwise, we CAN'T make sure if a client has event loop
786                  * which handles tdm events.
787                  */
788                 private_output->watch_output_changes = 1;
789         }
790
791         h->private_output = private_output;
792         h->func = func;
793         h->user_data = user_data;
794         LIST_ADDTAIL(&h->link, &private_output->change_handler_list);
795         LIST_INITHEAD(&h->call_link);
796
797         pthread_mutex_unlock(&private_client->lock);
798
799         return TDM_ERROR_NONE;
800 }
801
802 void
803 tdm_client_output_remove_change_handler(tdm_client_output *output,
804                                                                                 tdm_client_output_change_handler func,
805                                                                                 void *user_data)
806 {
807         tdm_private_client_output *private_output;
808         tdm_private_client *private_client;
809         tdm_client_output_handler_info *h = NULL;
810
811         TDM_RETURN_IF_FAIL(output != NULL);
812         TDM_RETURN_IF_FAIL(func != NULL);
813
814         private_output = (tdm_private_client_output*)output;
815         private_client = private_output->private_client;
816
817         pthread_mutex_lock(&private_client->lock);
818
819         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
820                 if (h->func != func || h->user_data != user_data)
821                         continue;
822
823                 LIST_DEL(&h->link);
824                 free(h);
825
826                 if (LIST_IS_EMPTY(&private_output->change_handler_list)) {
827                         if (!CHECK_WL_PROTOCOL_ERROR(private_client)) {
828                                 wl_tdm_output_watch_output_changes(private_output->output, 0);
829                                 wl_display_roundtrip_queue(private_client->display, private_client->queue);
830                         }
831                 }
832
833                 pthread_mutex_unlock(&private_client->lock);
834
835                 return;
836         }
837
838         pthread_mutex_unlock(&private_client->lock);
839 }
840
841 tdm_error
842 tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
843 {
844         tdm_private_client_output *private_output;
845         tdm_private_client *private_client;
846
847         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
848         TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER);
849
850         private_output = (tdm_private_client_output*)output;
851         private_client = private_output->private_client;
852
853         pthread_mutex_lock(&private_client->lock);
854
855         if (private_output->watch_output_changes) {
856                 *refresh = private_output->refresh;
857                 pthread_mutex_unlock(&private_client->lock);
858                 return TDM_ERROR_NONE;
859         }
860
861         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
862                 pthread_mutex_unlock(&private_client->lock);
863                 return TDM_ERROR_PROTOCOL_ERROR;
864         }
865
866         wl_proxy_set_queue((struct wl_proxy *)private_output->output, private_client->queue);
867         wl_tdm_output_get_mode(private_output->output);
868         wl_display_roundtrip_queue(private_client->display, private_client->queue);
869         wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
870
871         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
872                 pthread_mutex_unlock(&private_client->lock);
873                 return TDM_ERROR_PROTOCOL_ERROR;
874         }
875
876         *refresh = private_output->refresh;
877
878         pthread_mutex_unlock(&private_client->lock);
879
880         return TDM_ERROR_NONE;
881 }
882
883 tdm_error
884 tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
885 {
886         tdm_private_client_output *private_output;
887         tdm_private_client *private_client;
888
889         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
890         TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
891
892         private_output = (tdm_private_client_output*)output;
893         private_client = private_output->private_client;
894
895         pthread_mutex_lock(&private_client->lock);
896
897         if (private_output->watch_output_changes) {
898                 *status = private_output->connection;
899                 pthread_mutex_unlock(&private_client->lock);
900                 return TDM_ERROR_NONE;
901         }
902
903         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
904                 pthread_mutex_unlock(&private_client->lock);
905                 return TDM_ERROR_PROTOCOL_ERROR;
906         }
907
908         wl_proxy_set_queue((struct wl_proxy *)private_output->output, private_client->queue);
909         wl_tdm_output_get_connection(private_output->output);
910         wl_display_roundtrip_queue(private_client->display, private_client->queue);
911         wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
912
913         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
914                 pthread_mutex_unlock(&private_client->lock);
915                 return TDM_ERROR_PROTOCOL_ERROR;
916         }
917
918         *status = private_output->connection;
919
920         pthread_mutex_unlock(&private_client->lock);
921
922         return TDM_ERROR_NONE;
923 }
924
925 tdm_error
926 tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
927 {
928         tdm_private_client_output *private_output;
929         tdm_private_client *private_client;
930
931         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
932         TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER);
933
934         private_output = (tdm_private_client_output*)output;
935         private_client = private_output->private_client;
936
937         if (private_output->watch_output_changes) {
938                 *dpms = private_output->dpms;
939                 return TDM_ERROR_NONE;
940         }
941
942         pthread_mutex_lock(&private_client->lock);
943
944         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
945                 pthread_mutex_unlock(&private_client->lock);
946                 return TDM_ERROR_PROTOCOL_ERROR;
947         }
948
949         wl_proxy_set_queue((struct wl_proxy *)private_output->output, private_client->queue);
950         wl_tdm_output_get_dpms(private_output->output);
951         wl_display_roundtrip_queue(private_client->display, private_client->queue);
952         wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
953
954         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
955                 pthread_mutex_unlock(&private_client->lock);
956                 return TDM_ERROR_PROTOCOL_ERROR;
957         }
958
959         *dpms = private_output->dpms;
960
961         pthread_mutex_unlock(&private_client->lock);
962
963         return TDM_ERROR_NONE;
964 }
965
966 tdm_client_vblank*
967 tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
968 {
969         tdm_private_client *private_client;
970         tdm_private_client_output *private_output;
971         tdm_private_client_vblank *private_vblank;
972         struct wl_proxy *wrapper;
973
974         if (error)
975                 *error = TDM_ERROR_NONE;
976
977         if (!output) {
978                 TDM_ERR("'!output' failed");
979                 if (error)
980                         *error = TDM_ERROR_INVALID_PARAMETER;
981                 return NULL;
982         }
983
984         private_output = (tdm_private_client_output*)output;
985         private_client = private_output->private_client;
986
987         pthread_mutex_lock(&private_client->lock);
988
989         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
990                 if (error)
991                         *error = TDM_ERROR_PROTOCOL_ERROR;
992                 pthread_mutex_unlock(&private_client->lock);
993                 return NULL;
994         }
995
996         wrapper = wl_proxy_create_wrapper(private_output->output);
997         if (!wrapper) {
998                 TDM_ERR("create output_wrapper failed");
999                 if (error)
1000                         *error = TDM_ERROR_OUT_OF_MEMORY;
1001                 pthread_mutex_unlock(&private_client->lock);
1002                 return NULL;
1003         }
1004
1005         wl_proxy_set_queue(wrapper, private_client->queue);
1006
1007         private_vblank = calloc(1, sizeof *private_vblank);
1008         if (!private_vblank) {
1009                 /* LCOV_EXCL_START */
1010
1011                 TDM_ERR("alloc failed");
1012                 wl_proxy_wrapper_destroy(wrapper);
1013                 if (error)
1014                         *error = TDM_ERROR_OUT_OF_MEMORY;
1015                 pthread_mutex_unlock(&private_client->lock);
1016                 return NULL;
1017
1018                 /* LCOV_EXCL_STOP */
1019         }
1020
1021         private_vblank->private_output = private_output;
1022
1023         private_vblank->vblank = wl_tdm_output_create_vblank((struct wl_tdm_output*)wrapper);
1024         wl_proxy_wrapper_destroy(wrapper);
1025         if (!private_vblank->vblank) {
1026                 /* LCOV_EXCL_START */
1027
1028                 TDM_ERR("couldn't create vblank resource");
1029                 free(private_vblank);
1030                 if (error)
1031                         *error = TDM_ERROR_OUT_OF_MEMORY;
1032                 pthread_mutex_unlock(&private_client->lock);
1033                 return NULL;
1034
1035                 /* LCOV_EXCL_STOP */
1036         }
1037
1038         /* initial value */
1039         private_vblank->fps = private_output->refresh;
1040         private_vblank->offset = 0;
1041         private_vblank->enable_fake = 0;
1042
1043         LIST_INITHEAD(&private_vblank->wait_list);
1044
1045         wl_tdm_vblank_add_listener(private_vblank->vblank,
1046                                                            &tdm_client_vblank_listener, private_vblank);
1047         wl_display_roundtrip_queue(private_client->display, private_client->queue);
1048
1049         wl_proxy_set_queue((struct wl_proxy *)private_vblank->vblank, NULL);
1050
1051         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1052                 wl_tdm_vblank_destroy(private_vblank->vblank);
1053                 free(private_vblank);
1054                 if (error)
1055                         *error = TDM_ERROR_PROTOCOL_ERROR;
1056                 pthread_mutex_unlock(&private_client->lock);
1057                 return NULL;
1058         }
1059
1060         LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
1061
1062         pthread_mutex_unlock(&private_client->lock);
1063
1064         return (tdm_client_vblank*)private_vblank;
1065 }
1066
1067 void
1068 tdm_client_vblank_destroy(tdm_client_vblank *vblank)
1069 {
1070         tdm_private_client_vblank *private_vblank;
1071         tdm_private_client *private_client;
1072         tdm_client_wait_info *w = NULL, *ww = NULL;
1073
1074         TDM_RETURN_IF_FAIL(vblank != NULL);
1075
1076         private_vblank = vblank;
1077         private_client = private_vblank->private_output->private_client;
1078
1079         pthread_mutex_lock(&private_client->lock);
1080
1081         LIST_DEL(&private_vblank->link);
1082
1083         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
1084                 LIST_DEL(&w->link);
1085                 free(w);
1086         }
1087
1088         wl_tdm_vblank_destroy(private_vblank->vblank);
1089
1090         free(private_vblank);
1091
1092         pthread_mutex_unlock(&private_client->lock);
1093 }
1094
1095 tdm_error
1096 tdm_client_vblank_set_name(tdm_client_vblank *vblank, const char *name)
1097 {
1098         tdm_private_client_vblank *private_vblank;
1099         tdm_private_client *private_client;
1100
1101         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
1102
1103         private_vblank = vblank;
1104         private_client = private_vblank->private_output->private_client;
1105
1106         pthread_mutex_lock(&private_client->lock);
1107
1108         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1109                 pthread_mutex_unlock(&private_client->lock);
1110                 return TDM_ERROR_PROTOCOL_ERROR;
1111         }
1112
1113         if (!name)
1114                 name = TDM_VBLANK_DEFAULT_NAME;
1115
1116         strncpy(private_vblank->name, name, TDM_NAME_LEN - 1);
1117         private_vblank->name[TDM_NAME_LEN - 1] = '\0';
1118
1119         wl_tdm_vblank_set_name(private_vblank->vblank, private_vblank->name);
1120
1121         pthread_mutex_unlock(&private_client->lock);
1122
1123         return TDM_ERROR_NONE;
1124 }
1125
1126 tdm_error
1127 tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync)
1128 {
1129         tdm_private_client_vblank *private_vblank;
1130         tdm_private_client *private_client;
1131
1132         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
1133
1134         private_vblank = vblank;
1135         private_client = private_vblank->private_output->private_client;
1136
1137         pthread_mutex_lock(&private_client->lock);
1138         private_vblank->sync = sync;
1139         pthread_mutex_unlock(&private_client->lock);
1140
1141         return TDM_ERROR_NONE;
1142 }
1143
1144 tdm_error
1145 tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
1146 {
1147         tdm_private_client_vblank *private_vblank;
1148         tdm_private_client *private_client;
1149
1150         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
1151         TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
1152
1153         private_vblank = vblank;
1154         private_client = private_vblank->private_output->private_client;
1155
1156         pthread_mutex_lock(&private_client->lock);
1157
1158         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1159                 pthread_mutex_unlock(&private_client->lock);
1160                 return TDM_ERROR_PROTOCOL_ERROR;
1161         }
1162
1163         if (private_vblank->fps == fps) {
1164                 pthread_mutex_unlock(&private_client->lock);
1165                 return TDM_ERROR_NONE;
1166         }
1167
1168         private_vblank->fps = fps;
1169
1170         wl_tdm_vblank_set_fps(private_vblank->vblank, fps);
1171
1172         pthread_mutex_unlock(&private_client->lock);
1173
1174         return TDM_ERROR_NONE;
1175 }
1176
1177 tdm_error
1178 tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
1179 {
1180         tdm_private_client_vblank *private_vblank;
1181         tdm_private_client *private_client;
1182
1183         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
1184
1185         private_vblank = vblank;
1186         TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
1187
1188         private_client = private_vblank->private_output->private_client;
1189
1190         pthread_mutex_lock(&private_client->lock);
1191
1192         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1193                 pthread_mutex_unlock(&private_client->lock);
1194                 return TDM_ERROR_PROTOCOL_ERROR;
1195         }
1196
1197         if (private_vblank->offset == offset_ms) {
1198                 pthread_mutex_unlock(&private_client->lock);
1199                 return TDM_ERROR_NONE;
1200         }
1201
1202         private_vblank->offset = offset_ms;
1203
1204         wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms);
1205
1206         pthread_mutex_unlock(&private_client->lock);
1207
1208         return TDM_ERROR_NONE;
1209 }
1210
1211 tdm_error
1212 tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake)
1213 {
1214         tdm_private_client_vblank *private_vblank;
1215         tdm_private_client *private_client;
1216
1217         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
1218
1219         private_vblank = vblank;
1220         private_client = private_vblank->private_output->private_client;
1221
1222         pthread_mutex_lock(&private_client->lock);
1223
1224         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1225                 pthread_mutex_unlock(&private_client->lock);
1226                 return TDM_ERROR_PROTOCOL_ERROR;
1227         }
1228
1229         if (private_vblank->enable_fake == enable_fake) {
1230                 pthread_mutex_unlock(&private_client->lock);
1231                 return TDM_ERROR_NONE;
1232         }
1233
1234         private_vblank->enable_fake = enable_fake;
1235
1236         wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake);
1237
1238         pthread_mutex_unlock(&private_client->lock);
1239
1240         return TDM_ERROR_NONE;
1241 }
1242
1243 tdm_error
1244 tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data)
1245 {
1246         tdm_private_client *private_client;
1247         tdm_private_client_output *private_output;
1248         tdm_private_client_vblank *private_vblank;
1249         tdm_client_wait_info *w;
1250         struct timespec tp;
1251         unsigned int req_sec, req_usec;
1252         int ret = 0;
1253
1254         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
1255         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
1256         /* can't support "interval 0" and "getting current_msc" things because
1257          * there is a socket communication between TDM client and server. It's impossible
1258          * to return the current msc or sequence immediately.
1259          */
1260         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
1261
1262         private_vblank = vblank;
1263         private_output = private_vblank->private_output;
1264         private_client = private_output->private_client;
1265
1266         pthread_mutex_lock(&private_client->lock);
1267
1268         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1269                 pthread_mutex_unlock(&private_client->lock);
1270                 return TDM_ERROR_PROTOCOL_ERROR;
1271         }
1272
1273         if (!private_vblank->started)
1274                 private_vblank->started = 1;
1275
1276         if (!private_vblank->enable_fake) {
1277                 if (private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
1278                         TDM_ERR("output disconnected");
1279                         pthread_mutex_unlock(&private_client->lock);
1280                         return TDM_ERROR_OUTPUT_DISCONNECTED;
1281                 }
1282                 if (TDM_OUTPUT_DPMS_VSYNC_IS_OFF(private_output->dpms)) {
1283                         TDM_ERR("dpms %s", tdm_dpms_str(private_output->dpms));
1284                         pthread_mutex_unlock(&private_client->lock);
1285                         return TDM_ERROR_DPMS_OFF;
1286                 }
1287         }
1288
1289         w = calloc(1, sizeof *w);
1290         if (!w) {
1291                 /* LCOV_EXCL_START */
1292
1293                 TDM_ERR("alloc failed");
1294                 pthread_mutex_unlock(&private_client->lock);
1295                 return TDM_ERROR_OUT_OF_MEMORY;
1296
1297                 /* LCOV_EXCL_STOP */
1298         }
1299
1300         w->private_vblank = private_vblank;
1301         w->func = func;
1302         w->user_data = user_data;
1303
1304         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
1305         LIST_INITHEAD(&w->call_link);
1306
1307         clock_gettime(CLOCK_MONOTONIC, &tp);
1308         req_sec = (unsigned int)tp.tv_sec;
1309         req_usec = (unsigned int)(tp.tv_nsec / 1000);
1310
1311         w->req_id = ++private_output->req_id;
1312         w->req_time = TDM_TIME(req_sec, req_usec);
1313         w->need_free = (private_vblank->sync) ? 0 : 1;
1314
1315         wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, req_sec, req_usec);
1316
1317         if (private_vblank->enable_ttrace)
1318                 TDM_TRACE_ASYNC_BEGIN((int)w->req_time, "TDM_Client_Vblank:%u", private_vblank->stamp);
1319
1320         TDM_DBG("vblank(%p) interval(%u) req_id(%d) req(%.6f)",
1321                         vblank, interval, w->req_id, w->req_time);
1322
1323         private_vblank->req_time = w->req_time;
1324
1325         if (private_vblank->last_time >= w->req_time)
1326                 TDM_ERR("'last(%.6f) < req(%.6f)' failed", private_vblank->last_time, w->req_time);
1327
1328         if (!private_vblank->sync) {
1329                 wl_display_flush(private_client->display);
1330                 pthread_mutex_unlock(&private_client->lock);
1331                 return TDM_ERROR_NONE;
1332         }
1333
1334         /* LCOV_EXCL_START */
1335
1336         while (ret != -1 && !w->need_free)
1337                 ret = wl_display_dispatch(private_client->display);
1338
1339         clock_gettime(CLOCK_MONOTONIC, &tp);
1340         TDM_DBG("block during %d us",
1341                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
1342                         - (req_sec * 1000000 + req_usec));
1343
1344         LIST_DEL(&w->link);
1345         free(w);
1346
1347         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1348                 pthread_mutex_unlock(&private_client->lock);
1349                 return TDM_ERROR_PROTOCOL_ERROR;
1350         }
1351
1352         pthread_mutex_unlock(&private_client->lock);
1353
1354         return TDM_ERROR_NONE;
1355
1356         /* LCOV_EXCL_STOP */
1357 }
1358
1359 tdm_error
1360 tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence,
1361                                                    tdm_client_vblank_handler func, void *user_data)
1362 {
1363         tdm_private_client *private_client;
1364         tdm_private_client_output *private_output;
1365         tdm_private_client_vblank *private_vblank;
1366         tdm_client_wait_info *w;
1367         struct timespec tp;
1368         unsigned int req_sec, req_usec;
1369         int ret = 0;
1370
1371         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
1372         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
1373
1374         private_vblank = vblank;
1375         private_output = private_vblank->private_output;
1376         private_client = private_output->private_client;
1377
1378         pthread_mutex_lock(&private_client->lock);
1379
1380         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1381                 pthread_mutex_unlock(&private_client->lock);
1382                 return TDM_ERROR_PROTOCOL_ERROR;
1383         }
1384
1385         if (!private_vblank->started)
1386                 private_vblank->started = 1;
1387
1388         if (!private_vblank->enable_fake) {
1389                 if (private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
1390                         TDM_ERR("output disconnected");
1391                         pthread_mutex_unlock(&private_client->lock);
1392                         return TDM_ERROR_OUTPUT_DISCONNECTED;
1393                 }
1394                 if (TDM_OUTPUT_DPMS_VSYNC_IS_OFF(private_output->dpms)) {
1395                         TDM_ERR("dpms %s", tdm_dpms_str(private_output->dpms));
1396                         pthread_mutex_unlock(&private_client->lock);
1397                         return TDM_ERROR_DPMS_OFF;
1398                 }
1399         }
1400
1401         w = calloc(1, sizeof *w);
1402         if (!w) {
1403                 /* LCOV_EXCL_START */
1404
1405                 TDM_ERR("alloc failed");
1406                 pthread_mutex_unlock(&private_client->lock);
1407                 return TDM_ERROR_OUT_OF_MEMORY;
1408
1409                 /* LCOV_EXCL_STOP */
1410         }
1411
1412         w->private_vblank = private_vblank;
1413         w->func = func;
1414         w->user_data = user_data;
1415
1416         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
1417         LIST_INITHEAD(&w->call_link);
1418
1419         clock_gettime(CLOCK_MONOTONIC, &tp);
1420         req_sec = (unsigned int)tp.tv_sec;
1421         req_usec = (unsigned int)(tp.tv_nsec / 1000);
1422
1423         w->req_id = ++private_output->req_id;
1424         w->req_time = TDM_TIME(req_sec, req_usec);
1425         w->need_free = (private_vblank->sync) ? 0 : 1;
1426
1427         wl_tdm_vblank_wait_vblank_seq(private_vblank->vblank, sequence, w->req_id, req_sec, req_usec);
1428
1429         if (private_vblank->enable_ttrace)
1430                 TDM_TRACE_ASYNC_BEGIN((int)w->req_time, "TDM_Client_Vblank:%u", private_vblank->stamp);
1431
1432         TDM_DBG("vblank(%p) sequence(%u) req_id(%d) req(%.6f)",
1433                         vblank, sequence, w->req_id, w->req_time);
1434
1435         private_vblank->req_time = w->req_time;
1436
1437         if (private_vblank->last_time >= w->req_time)
1438                 TDM_ERR("'last(%.6f) < req(%.6f)' failed", private_vblank->last_time, w->req_time);
1439
1440         if (!private_vblank->sync) {
1441                 wl_display_flush(private_client->display);
1442                 pthread_mutex_unlock(&private_client->lock);
1443                 return TDM_ERROR_NONE;
1444         }
1445
1446         /* LCOV_EXCL_START */
1447
1448         while (ret != -1 && !w->need_free)
1449                 ret = wl_display_dispatch(private_client->display);
1450
1451         clock_gettime(CLOCK_MONOTONIC, &tp);
1452         TDM_DBG("block during %d us",
1453                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
1454                         - (req_sec * 1000000 + req_usec));
1455
1456         LIST_DEL(&w->link);
1457         free(w);
1458
1459         if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
1460                 pthread_mutex_unlock(&private_client->lock);
1461                 return TDM_ERROR_PROTOCOL_ERROR;
1462         }
1463
1464         pthread_mutex_unlock(&private_client->lock);
1465
1466         return TDM_ERROR_NONE;
1467
1468         /* LCOV_EXCL_STOP */
1469 }