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