b7a80c198929f22110b276356d37568c125bc393
[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-client-protocol.h"
51
52 typedef struct _tdm_private_client_vblank tdm_private_client_vblank;
53
54 typedef struct _tdm_private_client {
55         struct wl_display *display;
56         struct wl_registry *registry;
57         struct wl_tdm *tdm;
58         struct list_head output_list;
59
60         tdm_private_client_vblank *temp_vblank;
61 } tdm_private_client;
62
63 typedef struct _tdm_private_client_output {
64         tdm_private_client *private_client;
65
66         char name[TDM_NAME_LEN];
67         struct wl_tdm_output *output;
68         int width;
69         int height;
70         int refresh;
71         tdm_output_conn_status connection;
72         tdm_output_dpms dpms;
73         struct list_head vblank_list;
74         struct list_head change_handler_list;
75
76         unsigned int req_id;
77
78         struct list_head link;
79 } tdm_private_client_output;
80
81 struct _tdm_private_client_vblank {
82         tdm_private_client_output *private_output;
83
84         struct wl_tdm_vblank *vblank;
85         struct list_head wait_list;
86
87         unsigned int sync;
88         unsigned int fps;
89         int offset;
90         unsigned int enable_fake;
91
92         unsigned int started;
93
94         struct list_head link;
95 };
96
97 typedef struct _tdm_client_output_handler_info {
98         tdm_private_client_output *private_output;
99
100         tdm_client_output_change_handler func;
101         void *user_data;
102
103         struct list_head link;
104 } tdm_client_output_handler_info;
105
106 typedef struct _tdm_client_wait_info {
107         tdm_private_client_vblank *private_vblank;
108
109         tdm_client_vblank_handler func;
110         void *user_data;
111
112         unsigned int req_id;
113         unsigned int req_sec;
114         unsigned int req_usec;
115         int need_free;
116
117         struct list_head link;
118 } tdm_client_wait_info;
119
120 static void
121 _tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank,
122                                                    uint32_t req_id, uint32_t sequence, uint32_t tv_sec,
123                                                    uint32_t tv_usec, uint32_t error)
124 {
125         tdm_private_client_vblank *private_vblank = data;
126         tdm_client_wait_info *w = NULL, *ww = NULL;
127
128         TDM_RETURN_IF_FAIL(private_vblank != NULL);
129
130         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
131                 if (w->req_id != req_id)
132                         continue;
133
134                 if (w->func)
135                         w->func(private_vblank, error, sequence, tv_sec, tv_usec, w->user_data);
136
137                 if (w->need_free) {
138                         LIST_DEL(&w->link);
139                         free(w);
140                 } else
141                         w->need_free = 1;
142                 return;
143         }
144 }
145
146 static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = {
147         _tdm_client_vblank_cb_done,
148 };
149
150 static void
151 _tdm_client_output_destroy(tdm_private_client_output *private_output)
152 {
153         tdm_private_client_vblank *v = NULL, *vv = NULL;
154         tdm_client_output_handler_info *h = NULL, *hh = NULL;
155
156         LIST_DEL(&private_output->link);
157
158         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_list, link) {
159                 TDM_ERR("vblanks SHOULD be destroyed first!");
160                 LIST_DEL(&v->link);
161                 v->private_output = NULL;
162         }
163
164         LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_output->change_handler_list, link) {
165                 LIST_DEL(&h->link);
166                 free(h);
167         }
168
169         wl_tdm_output_destroy(private_output->output);
170
171         free(private_output);
172 }
173
174 static void
175 _tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output,
176                                                    uint32_t width, uint32_t height, uint32_t refresh)
177 {
178         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
179
180         TDM_RETURN_IF_FAIL(private_output != NULL);
181
182         private_output->width = width;
183         private_output->height = height;
184         private_output->refresh = refresh;
185
186         TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)",
187                         private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output),
188                         width, height, refresh);
189 }
190
191 static void
192 _tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
193 {
194         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
195         tdm_client_output_handler_info *h = NULL;
196         tdm_value v;
197
198         TDM_RETURN_IF_FAIL(private_output != NULL);
199
200         if (private_output->connection == value)
201                 return;
202
203         private_output->connection = value;
204
205         TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)",
206                         private_output,
207                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
208                         value);
209
210         v.u32 = value;
211         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
212                 if (h->func)
213                         h->func(private_output, TDM_OUTPUT_CHANGE_CONNECTION, v, h->user_data);
214         }
215 }
216
217 static void
218 _tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
219 {
220         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
221         tdm_client_output_handler_info *h = NULL;
222         tdm_value v;
223
224         TDM_RETURN_IF_FAIL(private_output != NULL);
225
226         if (private_output->dpms == value)
227                 return;
228
229         private_output->dpms = value;
230
231         TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)",
232                         private_output,
233                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
234                         value);
235
236         v.u32 = value;
237         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
238                 if (h->func)
239                         h->func(private_output, TDM_OUTPUT_CHANGE_DPMS, v, h->user_data);
240         }
241 }
242
243 static const struct wl_tdm_output_listener tdm_client_output_listener = {
244         _tdm_client_output_cb_mode,
245         _tdm_client_output_cb_connection,
246         _tdm_client_output_cb_dpms,
247 };
248
249 static void
250 _tdm_client_cb_global(void *data, struct wl_registry *registry,
251                                           uint32_t name, const char *interface,
252                                           uint32_t version)
253 {
254         tdm_private_client *private_client = data;
255
256         if (strncmp(interface, "wl_tdm", 6) == 0) {
257                 private_client->tdm =
258                         wl_registry_bind(registry, name, &wl_tdm_interface, version);
259                 TDM_RETURN_IF_FAIL(private_client->tdm != NULL);
260
261                 wl_display_flush(private_client->display);
262         }
263 }
264
265 static void
266 _tdm_client_cb_global_remove(void *data, struct wl_registry *registry, uint32_t name)
267 {
268 }
269
270 static const struct wl_registry_listener tdm_client_registry_listener = {
271         _tdm_client_cb_global,
272         _tdm_client_cb_global_remove
273 };
274
275 tdm_client*
276 tdm_client_create(tdm_error *error)
277 {
278         tdm_private_client *private_client;
279
280         private_client = calloc(1, sizeof *private_client);
281         if (!private_client) {
282                 TDM_ERR("alloc failed");
283                 if (error)
284                         *error = TDM_ERROR_OUT_OF_MEMORY;
285                 return NULL;
286         }
287
288         LIST_INITHEAD(&private_client->output_list);
289
290         private_client->display = wl_display_connect("tdm-socket");
291         TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed);
292
293         private_client->registry = wl_display_get_registry(private_client->display);
294         TDM_GOTO_IF_FAIL(private_client->registry != NULL, create_failed);
295
296         wl_registry_add_listener(private_client->registry,
297                                                          &tdm_client_registry_listener, private_client);
298         wl_display_roundtrip(private_client->display);
299
300         /* check global objects */
301         TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed);
302
303         if (error)
304                 *error = TDM_ERROR_NONE;
305
306         return (tdm_client*)private_client;
307 create_failed:
308         tdm_client_destroy((tdm_client*)private_client);
309         if (error)
310                 *error = TDM_ERROR_OPERATION_FAILED;
311         return NULL;
312 }
313
314 void
315 tdm_client_destroy(tdm_client *client)
316 {
317         tdm_private_client *private_client = (tdm_private_client*)client;
318         tdm_private_client_output *o = NULL, *oo = NULL;
319
320         if (!private_client)
321                 return;
322
323         if (private_client->temp_vblank)
324                 tdm_client_vblank_destroy(private_client->temp_vblank);
325
326         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) {
327                 _tdm_client_output_destroy(o);
328         }
329
330         if (private_client->tdm)
331                 wl_tdm_destroy(private_client->tdm);
332         if (private_client->registry)
333                 wl_registry_destroy(private_client->registry);
334         if (private_client->display)
335                 wl_display_disconnect(private_client->display);
336
337         free(private_client);
338 }
339
340 tdm_error
341 tdm_client_get_fd(tdm_client *client, int *fd)
342 {
343         tdm_private_client *private_client;
344
345         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
346         TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_ERROR_INVALID_PARAMETER);
347
348         private_client = (tdm_private_client*)client;
349
350         *fd = wl_display_get_fd(private_client->display);
351         if (*fd < 0)
352                 return TDM_ERROR_OPERATION_FAILED;
353
354         return TDM_ERROR_NONE;
355 }
356
357 tdm_error
358 tdm_client_handle_events(tdm_client *client)
359 {
360         tdm_private_client *private_client;
361
362         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
363
364         private_client = (tdm_private_client*)client;
365
366         wl_display_dispatch(private_client->display);
367
368         return TDM_ERROR_NONE;
369 }
370
371 typedef struct _tdm_client_vblank_temp {
372         tdm_client_vblank_handler2 func;
373         void *user_data;
374 } tdm_client_vblank_temp;
375
376 static void
377 _tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
378                                                                 unsigned int tv_sec, unsigned int tv_usec, void *user_data)
379 {
380         tdm_client_vblank_temp *vblank_temp = user_data;
381
382         TDM_RETURN_IF_FAIL(vblank_temp != NULL);
383
384         if (vblank_temp->func)
385                 vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data);
386
387         free(vblank_temp);
388 }
389
390 tdm_error
391 tdm_client_wait_vblank(tdm_client *client, char *name,
392                                            int sw_timer, int interval, int sync,
393                                            tdm_client_vblank_handler2 func, void *user_data)
394 {
395         tdm_private_client *private_client = (tdm_private_client*)client;
396         tdm_client_output *output;
397         tdm_client_vblank_temp *vblank_temp;
398         tdm_error ret;
399
400         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
401         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
402         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
403
404         if (!private_client->temp_vblank) {
405                 output = tdm_client_get_output(client, name, &ret);
406                 TDM_RETURN_VAL_IF_FAIL(output != NULL, ret);
407
408                 private_client->temp_vblank = tdm_client_output_create_vblank(output, &ret);
409                 TDM_RETURN_VAL_IF_FAIL(private_client->temp_vblank != NULL, ret);
410         }
411
412         tdm_client_vblank_set_enable_fake(private_client->temp_vblank, sw_timer);
413         tdm_client_vblank_set_sync(private_client->temp_vblank, sync);
414
415         vblank_temp = calloc(1, sizeof *vblank_temp);
416         TDM_RETURN_VAL_IF_FAIL(vblank_temp != NULL, TDM_ERROR_OUT_OF_MEMORY);
417
418         vblank_temp->func = func;
419         vblank_temp->user_data = user_data;
420
421         return tdm_client_vblank_wait(private_client->temp_vblank, interval, _tdm_client_vblank_handler_temp, vblank_temp);
422 }
423
424 tdm_client_output*
425 tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
426 {
427         tdm_private_client *private_client;
428         tdm_private_client_output *private_output = NULL;
429
430         if (error)
431                 *error = TDM_ERROR_NONE;
432
433         if (!client) {
434                 TDM_ERR("'!client' failed");
435                 if (error)
436                         *error = TDM_ERROR_INVALID_PARAMETER;
437                 return NULL;
438         }
439
440         private_client = (tdm_private_client*)client;
441
442         if (!name)
443                 name = "primary";
444
445         LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) {
446                 if (!strncmp(private_output->name, name, TDM_NAME_LEN))
447                         return (tdm_client_output*)private_output;
448         }
449
450         private_output = calloc(1, sizeof *private_output);
451         if (!private_output) {
452                 TDM_ERR("alloc failed");
453                 if (error)
454                         *error = TDM_ERROR_OUT_OF_MEMORY;
455                 return NULL;
456         }
457
458         private_output->private_client = private_client;
459
460         snprintf(private_output->name, TDM_NAME_LEN, "%s", name);
461         private_output->output = wl_tdm_create_output(private_client->tdm, private_output->name);
462         if (!private_output->output) {
463                 TDM_ERR("couldn't create output resource");
464                 free(private_output);
465                 if (error)
466                         *error = TDM_ERROR_OUT_OF_MEMORY;
467                 return NULL;
468         }
469
470         LIST_INITHEAD(&private_output->vblank_list);
471         LIST_INITHEAD(&private_output->change_handler_list);
472         LIST_ADDTAIL(&private_output->link, &private_client->output_list);
473
474         wl_tdm_output_add_listener(private_output->output,
475                                                            &tdm_client_output_listener, private_output);
476         wl_display_roundtrip(private_client->display);
477
478         return (tdm_client_output*)private_output;
479 }
480
481 tdm_error
482 tdm_client_output_add_change_handler(tdm_client_output *output,
483                                                                          tdm_client_output_change_handler func,
484                                                                          void *user_data)
485 {
486         tdm_private_client_output *private_output;
487         tdm_client_output_handler_info *h;
488
489         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
490         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
491
492         private_output = (tdm_private_client_output*)output;
493
494         h = calloc(1, sizeof *h);
495         TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY);
496
497         h->private_output = private_output;
498         h->func = func;
499         h->user_data = user_data;
500         LIST_ADDTAIL(&h->link, &private_output->change_handler_list);
501
502         return TDM_ERROR_NOT_IMPLEMENTED;
503 }
504
505 void
506 tdm_client_output_remove_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 = NULL;
512
513         TDM_RETURN_IF_FAIL(output != NULL);
514         TDM_RETURN_IF_FAIL(func != NULL);
515
516         private_output = (tdm_private_client_output*)output;
517
518         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
519                 if (h->func != func || h->user_data != user_data)
520                         continue;
521
522                 LIST_DEL(&h->link);
523                 free(h);
524                 return;
525         }
526 }
527
528 tdm_error
529 tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
530 {
531         tdm_private_client_output *private_output;
532
533         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
534         TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER);
535
536         private_output = (tdm_private_client_output*)output;
537
538         *refresh = private_output->refresh;
539
540         return TDM_ERROR_NONE;
541 }
542
543 tdm_error
544 tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
545 {
546         tdm_private_client_output *private_output;
547
548         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
549         TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
550
551         private_output = (tdm_private_client_output*)output;
552
553         *status = private_output->connection;
554
555         return TDM_ERROR_NONE;
556 }
557
558 tdm_error
559 tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
560 {
561         tdm_private_client_output *private_output;
562
563         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
564         TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER);
565
566         private_output = (tdm_private_client_output*)output;
567
568         *dpms = private_output->dpms;
569
570         return TDM_ERROR_NONE;
571 }
572
573 tdm_client_vblank*
574 tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
575 {
576         tdm_private_client_output *private_output;
577         tdm_private_client_vblank *private_vblank;
578
579         if (error)
580                 *error = TDM_ERROR_NONE;
581
582         if (!output) {
583                 TDM_ERR("'!output' failed");
584                 if (error)
585                         *error = TDM_ERROR_INVALID_PARAMETER;
586                 return NULL;
587         }
588
589         private_output = (tdm_private_client_output*)output;
590
591         private_vblank = calloc(1, sizeof *private_vblank);
592         if (!private_vblank) {
593                 TDM_ERR("alloc failed");
594                 if (error)
595                         *error = TDM_ERROR_OUT_OF_MEMORY;
596                 return NULL;
597         }
598
599         private_vblank->private_output = private_output;
600
601         private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output);
602         if (!private_vblank->vblank) {
603                 TDM_ERR("couldn't create vblank resource");
604                 free(private_vblank);
605                 if (error)
606                         *error = TDM_ERROR_OUT_OF_MEMORY;
607                 return NULL;
608         }
609
610         /* initial value */
611         private_vblank->fps = private_output->refresh;
612         private_vblank->offset = 0;
613         private_vblank->enable_fake = 0;
614
615         LIST_INITHEAD(&private_vblank->wait_list);
616         LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
617
618         wl_tdm_vblank_add_listener(private_vblank->vblank,
619                                                            &tdm_client_vblank_listener, private_vblank);
620
621         return (tdm_client_vblank*)private_vblank;
622 }
623
624 void
625 tdm_client_vblank_destroy(tdm_client_vblank *vblank)
626 {
627         tdm_private_client_vblank *private_vblank;
628         tdm_client_wait_info *w = NULL, *ww = NULL;
629
630         TDM_RETURN_IF_FAIL(vblank != NULL);
631
632         private_vblank = vblank;
633         LIST_DEL(&private_vblank->link);
634
635         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
636                 LIST_DEL(&w->link);
637                 free(w);
638         }
639
640         wl_tdm_vblank_destroy(private_vblank->vblank);
641
642         free(private_vblank);
643 }
644
645 tdm_error
646 tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync)
647 {
648         tdm_private_client_vblank *private_vblank;
649
650         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
651
652         private_vblank = vblank;
653         private_vblank->sync = sync;
654
655         return TDM_ERROR_NONE;
656 }
657
658 tdm_error
659 tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
660 {
661         tdm_private_client_vblank *private_vblank;
662
663         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
664         TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
665
666         private_vblank = vblank;
667         TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
668
669         if (private_vblank->fps == fps)
670                 return TDM_ERROR_NONE;
671         private_vblank->fps = fps;
672
673         wl_tdm_vblank_set_fps(private_vblank->vblank, fps);
674
675         return TDM_ERROR_NONE;
676 }
677
678 tdm_error
679 tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
680 {
681         tdm_private_client_vblank *private_vblank;
682
683         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
684
685         private_vblank = vblank;
686         TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
687
688         if (private_vblank->offset == offset_ms)
689                 return TDM_ERROR_NONE;
690         private_vblank->offset = offset_ms;
691
692         wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms);
693
694         return TDM_ERROR_NONE;
695 }
696
697 tdm_error
698 tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake)
699 {
700         tdm_private_client_vblank *private_vblank;
701
702         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
703
704         private_vblank = vblank;
705
706         if (private_vblank->enable_fake == enable_fake)
707                 return TDM_ERROR_NONE;
708         private_vblank->enable_fake = enable_fake;
709
710         wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake);
711
712         return TDM_ERROR_NONE;
713 }
714
715 tdm_error
716 tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data)
717 {
718         tdm_private_client *private_client;
719         tdm_private_client_output *private_output;
720         tdm_private_client_vblank *private_vblank;
721         tdm_client_wait_info *w;
722         struct timespec tp;
723         int ret = 0;
724
725         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
726         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
727         /* can't support "interval 0" and "getting current_msc" things because
728          * there is a socket communication between TDM client and server. It's impossible
729          * to return the current msc or sequence immediately.
730          */
731         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
732
733         private_vblank = vblank;
734         private_output = private_vblank->private_output;
735         private_client = private_output->private_client;
736
737         if (!private_vblank->started)
738                 private_vblank->started = 1;
739
740         if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
741                 TDM_INFO("dpms off");
742                 return TDM_ERROR_DPMS_OFF;
743         }
744
745         w = calloc(1, sizeof *w);
746         if (!w) {
747                 TDM_ERR("alloc failed");
748                 return TDM_ERROR_OUT_OF_MEMORY;
749         }
750
751         w->private_vblank = private_vblank;
752         w->func = func;
753         w->user_data = user_data;
754
755         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
756
757         clock_gettime(CLOCK_MONOTONIC, &tp);
758         w->req_id = ++private_output->req_id;
759         w->req_sec = (unsigned int)tp.tv_sec;
760         w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
761         w->need_free = (private_vblank->sync) ? 0 : 1;
762
763         wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec);
764
765         if (!private_vblank->sync) {
766                 wl_display_flush(private_client->display);
767                 return TDM_ERROR_NONE;
768         }
769
770         while (ret != -1 && !w->need_free)
771                 ret = wl_display_dispatch(private_client->display);
772
773         clock_gettime(CLOCK_MONOTONIC, &tp);
774         TDM_DBG("block during %d us",
775                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
776                         - (w->req_sec * 1000000 + w->req_usec));
777
778         LIST_DEL(&w->link);
779         free(w);
780
781         return TDM_ERROR_NONE;
782 }