vblank: add set_vblank_fps functionality for the given PID
[platform/core/uifw/libtdm.git] / client / tdm_client.c
1 /**************************************************************************
2  *
3  * libtdm
4  *
5  * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6  *
7  * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8  *          JinYoung Jeon <jy0.jeon@samsung.com>,
9  *          Taeheon Kim <th908.kim@samsung.com>,
10  *          YoungJun Cho <yj44.cho@samsung.com>,
11  *          SooChan Lim <sc1.lim@samsung.com>,
12  *          Boram Park <sc1.lim@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <time.h>
44 #include <strings.h>
45
46 #include "tdm_client.h"
47 #include "tdm_log.h"
48 #include "tdm_macro.h"
49 #include "tdm_list.h"
50 #include "tdm.h"
51 #include "tdm_private.h"
52 #include "tdm-client-protocol.h"
53
54 typedef struct _tdm_private_client_vblank tdm_private_client_vblank;
55
56 typedef struct _tdm_private_client {
57         struct wl_display *display;
58         struct wl_registry *registry;
59         struct wl_tdm *tdm;
60         struct list_head output_list;
61
62         tdm_private_client_vblank *temp_vblank;
63 } tdm_private_client;
64
65 typedef struct _tdm_private_client_output {
66         tdm_private_client *private_client;
67
68         char name[TDM_NAME_LEN];
69         struct wl_tdm_output *output;
70         int width;
71         int height;
72         int refresh;
73         tdm_output_conn_status connection;
74         tdm_output_dpms dpms;
75         struct list_head vblank_list;
76         struct list_head change_handler_list;
77
78         unsigned int req_id;
79
80         struct list_head link;
81 } tdm_private_client_output;
82
83 struct _tdm_private_client_vblank {
84         tdm_private_client_output *private_output;
85
86         struct wl_tdm_vblank *vblank;
87         struct list_head wait_list;
88
89         char name[TDM_NAME_LEN];
90         unsigned int sync;
91         unsigned int fps;
92         int offset;
93         unsigned int enable_fake;
94
95         unsigned int started;
96
97         struct list_head link;
98 };
99
100 typedef struct _tdm_client_output_handler_info {
101         tdm_private_client_output *private_output;
102
103         tdm_client_output_change_handler func;
104         void *user_data;
105
106         struct list_head link;
107 } tdm_client_output_handler_info;
108
109 typedef struct _tdm_client_wait_info {
110         tdm_private_client_vblank *private_vblank;
111
112         tdm_client_vblank_handler func;
113         void *user_data;
114
115         unsigned int req_id;
116         unsigned int req_sec;
117         unsigned int req_usec;
118         int need_free;
119
120         struct list_head link;
121 } tdm_client_wait_info;
122
123 static void
124 _tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank,
125                                                    uint32_t req_id, uint32_t sequence, uint32_t tv_sec,
126                                                    uint32_t tv_usec, uint32_t error)
127 {
128         tdm_private_client_vblank *private_vblank = data;
129         tdm_client_wait_info *w = NULL, *ww = NULL;
130
131         TDM_RETURN_IF_FAIL(private_vblank != NULL);
132
133         TDM_TRACE_COUNT(ClientDoneVBlank, req_id);
134
135         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
136                 if (w->req_id != req_id)
137                         continue;
138
139                 if (w->func)
140                         w->func(private_vblank, error, sequence, tv_sec, tv_usec, w->user_data);
141
142                 if (w->need_free) {
143                         LIST_DEL(&w->link);
144                         free(w);
145                 } else
146                         w->need_free = 1;
147                 return;
148         }
149 }
150
151 static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = {
152         _tdm_client_vblank_cb_done,
153 };
154
155 static void
156 _tdm_client_output_destroy(tdm_private_client_output *private_output)
157 {
158         tdm_private_client_vblank *v = NULL, *vv = NULL;
159         tdm_client_output_handler_info *h = NULL, *hh = NULL;
160
161         LIST_DEL(&private_output->link);
162
163         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_list, link) {
164                 TDM_ERR("vblanks SHOULD be destroyed first!");
165                 LIST_DEL(&v->link);
166                 v->private_output = NULL;
167         }
168
169         LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_output->change_handler_list, link) {
170                 LIST_DEL(&h->link);
171                 free(h);
172         }
173
174         wl_tdm_output_destroy(private_output->output);
175
176         free(private_output);
177 }
178
179 static void
180 _tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output,
181                                                    uint32_t width, uint32_t height, uint32_t refresh)
182 {
183         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
184
185         TDM_RETURN_IF_FAIL(private_output != NULL);
186
187         private_output->width = width;
188         private_output->height = height;
189         private_output->refresh = refresh;
190
191         TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)",
192                         private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output),
193                         width, height, refresh);
194 }
195
196 static void
197 _tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
198 {
199         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
200         tdm_client_output_handler_info *h = NULL;
201         tdm_value v;
202
203         TDM_RETURN_IF_FAIL(private_output != NULL);
204
205         if (private_output->connection == value)
206                 return;
207
208         private_output->connection = value;
209
210         TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)",
211                         private_output,
212                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
213                         value);
214
215         v.u32 = value;
216         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
217                 if (h->func)
218                         h->func(private_output, TDM_OUTPUT_CHANGE_CONNECTION, v, h->user_data);
219         }
220 }
221
222 static void
223 _tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
224 {
225         tdm_private_client_output *private_output = (tdm_private_client_output*)data;
226         tdm_client_output_handler_info *h = NULL;
227         tdm_value v;
228
229         TDM_RETURN_IF_FAIL(private_output != NULL);
230
231         if (private_output->dpms == value)
232                 return;
233
234         private_output->dpms = value;
235
236         TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)",
237                         private_output,
238                         wl_proxy_get_id((struct wl_proxy*)private_output->output),
239                         value);
240
241         v.u32 = value;
242         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
243                 if (h->func)
244                         h->func(private_output, TDM_OUTPUT_CHANGE_DPMS, v, h->user_data);
245         }
246 }
247
248 static const struct wl_tdm_output_listener tdm_client_output_listener = {
249         _tdm_client_output_cb_mode,
250         _tdm_client_output_cb_connection,
251         _tdm_client_output_cb_dpms,
252 };
253
254 static void
255 _tdm_client_cb_global(void *data, struct wl_registry *registry,
256                                           uint32_t name, const char *interface,
257                                           uint32_t version)
258 {
259         tdm_private_client *private_client = data;
260
261         if (strncmp(interface, "wl_tdm", 6) == 0) {
262                 private_client->tdm =
263                         wl_registry_bind(registry, name, &wl_tdm_interface, version);
264                 TDM_RETURN_IF_FAIL(private_client->tdm != NULL);
265
266                 wl_display_flush(private_client->display);
267         }
268 }
269
270 static void
271 _tdm_client_cb_global_remove(void *data, struct wl_registry *registry, uint32_t name)
272 {
273 }
274
275 static const struct wl_registry_listener tdm_client_registry_listener = {
276         _tdm_client_cb_global,
277         _tdm_client_cb_global_remove
278 };
279
280 tdm_client*
281 tdm_client_create(tdm_error *error)
282 {
283         tdm_private_client *private_client;
284
285         private_client = calloc(1, sizeof *private_client);
286         if (!private_client) {
287                 TDM_ERR("alloc failed");
288                 if (error)
289                         *error = TDM_ERROR_OUT_OF_MEMORY;
290                 return NULL;
291         }
292
293         LIST_INITHEAD(&private_client->output_list);
294
295         private_client->display = wl_display_connect("tdm-socket");
296         TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed);
297
298         private_client->registry = wl_display_get_registry(private_client->display);
299         TDM_GOTO_IF_FAIL(private_client->registry != NULL, create_failed);
300
301         wl_registry_add_listener(private_client->registry,
302                                                          &tdm_client_registry_listener, private_client);
303         wl_display_roundtrip(private_client->display);
304
305         /* check global objects */
306         TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed);
307
308         if (error)
309                 *error = TDM_ERROR_NONE;
310
311         return (tdm_client*)private_client;
312 create_failed:
313         tdm_client_destroy((tdm_client*)private_client);
314         if (error)
315                 *error = TDM_ERROR_OPERATION_FAILED;
316         return NULL;
317 }
318
319 void
320 tdm_client_destroy(tdm_client *client)
321 {
322         tdm_private_client *private_client = (tdm_private_client*)client;
323         tdm_private_client_output *o = NULL, *oo = NULL;
324
325         if (!private_client)
326                 return;
327
328         if (private_client->temp_vblank)
329                 tdm_client_vblank_destroy(private_client->temp_vblank);
330
331         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) {
332                 _tdm_client_output_destroy(o);
333         }
334
335         if (private_client->tdm)
336                 wl_tdm_destroy(private_client->tdm);
337         if (private_client->registry)
338                 wl_registry_destroy(private_client->registry);
339         if (private_client->display)
340                 wl_display_disconnect(private_client->display);
341
342         free(private_client);
343 }
344
345 tdm_error
346 tdm_client_get_fd(tdm_client *client, int *fd)
347 {
348         tdm_private_client *private_client;
349
350         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
351         TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_ERROR_INVALID_PARAMETER);
352
353         private_client = (tdm_private_client*)client;
354
355         *fd = wl_display_get_fd(private_client->display);
356         if (*fd < 0)
357                 return TDM_ERROR_OPERATION_FAILED;
358
359         return TDM_ERROR_NONE;
360 }
361
362 tdm_error
363 tdm_client_handle_events(tdm_client *client)
364 {
365         tdm_private_client *private_client;
366
367         TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
368
369         private_client = (tdm_private_client*)client;
370
371         wl_display_dispatch(private_client->display);
372
373         return TDM_ERROR_NONE;
374 }
375
376 typedef struct _tdm_client_vblank_temp {
377         tdm_client_vblank_handler2 func;
378         void *user_data;
379 } tdm_client_vblank_temp;
380
381 static void
382 _tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
383                                                                 unsigned int tv_sec, unsigned int tv_usec, void *user_data)
384 {
385         tdm_client_vblank_temp *vblank_temp = user_data;
386
387         TDM_RETURN_IF_FAIL(vblank_temp != NULL);
388
389         if (vblank_temp->func)
390                 vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data);
391
392         free(vblank_temp);
393 }
394
395 tdm_error
396 tdm_client_wait_vblank(tdm_client *client, char *name,
397                                            int sw_timer, int interval, int sync,
398                                            tdm_client_vblank_handler2 func, void *user_data)
399 {
400         tdm_private_client *private_client = (tdm_private_client*)client;
401         tdm_client_output *output;
402         tdm_client_vblank_temp *vblank_temp;
403         tdm_error ret;
404
405         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
406         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
407         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
408
409         if (!private_client->temp_vblank) {
410                 output = tdm_client_get_output(client, name, &ret);
411                 TDM_RETURN_VAL_IF_FAIL(output != NULL, ret);
412
413                 private_client->temp_vblank = tdm_client_output_create_vblank(output, &ret);
414                 TDM_RETURN_VAL_IF_FAIL(private_client->temp_vblank != NULL, ret);
415         }
416
417         tdm_client_vblank_set_enable_fake(private_client->temp_vblank, sw_timer);
418         tdm_client_vblank_set_sync(private_client->temp_vblank, sync);
419
420         vblank_temp = calloc(1, sizeof *vblank_temp);
421         TDM_RETURN_VAL_IF_FAIL(vblank_temp != NULL, TDM_ERROR_OUT_OF_MEMORY);
422
423         vblank_temp->func = func;
424         vblank_temp->user_data = user_data;
425
426         return tdm_client_vblank_wait(private_client->temp_vblank, interval, _tdm_client_vblank_handler_temp, vblank_temp);
427 }
428
429 tdm_error
430 tdm_client_set_client_vblank_fps(tdm_client *client, pid_t pid, const char *name, unsigned int fps)
431 {
432         tdm_private_client *private_client = (tdm_private_client*)client;
433
434         TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
435         TDM_RETURN_VAL_IF_FAIL(pid > 0, TDM_ERROR_INVALID_PARAMETER);
436         TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
437
438         if (!name)
439                 name = TDM_VBLANK_DEFAULT_NAME;
440
441         wl_tdm_set_client_vblank_fps(private_client->tdm, pid, name, fps);
442
443         wl_display_flush(private_client->display);
444
445         return TDM_ERROR_NONE;
446 }
447
448 tdm_client_output*
449 tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
450 {
451         tdm_private_client *private_client;
452         tdm_private_client_output *private_output = NULL;
453
454         if (error)
455                 *error = TDM_ERROR_NONE;
456
457         if (!client) {
458                 TDM_ERR("'!client' failed");
459                 if (error)
460                         *error = TDM_ERROR_INVALID_PARAMETER;
461                 return NULL;
462         }
463
464         private_client = (tdm_private_client*)client;
465
466         if (!name)
467                 name = "primary";
468
469         LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) {
470                 if (!strncmp(private_output->name, name, TDM_NAME_LEN))
471                         return (tdm_client_output*)private_output;
472         }
473
474         private_output = calloc(1, sizeof *private_output);
475         if (!private_output) {
476                 TDM_ERR("alloc failed");
477                 if (error)
478                         *error = TDM_ERROR_OUT_OF_MEMORY;
479                 return NULL;
480         }
481
482         private_output->private_client = private_client;
483
484         snprintf(private_output->name, TDM_NAME_LEN, "%s", name);
485         private_output->output = wl_tdm_create_output(private_client->tdm, private_output->name);
486         if (!private_output->output) {
487                 TDM_ERR("couldn't create output resource");
488                 free(private_output);
489                 if (error)
490                         *error = TDM_ERROR_OUT_OF_MEMORY;
491                 return NULL;
492         }
493
494         LIST_INITHEAD(&private_output->vblank_list);
495         LIST_INITHEAD(&private_output->change_handler_list);
496         LIST_ADDTAIL(&private_output->link, &private_client->output_list);
497
498         wl_tdm_output_add_listener(private_output->output,
499                                                            &tdm_client_output_listener, private_output);
500         wl_display_roundtrip(private_client->display);
501
502         return (tdm_client_output*)private_output;
503 }
504
505 tdm_error
506 tdm_client_output_add_change_handler(tdm_client_output *output,
507                                                                          tdm_client_output_change_handler func,
508                                                                          void *user_data)
509 {
510         tdm_private_client_output *private_output;
511         tdm_client_output_handler_info *h;
512
513         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
514         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
515
516         private_output = (tdm_private_client_output*)output;
517
518         h = calloc(1, sizeof *h);
519         TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY);
520
521         h->private_output = private_output;
522         h->func = func;
523         h->user_data = user_data;
524         LIST_ADDTAIL(&h->link, &private_output->change_handler_list);
525
526         return TDM_ERROR_NOT_IMPLEMENTED;
527 }
528
529 void
530 tdm_client_output_remove_change_handler(tdm_client_output *output,
531                                                                                 tdm_client_output_change_handler func,
532                                                                                 void *user_data)
533 {
534         tdm_private_client_output *private_output;
535         tdm_client_output_handler_info *h = NULL;
536
537         TDM_RETURN_IF_FAIL(output != NULL);
538         TDM_RETURN_IF_FAIL(func != NULL);
539
540         private_output = (tdm_private_client_output*)output;
541
542         LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
543                 if (h->func != func || h->user_data != user_data)
544                         continue;
545
546                 LIST_DEL(&h->link);
547                 free(h);
548                 return;
549         }
550 }
551
552 tdm_error
553 tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
554 {
555         tdm_private_client_output *private_output;
556
557         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
558         TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER);
559
560         private_output = (tdm_private_client_output*)output;
561
562         *refresh = private_output->refresh;
563
564         return TDM_ERROR_NONE;
565 }
566
567 tdm_error
568 tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
569 {
570         tdm_private_client_output *private_output;
571
572         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
573         TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
574
575         private_output = (tdm_private_client_output*)output;
576
577         *status = private_output->connection;
578
579         return TDM_ERROR_NONE;
580 }
581
582 tdm_error
583 tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
584 {
585         tdm_private_client_output *private_output;
586
587         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
588         TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER);
589
590         private_output = (tdm_private_client_output*)output;
591
592         *dpms = private_output->dpms;
593
594         return TDM_ERROR_NONE;
595 }
596
597 tdm_client_vblank*
598 tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
599 {
600         tdm_private_client_output *private_output;
601         tdm_private_client_vblank *private_vblank;
602
603         if (error)
604                 *error = TDM_ERROR_NONE;
605
606         if (!output) {
607                 TDM_ERR("'!output' failed");
608                 if (error)
609                         *error = TDM_ERROR_INVALID_PARAMETER;
610                 return NULL;
611         }
612
613         private_output = (tdm_private_client_output*)output;
614
615         private_vblank = calloc(1, sizeof *private_vblank);
616         if (!private_vblank) {
617                 TDM_ERR("alloc failed");
618                 if (error)
619                         *error = TDM_ERROR_OUT_OF_MEMORY;
620                 return NULL;
621         }
622
623         private_vblank->private_output = private_output;
624
625         private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output);
626         if (!private_vblank->vblank) {
627                 TDM_ERR("couldn't create vblank resource");
628                 free(private_vblank);
629                 if (error)
630                         *error = TDM_ERROR_OUT_OF_MEMORY;
631                 return NULL;
632         }
633
634         /* initial value */
635         private_vblank->fps = private_output->refresh;
636         private_vblank->offset = 0;
637         private_vblank->enable_fake = 0;
638
639         LIST_INITHEAD(&private_vblank->wait_list);
640         LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
641
642         wl_tdm_vblank_add_listener(private_vblank->vblank,
643                                                            &tdm_client_vblank_listener, private_vblank);
644
645         return (tdm_client_vblank*)private_vblank;
646 }
647
648 void
649 tdm_client_vblank_destroy(tdm_client_vblank *vblank)
650 {
651         tdm_private_client_vblank *private_vblank;
652         tdm_client_wait_info *w = NULL, *ww = NULL;
653
654         TDM_RETURN_IF_FAIL(vblank != NULL);
655
656         private_vblank = vblank;
657         LIST_DEL(&private_vblank->link);
658
659         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
660                 LIST_DEL(&w->link);
661                 free(w);
662         }
663
664         wl_tdm_vblank_destroy(private_vblank->vblank);
665
666         free(private_vblank);
667 }
668
669 tdm_error
670 tdm_client_vblank_set_name(tdm_client_vblank *vblank, const char *name)
671 {
672         tdm_private_client_vblank *private_vblank;
673
674         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
675
676         private_vblank = vblank;
677
678         if (!name)
679                 name = TDM_VBLANK_DEFAULT_NAME;
680
681         strncpy(private_vblank->name, name, TDM_NAME_LEN - 1);
682         private_vblank->name[TDM_NAME_LEN - 1] = '\0';
683
684         wl_tdm_vblank_set_name(private_vblank->vblank, private_vblank->name);
685
686         return TDM_ERROR_NONE;
687 }
688
689 tdm_error
690 tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync)
691 {
692         tdm_private_client_vblank *private_vblank;
693
694         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
695
696         private_vblank = vblank;
697         private_vblank->sync = sync;
698
699         return TDM_ERROR_NONE;
700 }
701
702 tdm_error
703 tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
704 {
705         tdm_private_client_vblank *private_vblank;
706
707         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
708         TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
709
710         private_vblank = vblank;
711         TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
712
713         if (private_vblank->fps == fps)
714                 return TDM_ERROR_NONE;
715         private_vblank->fps = fps;
716
717         wl_tdm_vblank_set_fps(private_vblank->vblank, fps);
718
719         return TDM_ERROR_NONE;
720 }
721
722 tdm_error
723 tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
724 {
725         tdm_private_client_vblank *private_vblank;
726
727         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
728
729         private_vblank = vblank;
730         TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
731
732         if (private_vblank->offset == offset_ms)
733                 return TDM_ERROR_NONE;
734         private_vblank->offset = offset_ms;
735
736         wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms);
737
738         return TDM_ERROR_NONE;
739 }
740
741 tdm_error
742 tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake)
743 {
744         tdm_private_client_vblank *private_vblank;
745
746         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
747
748         private_vblank = vblank;
749
750         if (private_vblank->enable_fake == enable_fake)
751                 return TDM_ERROR_NONE;
752         private_vblank->enable_fake = enable_fake;
753
754         wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake);
755
756         return TDM_ERROR_NONE;
757 }
758
759 tdm_error
760 tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data)
761 {
762         tdm_private_client *private_client;
763         tdm_private_client_output *private_output;
764         tdm_private_client_vblank *private_vblank;
765         tdm_client_wait_info *w;
766         struct timespec tp;
767         int ret = 0;
768
769         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
770         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
771         /* can't support "interval 0" and "getting current_msc" things because
772          * there is a socket communication between TDM client and server. It's impossible
773          * to return the current msc or sequence immediately.
774          */
775         TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
776
777         private_vblank = vblank;
778         private_output = private_vblank->private_output;
779         private_client = private_output->private_client;
780
781         if (!private_vblank->started)
782                 private_vblank->started = 1;
783
784         if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
785                 TDM_INFO("dpms off");
786                 return TDM_ERROR_DPMS_OFF;
787         }
788
789         w = calloc(1, sizeof *w);
790         if (!w) {
791                 TDM_ERR("alloc failed");
792                 return TDM_ERROR_OUT_OF_MEMORY;
793         }
794
795         w->private_vblank = private_vblank;
796         w->func = func;
797         w->user_data = user_data;
798
799         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
800
801         clock_gettime(CLOCK_MONOTONIC, &tp);
802         w->req_id = ++private_output->req_id;
803         w->req_sec = (unsigned int)tp.tv_sec;
804         w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
805         w->need_free = (private_vblank->sync) ? 0 : 1;
806
807         wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec);
808
809         TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
810
811         if (!private_vblank->sync) {
812                 wl_display_flush(private_client->display);
813                 return TDM_ERROR_NONE;
814         }
815
816         while (ret != -1 && !w->need_free)
817                 ret = wl_display_dispatch(private_client->display);
818
819         clock_gettime(CLOCK_MONOTONIC, &tp);
820         TDM_DBG("block during %d us",
821                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
822                         - (w->req_sec * 1000000 + w->req_usec));
823
824         LIST_DEL(&w->link);
825         free(w);
826
827         return TDM_ERROR_NONE;
828 }
829
830 tdm_error
831 tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence,
832                                                    tdm_client_vblank_handler func, void *user_data)
833 {
834         tdm_private_client *private_client;
835         tdm_private_client_output *private_output;
836         tdm_private_client_vblank *private_vblank;
837         tdm_client_wait_info *w;
838         struct timespec tp;
839         int ret = 0;
840
841         TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
842         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
843
844         private_vblank = vblank;
845         private_output = private_vblank->private_output;
846         private_client = private_output->private_client;
847
848         if (!private_vblank->started)
849                 private_vblank->started = 1;
850
851         if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
852                 TDM_INFO("dpms off");
853                 return TDM_ERROR_DPMS_OFF;
854         }
855
856         w = calloc(1, sizeof *w);
857         if (!w) {
858                 TDM_ERR("alloc failed");
859                 return TDM_ERROR_OUT_OF_MEMORY;
860         }
861
862         w->private_vblank = private_vblank;
863         w->func = func;
864         w->user_data = user_data;
865
866         LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
867
868         clock_gettime(CLOCK_MONOTONIC, &tp);
869         w->req_id = ++private_output->req_id;
870         w->req_sec = (unsigned int)tp.tv_sec;
871         w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
872         w->need_free = (private_vblank->sync) ? 0 : 1;
873
874         wl_tdm_vblank_wait_vblank_seq(private_vblank->vblank, sequence, w->req_id, w->req_sec, w->req_usec);
875
876         TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
877
878         if (!private_vblank->sync) {
879                 wl_display_flush(private_client->display);
880                 return TDM_ERROR_NONE;
881         }
882
883         while (ret != -1 && !w->need_free)
884                 ret = wl_display_dispatch(private_client->display);
885
886         clock_gettime(CLOCK_MONOTONIC, &tp);
887         TDM_DBG("block during %d us",
888                         ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
889                         - (w->req_sec * 1000000 + w->req_usec));
890
891         LIST_DEL(&w->link);
892         free(w);
893
894         return TDM_ERROR_NONE;
895 }