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