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