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