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