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