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