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