tdm: don't send output changes if client doesn't watch them
[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         if (LIST_IS_EMPTY(&private_output->change_handler_list))
503                 wl_tdm_output_watch_output_changes(private_output->output, 1);
504
505         h->private_output = private_output;
506         h->func = func;
507         h->user_data = user_data;
508         LIST_ADDTAIL(&h->link, &private_output->change_handler_list);
509
510         return TDM_ERROR_NOT_IMPLEMENTED;
511 }
512
513 void
514 tdm_client_output_remove_change_handler(tdm_client_output *output,
515                                                                                 tdm_client_output_change_handler func,
516                                                                                 void *user_data)
517 {
518         tdm_private_client_output *private_output;
519         tdm_client_output_handler_info *h = NULL;
520
521         TDM_RETURN_IF_FAIL(output != NULL);
522         TDM_RETURN_IF_FAIL(func != NULL);
523
524         private_output = (tdm_private_client_output*)output;
525
526         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
527                 if (h->func != func || h->user_data != user_data)
528                         continue;
529
530                 LIST_DEL(&h->link);
531                 free(h);
532
533                 if (LIST_IS_EMPTY(&private_output->change_handler_list))
534                         wl_tdm_output_watch_output_changes(private_output->output, 0);
535
536                 return;
537         }
538 }
539
540 tdm_error
541 tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
542 {
543         tdm_private_client_output *private_output;
544
545         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
546         TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER);
547
548         private_output = (tdm_private_client_output*)output;
549
550         *refresh = private_output->refresh;
551
552         return TDM_ERROR_NONE;
553 }
554
555 tdm_error
556 tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
557 {
558         tdm_private_client_output *private_output;
559
560         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
561         TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
562
563         private_output = (tdm_private_client_output*)output;
564
565         *status = private_output->connection;
566
567         return TDM_ERROR_NONE;
568 }
569
570 tdm_error
571 tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
572 {
573         tdm_private_client_output *private_output;
574
575         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
576         TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER);
577
578         private_output = (tdm_private_client_output*)output;
579
580         *dpms = private_output->dpms;
581
582         return TDM_ERROR_NONE;
583 }
584
585 tdm_client_vblank*
586 tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
587 {
588         tdm_private_client_output *private_output;
589         tdm_private_client_vblank *private_vblank;
590
591         if (error)
592                 *error = TDM_ERROR_NONE;
593
594         if (!output) {
595                 TDM_ERR("'!output' failed");
596                 if (error)
597                         *error = TDM_ERROR_INVALID_PARAMETER;
598                 return NULL;
599         }
600
601         private_output = (tdm_private_client_output*)output;
602
603         private_vblank = calloc(1, sizeof *private_vblank);
604         if (!private_vblank) {
605                 TDM_ERR("alloc failed");
606                 if (error)
607                         *error = TDM_ERROR_OUT_OF_MEMORY;
608                 return NULL;
609         }
610
611         private_vblank->private_output = private_output;
612
613         private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output);
614         if (!private_vblank->vblank) {
615                 TDM_ERR("couldn't create vblank resource");
616                 free(private_vblank);
617                 if (error)
618                         *error = TDM_ERROR_OUT_OF_MEMORY;
619                 return NULL;
620         }
621
622         /* initial value */
623         private_vblank->fps = private_output->refresh;
624         private_vblank->offset = 0;
625         private_vblank->enable_fake = 0;
626
627         LIST_INITHEAD(&private_vblank->wait_list);
628         LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
629
630         wl_tdm_vblank_add_listener(private_vblank->vblank,
631                                                            &tdm_client_vblank_listener, private_vblank);
632
633         return (tdm_client_vblank*)private_vblank;
634 }
635
636 void
637 tdm_client_vblank_destroy(tdm_client_vblank *vblank)
638 {
639         tdm_private_client_vblank *private_vblank;
640         tdm_client_wait_info *w = NULL, *ww = NULL;
641
642         TDM_RETURN_IF_FAIL(vblank != NULL);
643
644         private_vblank = vblank;
645         LIST_DEL(&private_vblank->link);
646
647         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
648                 LIST_DEL(&w->link);
649                 free(w);
650         }
651
652         wl_tdm_vblank_destroy(private_vblank->vblank);
653
654         free(private_vblank);
655 }
656
657 tdm_error
658 tdm_client_vblank_set_name(tdm_client_vblank *vblank, const char *name)
659 {
660         tdm_private_client_vblank *private_vblank;
661
662         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
663
664         private_vblank = vblank;
665
666         if (!name)
667                 name = TDM_VBLANK_DEFAULT_NAME;
668
669         strncpy(private_vblank->name, name, TDM_NAME_LEN - 1);
670         private_vblank->name[TDM_NAME_LEN - 1] = '\0';
671
672         wl_tdm_vblank_set_name(private_vblank->vblank, private_vblank->name);
673
674         return TDM_ERROR_NONE;
675 }
676
677 tdm_error
678 tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync)
679 {
680         tdm_private_client_vblank *private_vblank;
681
682         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
683
684         private_vblank = vblank;
685         private_vblank->sync = sync;
686
687         return TDM_ERROR_NONE;
688 }
689
690 tdm_error
691 tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
692 {
693         tdm_private_client_vblank *private_vblank;
694
695         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
696         TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
697
698         private_vblank = vblank;
699
700         if (private_vblank->fps == fps)
701                 return TDM_ERROR_NONE;
702         private_vblank->fps = fps;
703
704         wl_tdm_vblank_set_fps(private_vblank->vblank, fps);
705
706         return TDM_ERROR_NONE;
707 }
708
709 tdm_error
710 tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
711 {
712         tdm_private_client_vblank *private_vblank;
713
714         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
715
716         private_vblank = vblank;
717         TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
718
719         if (private_vblank->offset == offset_ms)
720                 return TDM_ERROR_NONE;
721         private_vblank->offset = offset_ms;
722
723         wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms);
724
725         return TDM_ERROR_NONE;
726 }
727
728 tdm_error
729 tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake)
730 {
731         tdm_private_client_vblank *private_vblank;
732
733         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
734
735         private_vblank = vblank;
736
737         if (private_vblank->enable_fake == enable_fake)
738                 return TDM_ERROR_NONE;
739         private_vblank->enable_fake = enable_fake;
740
741         wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake);
742
743         return TDM_ERROR_NONE;
744 }
745
746 tdm_error
747 tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data)
748 {
749         tdm_private_client *private_client;
750         tdm_private_client_output *private_output;
751         tdm_private_client_vblank *private_vblank;
752         tdm_client_wait_info *w;
753         struct timespec tp;
754         int ret = 0;
755
756         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
757         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
758         /* can't support "interval 0" and "getting current_msc" things because
759          * there is a socket communication between TDM client and server. It's impossible
760          * to return the current msc or sequence immediately.
761          */
762         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
763
764         private_vblank = vblank;
765         private_output = private_vblank->private_output;
766         private_client = private_output->private_client;
767
768         if (!private_vblank->started)
769                 private_vblank->started = 1;
770
771         if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
772                 TDM_INFO("dpms off");
773                 return TDM_ERROR_DPMS_OFF;
774         }
775
776         w = calloc(1, sizeof *w);
777         if (!w) {
778                 TDM_ERR("alloc failed");
779                 return TDM_ERROR_OUT_OF_MEMORY;
780         }
781
782         w->private_vblank = private_vblank;
783         w->func = func;
784         w->user_data = user_data;
785
786         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
787
788         clock_gettime(CLOCK_MONOTONIC, &tp);
789         w->req_id = ++private_output->req_id;
790         w->req_sec = (unsigned int)tp.tv_sec;
791         w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
792         w->need_free = (private_vblank->sync) ? 0 : 1;
793
794         wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec);
795
796         TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
797
798         if (!private_vblank->sync) {
799                 wl_display_flush(private_client->display);
800                 return TDM_ERROR_NONE;
801         }
802
803         while (ret != -1 && !w->need_free)
804                 ret = wl_display_dispatch(private_client->display);
805
806         clock_gettime(CLOCK_MONOTONIC, &tp);
807         TDM_DBG("block during %d us",
808                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
809                         - (w->req_sec * 1000000 + w->req_usec));
810
811         LIST_DEL(&w->link);
812         free(w);
813
814         return TDM_ERROR_NONE;
815 }
816
817 tdm_error
818 tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence,
819                                                    tdm_client_vblank_handler func, void *user_data)
820 {
821         tdm_private_client *private_client;
822         tdm_private_client_output *private_output;
823         tdm_private_client_vblank *private_vblank;
824         tdm_client_wait_info *w;
825         struct timespec tp;
826         int ret = 0;
827
828         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
829         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
830
831         private_vblank = vblank;
832         private_output = private_vblank->private_output;
833         private_client = private_output->private_client;
834
835         if (!private_vblank->started)
836                 private_vblank->started = 1;
837
838         if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
839                 TDM_INFO("dpms off");
840                 return TDM_ERROR_DPMS_OFF;
841         }
842
843         w = calloc(1, sizeof *w);
844         if (!w) {
845                 TDM_ERR("alloc failed");
846                 return TDM_ERROR_OUT_OF_MEMORY;
847         }
848
849         w->private_vblank = private_vblank;
850         w->func = func;
851         w->user_data = user_data;
852
853         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
854
855         clock_gettime(CLOCK_MONOTONIC, &tp);
856         w->req_id = ++private_output->req_id;
857         w->req_sec = (unsigned int)tp.tv_sec;
858         w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
859         w->need_free = (private_vblank->sync) ? 0 : 1;
860
861         wl_tdm_vblank_wait_vblank_seq(private_vblank->vblank, sequence, w->req_id, w->req_sec, w->req_usec);
862
863         TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
864
865         if (!private_vblank->sync) {
866                 wl_display_flush(private_client->display);
867                 return TDM_ERROR_NONE;
868         }
869
870         while (ret != -1 && !w->need_free)
871                 ret = wl_display_dispatch(private_client->display);
872
873         clock_gettime(CLOCK_MONOTONIC, &tp);
874         TDM_DBG("block during %d us",
875                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
876                         - (w->req_sec * 1000000 + w->req_usec));
877
878         LIST_DEL(&w->link);
879         free(w);
880
881         return TDM_ERROR_NONE;
882 }