add error information to protocol
[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.h"
47 #include "tdm_log.h"
48 #include "tdm_macro.h"
49 #include "tdm_list.h"
50 #include "tdm.h"
51 #include "tdm_private.h"
52 #include "tdm-client-protocol.h"
53
54 typedef struct _tdm_private_client_vblank tdm_private_client_vblank;
55
56 typedef struct _tdm_private_client {
57         struct wl_display *display;
58         struct wl_registry *registry;
59         struct wl_tdm *tdm;
60         struct list_head output_list;
61
62         tdm_private_client_vblank *temp_vblank;
63 } tdm_private_client;
64
65 typedef struct _tdm_private_client_output {
66         tdm_private_client *private_client;
67
68         char name[TDM_NAME_LEN];
69         struct wl_tdm_output *output;
70         int width;
71         int height;
72         int refresh;
73         tdm_output_conn_status connection;
74         tdm_output_dpms dpms;
75         struct list_head vblank_list;
76         struct list_head change_handler_list;
77
78         unsigned int req_id;
79         unsigned int watch_output_changes;
80
81         struct list_head link;
82 } tdm_private_client_output;
83
84 struct _tdm_private_client_vblank {
85         tdm_private_client_output *private_output;
86
87         struct wl_tdm_vblank *vblank;
88         struct list_head wait_list;
89
90         char name[TDM_NAME_LEN];
91         unsigned int sync;
92         unsigned int fps;
93         int offset;
94         unsigned int enable_fake;
95
96         unsigned int started;
97
98         double last_time;
99
100         struct list_head link;
101 };
102
103 typedef struct _tdm_client_output_handler_info {
104         tdm_private_client_output *private_output;
105
106         tdm_client_output_change_handler func;
107         void *user_data;
108
109         struct list_head link;
110 } tdm_client_output_handler_info;
111
112 typedef struct _tdm_client_wait_info {
113         tdm_private_client_vblank *private_vblank;
114
115         tdm_client_vblank_handler func;
116         void *user_data;
117
118         unsigned int req_id;
119         double req_time;
120         int need_free;
121
122         struct list_head link;
123 } tdm_client_wait_info;
124
125 static void
126 _tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank,
127                                                    uint32_t req_id, uint32_t sequence, uint32_t tv_sec,
128                                                    uint32_t tv_usec, uint32_t error)
129 {
130         tdm_private_client_vblank *private_vblank = data;
131         tdm_client_wait_info *w = NULL, *ww = NULL;
132
133         TDM_RETURN_IF_FAIL(private_vblank != NULL);
134
135         private_vblank->last_time = TDM_TIME(tv_sec, tv_usec);
136
137         TDM_DBG("vblank(%p) req_id(%u) sequence(%u) time(%.6f)",
138                         private_vblank, req_id, sequence, TDM_TIME(tv_sec, tv_usec));
139
140         TDM_TRACE_COUNT(ClientDoneVBlank, req_id);
141
142         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
143                 if (w->req_id != req_id)
144                         continue;
145
146                 if (w->req_time >= private_vblank->last_time)
147                         TDM_WRN("'req(%.6f) < last(%.6f)' failed", w->req_time, private_vblank->last_time);
148
149                 if (w->func)
150                         w->func(private_vblank, error, sequence, tv_sec, tv_usec, w->user_data);
151
152                 if (w->need_free) {
153                         LIST_DEL(&w->link);
154                         free(w);
155                 } else
156                         w->need_free = 1;
157                 return;
158         }
159 }
160
161 static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = {
162         _tdm_client_vblank_cb_done,
163 };
164
165 static void
166 _tdm_client_output_destroy(tdm_private_client_output *private_output)
167 {
168         tdm_private_client_vblank *v = NULL, *vv = NULL;
169         tdm_client_output_handler_info *h = NULL, *hh = NULL;
170
171         LIST_DEL(&private_output->link);
172
173         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_list, link) {
174                 TDM_ERR("vblanks SHOULD be destroyed first!");
175                 LIST_DEL(&v->link);
176                 v->private_output = NULL;
177         }
178
179         LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_output->change_handler_list, link) {
180                 LIST_DEL(&h->link);
181                 free(h);
182         }
183
184         wl_tdm_output_destroy(private_output->output);
185
186         free(private_output);
187 }
188
189 static void
190 _tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output,
191                                                    uint32_t width, uint32_t height, uint32_t refresh, uint32_t error)
192 {
193         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
194
195         TDM_RETURN_IF_FAIL(private_output != NULL);
196
197         private_output->width = width;
198         private_output->height = height;
199         private_output->refresh = refresh;
200
201         if (error != TDM_ERROR_NONE)
202                 TDM_INFO("mode event error: %d", error);
203
204         TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)",
205                         private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output),
206                         width, height, refresh);
207 }
208
209 static void
210 _tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value, uint32_t error)
211 {
212         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
213         tdm_client_output_handler_info *h = NULL;
214         tdm_value v;
215
216         TDM_RETURN_IF_FAIL(private_output != NULL);
217
218         if (private_output->connection == value)
219                 return;
220
221         private_output->connection = value;
222
223         if (error != TDM_ERROR_NONE)
224                 TDM_INFO("connection event error: %d", error);
225
226         TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)",
227                         private_output,
228                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
229                         value);
230
231         v.u32 = value;
232         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
233                 if (h->func)
234                         h->func(private_output, TDM_OUTPUT_CHANGE_CONNECTION, v, h->user_data);
235         }
236 }
237
238 static void
239 _tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value, uint32_t error)
240 {
241         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
242         tdm_client_output_handler_info *h = NULL;
243         tdm_value v;
244
245         TDM_RETURN_IF_FAIL(private_output != NULL);
246
247         if (private_output->dpms == value)
248                 return;
249
250         private_output->dpms = value;
251
252         if (error != TDM_ERROR_NONE)
253                 TDM_INFO("dpms event error: %d", error);
254
255         TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)",
256                         private_output,
257                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
258                         value);
259
260         v.u32 = value;
261         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
262                 if (h->func)
263                         h->func(private_output, TDM_OUTPUT_CHANGE_DPMS, v, h->user_data);
264         }
265 }
266
267 static const struct wl_tdm_output_listener tdm_client_output_listener = {
268         _tdm_client_output_cb_mode,
269         _tdm_client_output_cb_connection,
270         _tdm_client_output_cb_dpms,
271 };
272
273 static void
274 _tdm_client_cb_global(void *data, struct wl_registry *registry,
275                                           uint32_t name, const char *interface,
276                                           uint32_t version)
277 {
278         tdm_private_client *private_client = data;
279
280         if (strncmp(interface, "wl_tdm", 6) == 0) {
281                 private_client->tdm =
282                         wl_registry_bind(registry, name, &wl_tdm_interface, version);
283                 TDM_RETURN_IF_FAIL(private_client->tdm != NULL);
284
285                 wl_display_flush(private_client->display);
286         }
287 }
288
289 static void
290 _tdm_client_cb_global_remove(void *data, struct wl_registry *registry, uint32_t name)
291 {
292 }
293
294 static const struct wl_registry_listener tdm_client_registry_listener = {
295         _tdm_client_cb_global,
296         _tdm_client_cb_global_remove
297 };
298
299 tdm_client*
300 tdm_client_create(tdm_error *error)
301 {
302         tdm_private_client *private_client;
303
304         private_client = calloc(1, sizeof *private_client);
305         if (!private_client) {
306                 TDM_ERR("alloc failed");
307                 if (error)
308                         *error = TDM_ERROR_OUT_OF_MEMORY;
309                 return NULL;
310         }
311
312         LIST_INITHEAD(&private_client->output_list);
313
314         private_client->display = wl_display_connect("tdm-socket");
315         TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed);
316
317         private_client->registry = wl_display_get_registry(private_client->display);
318         TDM_GOTO_IF_FAIL(private_client->registry != NULL, create_failed);
319
320         wl_registry_add_listener(private_client->registry,
321                                                          &tdm_client_registry_listener, private_client);
322         wl_display_roundtrip(private_client->display);
323
324         /* check global objects */
325         TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed);
326
327         if (error)
328                 *error = TDM_ERROR_NONE;
329
330         return (tdm_client*)private_client;
331 create_failed:
332         tdm_client_destroy((tdm_client*)private_client);
333         if (error)
334                 *error = TDM_ERROR_OPERATION_FAILED;
335         return NULL;
336 }
337
338 void
339 tdm_client_destroy(tdm_client *client)
340 {
341         tdm_private_client *private_client = (tdm_private_client*)client;
342         tdm_private_client_output *o = NULL, *oo = NULL;
343
344         if (!private_client)
345                 return;
346
347         if (private_client->temp_vblank)
348                 tdm_client_vblank_destroy(private_client->temp_vblank);
349
350         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) {
351                 _tdm_client_output_destroy(o);
352         }
353
354         if (private_client->tdm)
355                 wl_tdm_destroy(private_client->tdm);
356         if (private_client->registry)
357                 wl_registry_destroy(private_client->registry);
358         if (private_client->display)
359                 wl_display_disconnect(private_client->display);
360
361         free(private_client);
362 }
363
364 tdm_error
365 tdm_client_get_fd(tdm_client *client, int *fd)
366 {
367         tdm_private_client *private_client;
368
369         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
370         TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_ERROR_INVALID_PARAMETER);
371
372         private_client = (tdm_private_client*)client;
373
374         *fd = wl_display_get_fd(private_client->display);
375         if (*fd < 0)
376                 return TDM_ERROR_OPERATION_FAILED;
377
378         return TDM_ERROR_NONE;
379 }
380
381 tdm_error
382 tdm_client_handle_events(tdm_client *client)
383 {
384         tdm_private_client *private_client;
385
386         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
387
388         private_client = (tdm_private_client*)client;
389
390         wl_display_dispatch(private_client->display);
391
392         return TDM_ERROR_NONE;
393 }
394
395 typedef struct _tdm_client_vblank_temp {
396         tdm_client_vblank_handler2 func;
397         void *user_data;
398 } tdm_client_vblank_temp;
399
400 static void
401 _tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
402                                                                 unsigned int tv_sec, unsigned int tv_usec, void *user_data)
403 {
404         tdm_client_vblank_temp *vblank_temp = user_data;
405
406         TDM_RETURN_IF_FAIL(vblank_temp != NULL);
407
408         if (vblank_temp->func)
409                 vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data);
410
411         free(vblank_temp);
412 }
413
414 tdm_error
415 tdm_client_wait_vblank(tdm_client *client, char *name,
416                                            int sw_timer, int interval, int sync,
417                                            tdm_client_vblank_handler2 func, void *user_data)
418 {
419         tdm_private_client *private_client = (tdm_private_client*)client;
420         tdm_client_output *output;
421         tdm_client_vblank_temp *vblank_temp;
422         tdm_error ret;
423
424         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
425         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
426         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
427
428         if (!private_client->temp_vblank) {
429                 output = tdm_client_get_output(client, name, &ret);
430                 TDM_RETURN_VAL_IF_FAIL(output != NULL, ret);
431
432                 private_client->temp_vblank = tdm_client_output_create_vblank(output, &ret);
433                 TDM_RETURN_VAL_IF_FAIL(private_client->temp_vblank != NULL, ret);
434         }
435
436         tdm_client_vblank_set_enable_fake(private_client->temp_vblank, sw_timer);
437         tdm_client_vblank_set_sync(private_client->temp_vblank, sync);
438
439         vblank_temp = calloc(1, sizeof *vblank_temp);
440         TDM_RETURN_VAL_IF_FAIL(vblank_temp != NULL, TDM_ERROR_OUT_OF_MEMORY);
441
442         vblank_temp->func = func;
443         vblank_temp->user_data = user_data;
444
445         return tdm_client_vblank_wait(private_client->temp_vblank, interval, _tdm_client_vblank_handler_temp, vblank_temp);
446 }
447
448 tdm_client_output*
449 tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
450 {
451         tdm_private_client *private_client;
452         tdm_private_client_output *private_output = NULL;
453
454         if (error)
455                 *error = TDM_ERROR_NONE;
456
457         if (!client) {
458                 TDM_ERR("'!client' failed");
459                 if (error)
460                         *error = TDM_ERROR_INVALID_PARAMETER;
461                 return NULL;
462         }
463
464         private_client = (tdm_private_client*)client;
465
466         if (!name)
467                 name = "primary";
468
469         LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) {
470                 if (!strncmp(private_output->name, name, TDM_NAME_LEN))
471                         return (tdm_client_output*)private_output;
472         }
473
474         private_output = calloc(1, sizeof *private_output);
475         if (!private_output) {
476                 TDM_ERR("alloc failed");
477                 if (error)
478                         *error = TDM_ERROR_OUT_OF_MEMORY;
479                 return NULL;
480         }
481
482         private_output->private_client = private_client;
483
484         snprintf(private_output->name, TDM_NAME_LEN, "%s", name);
485         private_output->output = wl_tdm_create_output(private_client->tdm, private_output->name);
486         if (!private_output->output) {
487                 TDM_ERR("couldn't create output resource");
488                 free(private_output);
489                 if (error)
490                         *error = TDM_ERROR_OUT_OF_MEMORY;
491                 return NULL;
492         }
493
494         LIST_INITHEAD(&private_output->vblank_list);
495         LIST_INITHEAD(&private_output->change_handler_list);
496         LIST_ADDTAIL(&private_output->link, &private_client->output_list);
497
498         wl_tdm_output_add_listener(private_output->output,
499                                                            &tdm_client_output_listener, private_output);
500         wl_display_roundtrip(private_client->display);
501
502         return (tdm_client_output*)private_output;
503 }
504
505 tdm_error
506 tdm_client_output_add_change_handler(tdm_client_output *output,
507                                                                          tdm_client_output_change_handler func,
508                                                                          void *user_data)
509 {
510         tdm_private_client_output *private_output;
511         tdm_client_output_handler_info *h;
512
513         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
514         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
515
516         private_output = (tdm_private_client_output*)output;
517
518         h = calloc(1, sizeof *h);
519         TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY);
520
521         if (LIST_IS_EMPTY(&private_output->change_handler_list)) {
522                 wl_tdm_output_watch_output_changes(private_output->output, 1);
523
524                 /* TODO: this is very tricky.
525                  * If a client adds the change_handler, we might be able to guess that
526                  * the client will watch the tdm client's fd and handle tdm events in
527                  * event loop. Otherwise, we CAN'T make sure if a client has event loop
528                  * which handles tdm events.
529                  */
530                 private_output->watch_output_changes = 1;
531         }
532
533         h->private_output = private_output;
534         h->func = func;
535         h->user_data = user_data;
536         LIST_ADDTAIL(&h->link, &private_output->change_handler_list);
537
538         return TDM_ERROR_NOT_IMPLEMENTED;
539 }
540
541 void
542 tdm_client_output_remove_change_handler(tdm_client_output *output,
543                                                                                 tdm_client_output_change_handler func,
544                                                                                 void *user_data)
545 {
546         tdm_private_client_output *private_output;
547         tdm_client_output_handler_info *h = NULL;
548
549         TDM_RETURN_IF_FAIL(output != NULL);
550         TDM_RETURN_IF_FAIL(func != NULL);
551
552         private_output = (tdm_private_client_output*)output;
553
554         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
555                 if (h->func != func || h->user_data != user_data)
556                         continue;
557
558                 LIST_DEL(&h->link);
559                 free(h);
560
561                 if (LIST_IS_EMPTY(&private_output->change_handler_list))
562                         wl_tdm_output_watch_output_changes(private_output->output, 0);
563
564                 return;
565         }
566 }
567
568 tdm_error
569 tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
570 {
571         tdm_private_client_output *private_output;
572         tdm_private_client *private_client;
573
574         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
575         TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER);
576
577         private_output = (tdm_private_client_output*)output;
578
579         if (private_output->watch_output_changes) {
580                 *refresh = private_output->refresh;
581                 return TDM_ERROR_NONE;
582         }
583
584         private_client = private_output->private_client;
585         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
586
587         wl_tdm_output_get_mode(private_output->output);
588         wl_display_roundtrip(private_client->display);
589
590         *refresh = private_output->refresh;
591
592         return TDM_ERROR_NONE;
593 }
594
595 tdm_error
596 tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
597 {
598         tdm_private_client_output *private_output;
599         tdm_private_client *private_client;
600
601         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
602         TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
603
604         private_output = (tdm_private_client_output*)output;
605
606         if (private_output->watch_output_changes) {
607                 *status = private_output->connection;
608                 return TDM_ERROR_NONE;
609         }
610
611         private_client = private_output->private_client;
612         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
613
614         wl_tdm_output_get_connection(private_output->output);
615         wl_display_roundtrip(private_client->display);
616
617         *status = private_output->connection;
618
619         return TDM_ERROR_NONE;
620 }
621
622 tdm_error
623 tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
624 {
625         tdm_private_client_output *private_output;
626         tdm_private_client *private_client;
627
628         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
629         TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER);
630
631         private_output = (tdm_private_client_output*)output;
632
633         if (private_output->watch_output_changes) {
634                 *dpms = private_output->dpms;
635                 return TDM_ERROR_NONE;
636         }
637
638         private_client = private_output->private_client;
639         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
640
641         wl_tdm_output_get_dpms(private_output->output);
642         wl_display_roundtrip(private_client->display);
643
644         *dpms = private_output->dpms;
645
646         return TDM_ERROR_NONE;
647 }
648
649 tdm_client_vblank*
650 tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
651 {
652         tdm_private_client_output *private_output;
653         tdm_private_client_vblank *private_vblank;
654
655         if (error)
656                 *error = TDM_ERROR_NONE;
657
658         if (!output) {
659                 TDM_ERR("'!output' failed");
660                 if (error)
661                         *error = TDM_ERROR_INVALID_PARAMETER;
662                 return NULL;
663         }
664
665         private_output = (tdm_private_client_output*)output;
666
667         private_vblank = calloc(1, sizeof *private_vblank);
668         if (!private_vblank) {
669                 TDM_ERR("alloc failed");
670                 if (error)
671                         *error = TDM_ERROR_OUT_OF_MEMORY;
672                 return NULL;
673         }
674
675         private_vblank->private_output = private_output;
676
677         private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output);
678         if (!private_vblank->vblank) {
679                 TDM_ERR("couldn't create vblank resource");
680                 free(private_vblank);
681                 if (error)
682                         *error = TDM_ERROR_OUT_OF_MEMORY;
683                 return NULL;
684         }
685
686         /* initial value */
687         private_vblank->fps = private_output->refresh;
688         private_vblank->offset = 0;
689         private_vblank->enable_fake = 0;
690
691         LIST_INITHEAD(&private_vblank->wait_list);
692         LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
693
694         wl_tdm_vblank_add_listener(private_vblank->vblank,
695                                                            &tdm_client_vblank_listener, private_vblank);
696
697         return (tdm_client_vblank*)private_vblank;
698 }
699
700 void
701 tdm_client_vblank_destroy(tdm_client_vblank *vblank)
702 {
703         tdm_private_client_vblank *private_vblank;
704         tdm_client_wait_info *w = NULL, *ww = NULL;
705
706         TDM_RETURN_IF_FAIL(vblank != NULL);
707
708         private_vblank = vblank;
709         LIST_DEL(&private_vblank->link);
710
711         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
712                 LIST_DEL(&w->link);
713                 free(w);
714         }
715
716         wl_tdm_vblank_destroy(private_vblank->vblank);
717
718         free(private_vblank);
719 }
720
721 tdm_error
722 tdm_client_vblank_set_name(tdm_client_vblank *vblank, const char *name)
723 {
724         tdm_private_client_vblank *private_vblank;
725
726         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
727
728         private_vblank = vblank;
729
730         if (!name)
731                 name = TDM_VBLANK_DEFAULT_NAME;
732
733         strncpy(private_vblank->name, name, TDM_NAME_LEN - 1);
734         private_vblank->name[TDM_NAME_LEN - 1] = '\0';
735
736         wl_tdm_vblank_set_name(private_vblank->vblank, private_vblank->name);
737
738         return TDM_ERROR_NONE;
739 }
740
741 tdm_error
742 tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync)
743 {
744         tdm_private_client_vblank *private_vblank;
745
746         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
747
748         private_vblank = vblank;
749         private_vblank->sync = sync;
750
751         return TDM_ERROR_NONE;
752 }
753
754 tdm_error
755 tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
756 {
757         tdm_private_client_vblank *private_vblank;
758
759         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
760         TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
761
762         private_vblank = vblank;
763
764         if (private_vblank->fps == fps)
765                 return TDM_ERROR_NONE;
766         private_vblank->fps = fps;
767
768         wl_tdm_vblank_set_fps(private_vblank->vblank, fps);
769
770         return TDM_ERROR_NONE;
771 }
772
773 tdm_error
774 tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
775 {
776         tdm_private_client_vblank *private_vblank;
777
778         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
779
780         private_vblank = vblank;
781         TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
782
783         if (private_vblank->offset == offset_ms)
784                 return TDM_ERROR_NONE;
785         private_vblank->offset = offset_ms;
786
787         wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms);
788
789         return TDM_ERROR_NONE;
790 }
791
792 tdm_error
793 tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake)
794 {
795         tdm_private_client_vblank *private_vblank;
796
797         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
798
799         private_vblank = vblank;
800
801         if (private_vblank->enable_fake == enable_fake)
802                 return TDM_ERROR_NONE;
803         private_vblank->enable_fake = enable_fake;
804
805         wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake);
806
807         return TDM_ERROR_NONE;
808 }
809
810 tdm_error
811 tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data)
812 {
813         tdm_private_client *private_client;
814         tdm_private_client_output *private_output;
815         tdm_private_client_vblank *private_vblank;
816         tdm_client_wait_info *w;
817         struct timespec tp;
818         unsigned int req_sec, req_usec;
819         int ret = 0;
820
821         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
822         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
823         /* can't support "interval 0" and "getting current_msc" things because
824          * there is a socket communication between TDM client and server. It's impossible
825          * to return the current msc or sequence immediately.
826          */
827         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
828
829         private_vblank = vblank;
830         private_output = private_vblank->private_output;
831         private_client = private_output->private_client;
832
833         if (!private_vblank->started)
834                 private_vblank->started = 1;
835
836         if (private_output->watch_output_changes)
837                 if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
838                         TDM_INFO("dpms off");
839                         return TDM_ERROR_DPMS_OFF;
840                 }
841
842         w = calloc(1, sizeof *w);
843         if (!w) {
844                 TDM_ERR("alloc failed");
845                 return TDM_ERROR_OUT_OF_MEMORY;
846         }
847
848         w->private_vblank = private_vblank;
849         w->func = func;
850         w->user_data = user_data;
851
852         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
853
854         clock_gettime(CLOCK_MONOTONIC, &tp);
855         req_sec = (unsigned int)tp.tv_sec;
856         req_usec = (unsigned int)(tp.tv_nsec / 1000);
857
858         w->req_id = ++private_output->req_id;
859         w->req_time = TDM_TIME(req_sec, req_usec);
860         w->need_free = (private_vblank->sync) ? 0 : 1;
861
862         wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, req_sec, req_usec);
863
864         TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
865
866         TDM_DBG("vblank(%p) interval(%u) req_id(%d) req(%.6f)",
867                         vblank, interval, w->req_id, TDM_TIME(req_sec, req_usec));
868
869         if (private_vblank->last_time >= w->req_time)
870                 TDM_ERR("'last(%.6f) < req(%.6f)' failed", private_vblank->last_time, w->req_time);
871
872         if (!private_vblank->sync) {
873                 wl_display_flush(private_client->display);
874                 return TDM_ERROR_NONE;
875         }
876
877         while (ret != -1 && !w->need_free)
878                 ret = wl_display_dispatch(private_client->display);
879
880         clock_gettime(CLOCK_MONOTONIC, &tp);
881         TDM_DBG("block during %d us",
882                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
883                         - (req_sec * 1000000 + req_usec));
884
885         LIST_DEL(&w->link);
886         free(w);
887
888         return TDM_ERROR_NONE;
889 }
890
891 tdm_error
892 tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence,
893                                                    tdm_client_vblank_handler func, void *user_data)
894 {
895         tdm_private_client *private_client;
896         tdm_private_client_output *private_output;
897         tdm_private_client_vblank *private_vblank;
898         tdm_client_wait_info *w;
899         struct timespec tp;
900         unsigned int req_sec, req_usec;
901         int ret = 0;
902
903         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
904         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
905
906         private_vblank = vblank;
907         private_output = private_vblank->private_output;
908         private_client = private_output->private_client;
909
910         if (!private_vblank->started)
911                 private_vblank->started = 1;
912
913         if (private_output->watch_output_changes)
914                 if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
915                         TDM_INFO("dpms off");
916                         return TDM_ERROR_DPMS_OFF;
917                 }
918
919         w = calloc(1, sizeof *w);
920         if (!w) {
921                 TDM_ERR("alloc failed");
922                 return TDM_ERROR_OUT_OF_MEMORY;
923         }
924
925         w->private_vblank = private_vblank;
926         w->func = func;
927         w->user_data = user_data;
928
929         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
930
931         clock_gettime(CLOCK_MONOTONIC, &tp);
932         req_sec = (unsigned int)tp.tv_sec;
933         req_usec = (unsigned int)(tp.tv_nsec / 1000);
934
935         w->req_id = ++private_output->req_id;
936         w->req_time = TDM_TIME(req_sec, req_usec);
937         w->need_free = (private_vblank->sync) ? 0 : 1;
938
939         wl_tdm_vblank_wait_vblank_seq(private_vblank->vblank, sequence, w->req_id, req_sec, req_usec);
940
941         TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
942
943         TDM_DBG("vblank(%p) sequence(%u) req_id(%d) req(%.6f)",
944                         vblank, sequence, w->req_id, TDM_TIME(req_sec, req_usec));
945
946         if (private_vblank->last_time >= w->req_time)
947                 TDM_ERR("'last(%.6f) < req(%.6f)' failed", private_vblank->last_time, w->req_time);
948
949         if (!private_vblank->sync) {
950                 wl_display_flush(private_client->display);
951                 return TDM_ERROR_NONE;
952         }
953
954         while (ret != -1 && !w->need_free)
955                 ret = wl_display_dispatch(private_client->display);
956
957         clock_gettime(CLOCK_MONOTONIC, &tp);
958         TDM_DBG("block during %d us",
959                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
960                         - (req_sec * 1000000 + req_usec));
961
962         LIST_DEL(&w->link);
963         free(w);
964
965         return TDM_ERROR_NONE;
966 }