tdm_server: check timeout flag and vblank_info in timeout callback
[platform/core/uifw/libtdm.git] / src / tdm_server.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 <boram1288.park@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 #define WL_HIDE_DEPRECATED
41
42 #include <tdm-server-protocol.h>
43
44 #include "tdm_private.h"
45
46 /* CAUTION:
47  * - tdm server doesn't care about thread things.
48  * - DO NOT use the TDM internal functions here.
49  *     However, the internal function which does lock/unlock the mutex of
50  *     private_display in itself can be called.
51  * - DO NOT use the tdm_private_display structure here.
52  * - The callback function things can be called in main thread.
53  */
54
55 struct _tdm_private_server {
56         tdm_private_loop *private_loop;
57         struct list_head output_list;
58         struct list_head voutput_list;
59         struct list_head wait_list;
60 };
61
62 typedef struct _tdm_server_output_info {
63         struct list_head link;
64         tdm_private_server *private_server;
65         struct wl_resource *resource;
66         tdm_output *output;
67         struct list_head vblank_list;
68         unsigned int watch_output_changes;
69 } tdm_server_output_info;
70
71 typedef struct _tdm_server_voutput_buffer {
72         struct list_head link;
73         struct wl_resource *wl_buffer;
74         tbm_surface_h buffer;
75 } tdm_server_voutput_buffer;
76
77 typedef struct _tdm_server_voutput_info {
78         struct list_head link;
79         tdm_private_server *private_server;
80         struct wl_resource *resource;
81         tdm_voutput *voutput;
82         tdm_output *output;
83
84         tdm_output_conn_status status;
85         struct {
86                 int count;
87                 tdm_output_mode *modes;
88         } available_modes;
89
90         unsigned int mmwidth;
91         unsigned int mmheight;
92
93         struct list_head buffer_list;
94         tdm_server_voutput_buffer *attach_buffer;
95         int committing;
96 } tdm_server_voutput_info;
97
98 typedef struct _tdm_server_vblank_info {
99         struct list_head link;
100         tdm_server_output_info *output_info;
101         struct wl_resource *resource;
102
103         tdm_vblank *vblank;
104         unsigned int stamp;
105
106         /* for timeout */
107         tdm_event_loop_source *vblank_timeout_timer;
108 } tdm_server_vblank_info;
109
110 typedef struct _tdm_server_wait_info {
111         struct list_head link;
112         tdm_server_vblank_info *vblank_info;
113
114         unsigned int req_id;
115         double req_time;
116
117         unsigned int timeout;
118 } tdm_server_wait_info;
119
120 typedef struct _tdm_server_client_info {
121         struct list_head link;
122         pid_t pid;
123         char name[TDM_NAME_LEN];
124         struct wl_resource *resource;
125 } tdm_server_client_info;
126
127 static tdm_private_server *keep_private_server;
128 static struct list_head client_list;
129
130 static void destroy_wait(tdm_server_wait_info *wait_info);
131 static void _tdm_server_vblank_timeout_update(tdm_server_vblank_info *vblank_info, int ms_delay);
132
133 static void
134 _tdm_server_get_process_name(pid_t pid, char *name, unsigned int size)
135 {
136         char proc[TDM_NAME_LEN], pname[TDM_NAME_LEN];
137         FILE *h;
138         size_t len;
139
140         snprintf(name, size, "Unknown");
141
142         snprintf(proc, TDM_NAME_LEN, "/proc/%d/cmdline", pid);
143         h = fopen(proc, "r");
144         if (!h)
145                 return;
146
147         len = fread(pname, sizeof(char), TDM_NAME_LEN, h);
148         if (len == 0) {
149                 char *p = strncpy(pname, "NO NAME", sizeof(pname) - 1);
150                 len = p - pname;
151         }
152         pname[len - 1] = '\0';
153
154         strncpy(name, pname, size - 1);
155         name[size - 1] = '\0';
156
157         fclose(h);
158 }
159
160 /* LCOV_EXCL_START */
161 static void
162 _tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error,
163                                           unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
164 {
165         tdm_server_wait_info *found;
166         tdm_server_vblank_info *vblank_info;
167         tdm_server_output_info *output_info;
168         tdm_private_server *private_server;
169         tdm_private_loop *private_loop;
170
171         TDM_RETURN_IF_FAIL(keep_private_server != NULL);
172
173         LIST_FIND_ITEM(wait_info, &keep_private_server->wait_list,
174                                    tdm_server_wait_info, link, found);
175         if (!found) {
176                 TDM_ERR("wait_info(%p) is destroyed", wait_info);
177                 return;
178         }
179
180         if (tdm_debug_module & TDM_DEBUG_VBLANK)
181                 TDM_DBG("req_id(%d) done", wait_info->req_id);
182
183         vblank_info = wait_info->vblank_info;
184         output_info = vblank_info->output_info;
185         private_server = output_info->private_server;
186         private_loop = private_server->private_loop;
187
188         tdm_display_lock(private_loop->dpy);
189         _tdm_server_vblank_timeout_update(vblank_info, 0);
190         tdm_display_unlock(private_loop->dpy);
191
192         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
193                 TDM_TRACE_ASYNC_END((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
194
195         if (!wait_info->timeout)
196                 wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id,
197                                                                 sequence, tv_sec, tv_usec, error);
198
199         destroy_wait(wait_info);
200 }
201 /* LCOV_EXCL_STOP */
202
203 /* LCOV_EXCL_START */
204 static tdm_error
205 _tdm_server_timeout_timer_cb(void *user_data)
206 {
207         tdm_server_vblank_info *vblank_info = user_data;
208         tdm_server_wait_info *wait_info = NULL;
209         double curr;
210         unsigned int tv_sec;
211         unsigned int tv_usec;
212
213         TDM_RETURN_VAL_IF_FAIL(vblank_info != NULL, TDM_ERROR_OPERATION_FAILED);
214         TDM_RETURN_VAL_IF_FAIL(keep_private_server != NULL, TDM_ERROR_OPERATION_FAILED);
215
216         curr = tdm_helper_get_time();
217         tv_sec = TDM_TIME_SEC(curr);
218         tv_usec = TDM_TIME_USEC(curr);
219
220         LIST_FOR_EACH_ENTRY(wait_info, &keep_private_server->wait_list, link) {
221                 if (wait_info->timeout) continue;
222                 if (vblank_info != wait_info->vblank_info) continue;
223
224                 wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id,
225                                                                 0, tv_sec, tv_usec, TDM_ERROR_TIMEOUT);
226                 TDM_ERR("tdm_server_vblank(%p) req_id(%d) timeout force send vblank", vblank_info, wait_info->req_id);
227                 wait_info->timeout = 1;
228         }
229
230         return TDM_ERROR_NONE;
231 }
232
233 static void
234 _tdm_server_vblank_timeout_update(tdm_server_vblank_info *vblank_info, int ms_delay)
235 {
236         tdm_server_output_info *output_info = vblank_info->output_info;
237         tdm_private_server *private_server = output_info->private_server;
238         tdm_private_loop *private_loop = private_server->private_loop;
239         tdm_error ret;
240
241         if (!vblank_info->vblank_timeout_timer) {
242                 vblank_info->vblank_timeout_timer =
243                         tdm_event_loop_add_timer_handler(private_loop->dpy,
244                                                                                          _tdm_server_timeout_timer_cb,
245                                                                                          vblank_info,
246                                                                                          &ret);
247                 if (!vblank_info->vblank_timeout_timer) {
248                         TDM_ERR("tdm_server_vblank(%p) couldn't add timer", vblank_info);
249                         return;
250                 }
251
252                 if (tdm_debug_module & TDM_DEBUG_VBLANK)
253                         TDM_INFO("tdm_server_vblank(%p) create vblank timeout timer", vblank_info);
254         }
255
256         ret = tdm_event_loop_source_timer_update(vblank_info->vblank_timeout_timer, ms_delay);
257         if (ret != TDM_ERROR_NONE) {
258                 TDM_ERR("tdm_server_vblank(%p) couldn't update timer", vblank_info);
259                 return;
260         }
261 }
262
263 static void
264 _tdm_server_cb_vblank(tdm_vblank *vblank, tdm_error error, unsigned int sequence,
265                                           unsigned int tv_sec, unsigned int tv_usec, void *user_data)
266 {
267         _tdm_server_send_done((tdm_server_wait_info*)user_data, error, sequence, tv_sec, tv_usec);
268 }
269 /* LCOV_EXCL_STOP */
270
271 /* LCOV_EXCL_START */
272 static void
273 _tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type,
274                                                          tdm_value value, void *user_data)
275 {
276         tdm_server_output_info *output_info = user_data;
277         struct wl_client *client;
278         pid_t pid = 0;
279         tdm_output_conn_status status;
280         tdm_error ret = TDM_ERROR_NONE;
281
282         TDM_RETURN_IF_FAIL(output_info != NULL);
283
284         client = wl_resource_get_client(output_info->resource);
285         TDM_RETURN_IF_FAIL(client != NULL);
286
287         wl_client_get_credentials(client, &pid, NULL, NULL);
288
289         if (!output_info->watch_output_changes) {
290                 TDM_DBG("skip sending the output changes: pid(%d)", (unsigned int)pid);
291                 return;
292         }
293
294         TDM_DBG("send the output changes: %d, type:%d, value:%d", (unsigned int)pid, type, value.u32);
295
296         switch (type) {
297         case TDM_OUTPUT_CHANGE_DPMS:
298                 wl_tdm_output_send_dpms(output_info->resource, value.u32, TDM_ERROR_NONE);
299                 break;
300         case TDM_OUTPUT_CHANGE_CONNECTION:
301                 status = value.u32;
302                 if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) {
303                         const tdm_output_mode *mode = NULL;
304                         unsigned int hdisplay, vdisplay, vrefresh;
305
306                         ret = tdm_output_get_mode(output_info->output, &mode);
307                         if (ret == TDM_ERROR_NONE) {
308                                 hdisplay = (mode) ? mode->hdisplay : 0;
309                                 vdisplay = (mode) ? mode->vdisplay : 0;
310                                 vrefresh = (mode) ? mode->vrefresh : 0;
311
312                                 wl_tdm_output_send_mode(output_info->resource, hdisplay, vdisplay, vrefresh, ret);
313                         } else {
314                                 wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, ret);
315                         }
316                 }
317                 wl_tdm_output_send_connection(output_info->resource, value.u32, TDM_ERROR_NONE);
318                 break;
319         default:
320                 break;
321         }
322 }
323 /* LCOV_EXCL_STOP */
324
325 static void
326 destroy_wait(tdm_server_wait_info *wait_info)
327 {
328         LIST_DEL(&wait_info->link);
329         free(wait_info);
330 }
331
332 static void
333 destroy_vblank_callback(struct wl_resource *resource)
334 {
335         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
336         tdm_server_output_info *output_info = vblank_info->output_info;
337         tdm_private_server *private_server = output_info->private_server;
338         tdm_private_loop *private_loop = private_server->private_loop;
339         tdm_server_wait_info *w = NULL, *ww = NULL;
340
341         TDM_RETURN_IF_FAIL(vblank_info != NULL);
342
343         if (vblank_info->vblank_timeout_timer) {
344                 tdm_display_lock(private_loop->dpy);
345                 tdm_event_loop_source_remove(vblank_info->vblank_timeout_timer);
346                 tdm_display_unlock(private_loop->dpy);
347         }
348
349         LIST_DEL(&vblank_info->link);
350
351         if (vblank_info->vblank)
352                 tdm_vblank_destroy(vblank_info->vblank);
353
354         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &keep_private_server->wait_list, link) {
355                 if (w->vblank_info == vblank_info)
356                         destroy_wait(w);
357         }
358
359         free(vblank_info);
360 }
361
362 /* LCOV_EXCL_START */
363 static void
364 _tdm_server_vblank_cb_destroy(struct wl_client *client, struct wl_resource *resource)
365 {
366         wl_resource_destroy(resource);
367 }
368 /* LCOV_EXCL_STOP */
369
370 /* LCOV_EXCL_START */
371 static void
372 _tdm_server_vblank_cb_set_name(struct wl_client *client, struct wl_resource *resource, const char *name)
373 {
374         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
375         tdm_error ret;
376
377         ret = tdm_vblank_set_name(vblank_info->vblank, name);
378         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
379 }
380 /* LCOV_EXCL_STOP */
381
382 /* LCOV_EXCL_START */
383 static void
384 _tdm_server_vblank_cb_set_fps(struct wl_client *client, struct wl_resource *resource, uint32_t fps)
385 {
386         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
387         tdm_error ret;
388
389         ret = tdm_vblank_set_fps(vblank_info->vblank, fps);
390         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
391 }
392 /* LCOV_EXCL_STOP */
393
394 /* LCOV_EXCL_START */
395 static void
396 _tdm_server_vblank_cb_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t offset)
397 {
398         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
399         tdm_error ret;
400
401         ret = tdm_vblank_set_offset(vblank_info->vblank, offset);
402         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
403 }
404 /* LCOV_EXCL_STOP */
405
406 static void
407 _tdm_server_vblank_cb_set_enable_fake(struct wl_client *client, struct wl_resource *resource, uint32_t enable_fake)
408 {
409         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
410         tdm_error ret;
411
412         ret = tdm_vblank_set_enable_fake(vblank_info->vblank, enable_fake);
413         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
414 }
415
416 static void
417 _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource *resource,
418                                                                   uint32_t interval, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
419 {
420         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
421         tdm_server_output_info *output_info = vblank_info->output_info;
422         tdm_private_server *private_server = output_info->private_server;
423         tdm_private_loop *private_loop = private_server->private_loop;
424         tdm_server_wait_info *wait_info;
425         unsigned int enable_fake = 0;
426         tdm_error ret;
427
428         wait_info = calloc(1, sizeof * wait_info);
429         if (!wait_info) {
430                 /* LCOV_EXCL_START */
431
432                 TDM_ERR("alloc failed");
433                 ret = TDM_ERROR_OUT_OF_MEMORY;
434                 goto wait_failed;
435
436                 /* LCOV_EXCL_STOP */
437         }
438
439         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
440         wait_info->vblank_info = vblank_info;
441         wait_info->req_id = req_id;
442         wait_info->req_time = TDM_TIME(req_sec, req_usec);
443
444         if (tdm_debug_module & TDM_DEBUG_VBLANK)
445                 TDM_DBG("req_id(%d) wait", req_id);
446
447         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
448                 TDM_TRACE_ASYNC_BEGIN((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
449
450         ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info);
451
452         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
453         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
454                 goto wait_failed;
455
456         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
457
458         tdm_display_lock(private_loop->dpy);
459         _tdm_server_vblank_timeout_update(vblank_info, 1000);
460         tdm_display_unlock(private_loop->dpy);
461
462         return;
463 wait_failed:
464         /* LCOV_EXCL_START */
465
466         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
467         if (wait_info)
468                 destroy_wait(wait_info);
469
470         /* LCOV_EXCL_STOP */
471 }
472
473 static void
474 _tdm_server_vblank_cb_wait_vblank_seq(struct wl_client *client, struct wl_resource *resource,
475                                                                           uint32_t sequence, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
476 {
477         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
478         tdm_server_output_info *output_info = vblank_info->output_info;
479         tdm_private_server *private_server = output_info->private_server;
480         tdm_server_wait_info *wait_info;
481         unsigned int enable_fake = 0;
482         tdm_error ret;
483
484         wait_info = calloc(1, sizeof * wait_info);
485         if (!wait_info) {
486                 /* LCOV_EXCL_START */
487
488                 TDM_ERR("alloc failed");
489                 ret = TDM_ERROR_OUT_OF_MEMORY;
490                 goto wait_failed;
491
492                 /* LCOV_EXCL_STOP */
493         }
494
495         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
496         wait_info->vblank_info = vblank_info;
497         wait_info->req_id = req_id;
498         wait_info->req_time = TDM_TIME(req_sec, req_usec);
499
500         if (tdm_debug_module & TDM_DEBUG_VBLANK)
501                 TDM_DBG("req_id(%d) wait", req_id);
502
503         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
504                 TDM_TRACE_ASYNC_BEGIN((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
505
506         ret = tdm_vblank_wait_seq(vblank_info->vblank, req_sec, req_usec, sequence, _tdm_server_cb_vblank, wait_info);
507
508         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
509         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
510                 goto wait_failed;
511
512         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
513
514         return;
515 wait_failed:
516         /* LCOV_EXCL_START */
517
518         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
519         if (wait_info)
520                 destroy_wait(wait_info);
521
522         /* LCOV_EXCL_STOP */
523 }
524
525 static const struct wl_tdm_vblank_interface tdm_vblank_implementation = {
526         _tdm_server_vblank_cb_destroy,
527         _tdm_server_vblank_cb_set_name,
528         _tdm_server_vblank_cb_set_fps,
529         _tdm_server_vblank_cb_set_offset,
530         _tdm_server_vblank_cb_set_enable_fake,
531         _tdm_server_vblank_cb_wait_vblank,
532         _tdm_server_vblank_cb_wait_vblank_seq,
533 };
534
535 /* LCOV_EXCL_START */
536 static void
537 _tdm_server_output_cb_destroy(struct wl_client *client, struct wl_resource *resource)
538 {
539         wl_resource_destroy(resource);
540 }
541 /* LCOV_EXCL_STOP */
542
543 static void
544 _tdm_server_output_cb_create_vblank(struct wl_client *client, struct wl_resource *resource, uint32_t id)
545 {
546         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
547         tdm_private_server *private_server = output_info->private_server;
548         tdm_private_loop *private_loop = private_server->private_loop;
549         struct wl_resource *vblank_resource;
550         tdm_vblank *vblank;
551         tdm_server_vblank_info *vblank_info;
552
553         vblank_resource =
554                 wl_resource_create(client, &wl_tdm_vblank_interface,
555                                                    wl_resource_get_version(resource), id);
556         if (!vblank_resource) {
557                 /* LCOV_EXCL_START */
558
559                 wl_resource_post_no_memory(resource);
560                 TDM_ERR("wl_resource_create failed");
561                 return;
562
563                 /* LCOV_EXCL_STOP */
564         }
565
566         vblank = tdm_vblank_create(private_loop->dpy, output_info->output, NULL);
567         if (!vblank) {
568                 /* LCOV_EXCL_START */
569
570                 wl_resource_post_no_memory(resource);
571                 wl_resource_destroy(vblank_resource);
572                 TDM_ERR("tdm_vblank_create failed");
573                 return;
574
575                 /* LCOV_EXCL_STOP */
576         }
577
578         vblank_info = calloc(1, sizeof * vblank_info);
579         if (!vblank_info) {
580                 /* LCOV_EXCL_START */
581
582                 wl_resource_post_no_memory(resource);
583                 wl_resource_destroy(vblank_resource);
584                 tdm_vblank_destroy(vblank);
585                 TDM_ERR("alloc failed");
586                 return;
587
588                 /* LCOV_EXCL_STOP */
589         }
590
591         LIST_ADDTAIL(&vblank_info->link, &output_info->vblank_list);
592         vblank_info->output_info = output_info;
593         vblank_info->resource = vblank_resource;
594         vblank_info->vblank = vblank;
595         vblank_info->stamp = (unsigned int)tdm_vblank_get_stamp(vblank);
596
597         tdm_vblank_set_resource(vblank, vblank_resource);
598
599         wl_resource_set_implementation(vblank_resource, &tdm_vblank_implementation,
600                                                                    vblank_info, destroy_vblank_callback);
601
602         wl_tdm_vblank_send_stamp(vblank_info->resource, vblank_info->stamp);
603
604         if (tdm_ttrace_module & TDM_TTRACE_CLIENT_VBLANK) {
605                 tdm_output *output = tdm_display_get_output(private_loop->dpy, tdm_ttrace_output, NULL);
606                 if (output == output_info->output)
607                         wl_tdm_vblank_send_ttrace(vblank_info->resource, 1);
608         }
609
610         return;
611 }
612
613 /* LCOV_EXCL_START */
614 static void
615 _tdm_server_output_cb_watch_output_changes(struct wl_client *client, struct wl_resource *resource, unsigned int enable)
616 {
617         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
618
619         TDM_RETURN_IF_FAIL(output_info != NULL);
620
621         output_info->watch_output_changes = enable;
622 }
623 /* LCOV_EXCL_STOP */
624
625 static void
626 _tdm_server_output_cb_get_connection(struct wl_client *client, struct wl_resource *resource)
627 {
628         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
629         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
630         tdm_error ret;
631
632         TDM_RETURN_IF_FAIL(output_info != NULL);
633
634         ret = tdm_output_get_conn_status(output_info->output, &status);
635         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
636
637         wl_tdm_output_send_connection(output_info->resource, status, ret);
638
639         return;
640
641 failed:
642         wl_tdm_output_send_connection(output_info->resource, TDM_OUTPUT_CONN_STATUS_DISCONNECTED, ret);
643 }
644
645 static void
646 _tdm_server_output_cb_get_mode(struct wl_client *client, struct wl_resource *resource)
647 {
648         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
649         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
650         tdm_error ret;
651
652         TDM_RETURN_IF_FAIL(output_info != NULL);
653
654         ret = tdm_output_get_conn_status(output_info->output, &status);
655         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
656
657         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
658                 const tdm_output_mode *mode = NULL;
659                 unsigned int hdisplay, vdisplay, vrefresh;
660
661                 ret = tdm_output_get_mode(output_info->output, &mode);
662                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
663
664                 hdisplay = (mode) ? mode->hdisplay : 0;
665                 vdisplay = (mode) ? mode->vdisplay : 0;
666                 vrefresh = (mode) ? mode->vrefresh : 0;
667
668                 wl_tdm_output_send_mode(output_info->resource, hdisplay, vdisplay, vrefresh, ret);
669         } else {
670                 wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, TDM_ERROR_OUTPUT_DISCONNECTED);
671         }
672
673         return;
674 failed:
675         wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, ret);
676 }
677
678 static void
679 _tdm_server_output_cb_get_dpms(struct wl_client *client, struct wl_resource *resource)
680 {
681         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
682         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
683         tdm_error ret;
684
685         TDM_RETURN_IF_FAIL(output_info != NULL);
686
687         ret = tdm_output_get_conn_status(output_info->output, &status);
688         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
689
690         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
691                 tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_OFF;
692
693                 ret = tdm_output_get_dpms(output_info->output, &dpms_value);
694                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
695
696                 wl_tdm_output_send_dpms(output_info->resource, dpms_value, ret);
697         } else {
698                 wl_tdm_output_send_dpms(output_info->resource, TDM_OUTPUT_DPMS_OFF, TDM_ERROR_OUTPUT_DISCONNECTED);
699         }
700
701         return;
702 failed:
703         wl_tdm_output_send_dpms(output_info->resource, TDM_OUTPUT_DPMS_OFF, ret);
704 }
705
706 static const struct wl_tdm_output_interface tdm_output_implementation = {
707         _tdm_server_output_cb_destroy,
708         _tdm_server_output_cb_create_vblank,
709         _tdm_server_output_cb_watch_output_changes,
710         _tdm_server_output_cb_get_connection,
711         _tdm_server_output_cb_get_mode,
712         _tdm_server_output_cb_get_dpms,
713 };
714
715 static void
716 destroy_output_callback(struct wl_resource *resource)
717 {
718         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
719         tdm_server_vblank_info *v = NULL, *vv = NULL;
720
721         TDM_RETURN_IF_FAIL(output_info != NULL);
722
723         LIST_DEL(&output_info->link);
724
725         tdm_output_remove_change_handler(output_info->output,
726                                                                          _tdm_server_cb_output_change, output_info);
727
728         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &output_info->vblank_list, link) {
729                 wl_resource_destroy(v->resource);
730         }
731
732         free(output_info);
733 }
734
735 static void
736 _tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resource,
737                                                          const char *name, uint32_t id)
738 {
739         tdm_private_server *private_server = wl_resource_get_user_data(resource);
740         tdm_server_output_info *output_info;
741         struct wl_resource *output_resource = NULL;
742         tdm_output *output;
743         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
744         tdm_error ret;
745
746         output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL);
747         if (!output) {
748                 /* LCOV_EXCL_START */
749
750                 TDM_ERR("There is no '%s' output", name);
751                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
752                                                            "There is no '%s' output", name);
753                 return;
754
755                 /* LCOV_EXCL_STOP */
756         }
757
758         output_resource =
759                 wl_resource_create(client, &wl_tdm_output_interface,
760                                                    wl_resource_get_version(resource), id);
761         if (!output_resource) {
762                 /* LCOV_EXCL_START */
763
764                 wl_resource_post_no_memory(resource);
765                 TDM_ERR("wl_resource_create failed");
766                 return;
767
768                 /* LCOV_EXCL_STOP */
769         }
770
771         output_info = calloc(1, sizeof * output_info);
772         if (!output_info) {
773                 /* LCOV_EXCL_START */
774
775                 wl_resource_post_no_memory(resource);
776                 wl_resource_destroy(output_resource);
777                 TDM_ERR("alloc failed");
778                 return;
779
780                 /* LCOV_EXCL_STOP */
781         }
782
783         ret = tdm_output_add_change_handler(output, _tdm_server_cb_output_change, output_info);
784         if (ret != TDM_ERROR_NONE) {
785                 wl_resource_post_no_memory(resource);
786                 wl_resource_destroy(output_resource);
787                 free(output_info);
788                 TDM_ERR("tdm_output_add_change_handler failed");
789                 return;
790         }
791
792         LIST_ADDTAIL(&output_info->link, &private_server->output_list);
793         output_info->private_server = private_server;
794         output_info->resource = output_resource;
795         output_info->output = output;
796         LIST_INITHEAD(&output_info->vblank_list);
797
798         wl_resource_set_implementation(output_resource, &tdm_output_implementation,
799                                                                    output_info, destroy_output_callback);
800
801         ret = tdm_output_get_conn_status(output, &status);
802         wl_tdm_output_send_connection(output_resource, status, ret);
803
804         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
805                 tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_OFF;
806                 const tdm_output_mode *mode = NULL;
807                 unsigned int hdisplay, vdisplay, vrefresh;
808
809                 ret = tdm_output_get_mode(output, &mode);
810                 hdisplay = (mode) ? mode->hdisplay : 0;
811                 vdisplay = (mode) ? mode->vdisplay : 0;
812                 vrefresh = (mode) ? mode->vrefresh : 0;
813                 wl_tdm_output_send_mode(output_resource, hdisplay, vdisplay, vrefresh, ret);
814
815                 ret = tdm_output_get_dpms(output, &dpms_value);
816                 wl_tdm_output_send_dpms(output_resource, dpms_value, ret);
817         } else {
818                 wl_tdm_output_send_mode(output_resource, 0, 0, 0, TDM_ERROR_OUTPUT_DISCONNECTED);
819                 wl_tdm_output_send_dpms(output_resource, TDM_OUTPUT_DPMS_OFF, TDM_ERROR_OUTPUT_DISCONNECTED);
820         }
821 }
822
823 static void _tdm_voutput_cb_destroy(struct wl_client *client, struct wl_resource *resource)
824 {
825         wl_resource_destroy(resource);
826 }
827
828 static void
829 _tdm_voutput_cb_set_available_modes(struct wl_client *client,
830                                                                         struct wl_resource *resource,
831                                                                         struct wl_array *modes)
832 {
833         tdm_server_voutput_info *voutput_info;
834         tdm_output_mode *mode;
835         int size, count = 0, i = 0;
836
837         voutput_info = wl_resource_get_user_data(resource);
838
839         voutput_info->available_modes.count = 0;
840         if (voutput_info->available_modes.modes)
841                 free(voutput_info->available_modes.modes);
842
843         wl_array_for_each(mode, modes)
844                 count++;
845         size = sizeof(tdm_output_mode);
846
847         voutput_info->available_modes.modes = malloc(count * size);
848         voutput_info->available_modes.count = count;
849
850         wl_array_for_each(mode, modes)
851                 memcpy(&voutput_info->available_modes.modes[i++], mode, size);
852 }
853
854 static void
855 _tdm_voutput_cb_set_physical_size(struct wl_client *client, struct wl_resource *resource,
856                                                                   unsigned int mmwidth, unsigned int mmheight)
857 {
858         tdm_server_voutput_info *voutput_info;
859
860         voutput_info = wl_resource_get_user_data(resource);
861
862         voutput_info->mmwidth = mmwidth;
863         voutput_info->mmheight = mmheight;
864 }
865
866 static void
867 _tdm_voutput_cb_set_mode(struct wl_client *client, struct wl_resource *resource, unsigned int index)
868 {
869         tdm_server_voutput_info *voutput_info = NULL;
870         tdm_output *output = NULL;
871         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
872         const tdm_output_mode *modes, *mode;
873
874         int count = 0;
875         tdm_error ret;
876
877         voutput_info = wl_resource_get_user_data(resource);
878         TDM_RETURN_IF_FAIL(voutput_info != NULL);
879         output = voutput_info->output;
880
881         ret = tdm_output_get_conn_status(output, &status);
882         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
883         TDM_RETURN_IF_FAIL(status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED);
884
885         ret = tdm_output_get_available_modes(output, &modes, &count);
886         TDM_RETURN_IF_FAIL(index < count);
887
888         mode = &modes[index];
889         TDM_DBG("mode set request to index:%d (%dx%d, %d)", index, mode->hdisplay, mode->vdisplay, mode->vrefresh);
890
891         ret = tdm_output_set_mode(output, mode);
892         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
893
894         tdm_output_request_mode_set(voutput_info->output, index);
895 }
896
897 static void
898 _tdm_voutput_cb_connect(struct wl_client *client, struct wl_resource *resource)
899 {
900         tdm_server_voutput_info *voutput_info;
901
902         voutput_info = wl_resource_get_user_data(resource);
903         voutput_info->status = TDM_OUTPUT_CONN_STATUS_CONNECTED;
904
905         tdm_voutput_set_physical_size(voutput_info->voutput, voutput_info->mmwidth, voutput_info->mmheight);
906         tdm_voutput_set_available_mode(voutput_info->voutput, voutput_info->available_modes.modes, voutput_info->available_modes.count);
907         tdm_voutput_connect(voutput_info->voutput);
908 }
909
910 static void
911 _tdm_voutput_cb_disconnect(struct wl_client *client, struct wl_resource *resource)
912 {
913         tdm_server_voutput_info *voutput_info;
914
915         voutput_info = wl_resource_get_user_data(resource);
916         voutput_info->status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
917
918         /* Do free resources when it's being disconnected */
919         free(voutput_info->available_modes.modes);
920         voutput_info->available_modes.modes = NULL;
921         voutput_info->available_modes.count = 0;
922         voutput_info->mmwidth = 0;
923         voutput_info->mmheight = 0;
924
925         if (voutput_info->attach_buffer) {
926                 tbm_surface_h buffer = voutput_info->attach_buffer->buffer;
927                 tbm_surface_internal_unref(buffer);
928                 voutput_info->committing = 0;
929                 voutput_info->attach_buffer = NULL;
930         }
931
932         tdm_voutput_disconnect(voutput_info->voutput);
933 }
934
935 static void
936 _tdm_voutput_cb_commit_done(struct wl_client *client, struct wl_resource *resource)
937 {
938         tdm_server_voutput_info *voutput_info;
939         tbm_surface_h buffer;
940
941         voutput_info = wl_resource_get_user_data(resource);
942         if (voutput_info->status != TDM_OUTPUT_CONN_STATUS_CONNECTED) {
943                 TDM_DBG("not connected.");
944                 return;
945         }
946
947         buffer = voutput_info->attach_buffer->buffer;
948         tbm_surface_internal_unref(buffer);
949         voutput_info->committing = 0;
950         voutput_info->attach_buffer = NULL;
951
952         tdm_voutput_commit_done(voutput_info->voutput);
953 }
954
955 static const struct wl_tdm_voutput_interface tdm_voutput_implementation = {
956         _tdm_voutput_cb_destroy,
957         _tdm_voutput_cb_set_available_modes,
958         _tdm_voutput_cb_set_physical_size,
959         _tdm_voutput_cb_set_mode,
960         _tdm_voutput_cb_connect,
961         _tdm_voutput_cb_disconnect,
962         _tdm_voutput_cb_commit_done
963 };
964
965 static void
966 _tdm_voutput_wl_buffer_destroy(struct wl_client *client, struct wl_resource *wl_buffer)
967 {
968         wl_resource_destroy(wl_buffer);
969 }
970
971 static const struct wl_buffer_interface _tdm_voutput_buffer_impementation = {
972         _tdm_voutput_wl_buffer_destroy
973 };
974
975 static void
976 _tdm_voutput_buffer_destory(struct wl_resource *wl_buffer)
977 {
978         tdm_server_voutput_info *voutput_info = NULL;
979         tdm_server_voutput_buffer *vb = NULL, *vbb = NULL;
980
981         voutput_info = (tdm_server_voutput_info *)wl_resource_get_user_data(wl_buffer);
982         TDM_RETURN_IF_FAIL(voutput_info != NULL);
983
984         LIST_FOR_EACH_ENTRY_SAFE(vb, vbb, &voutput_info->buffer_list, link) {
985                 if (vb->wl_buffer == wl_buffer) {
986                         tbm_surface_internal_unref(vb->buffer);
987                         wl_resource_set_user_data(wl_buffer, NULL);
988                         LIST_DEL(&vb->link);
989                         free(vb);
990                 }
991         }
992 }
993
994 struct wl_resource *
995 _tdm_voutput_create_wl_buffer(tdm_server_voutput_info *voutput_info)
996 {
997         struct wl_client *wl_client;
998         struct wl_resource *wl_buffer = NULL;
999
1000         wl_client = wl_resource_get_client(voutput_info->resource);
1001
1002         /* create a wl_buffer resource */
1003         wl_buffer = wl_resource_create(wl_client, &wl_buffer_interface, 1, 0);
1004         TDM_RETURN_VAL_IF_FAIL(wl_buffer != NULL, NULL);
1005
1006         wl_resource_set_implementation(wl_buffer,
1007                                (void (**)(void)) &_tdm_voutput_buffer_impementation,
1008                                voutput_info, _tdm_voutput_buffer_destory);
1009
1010         return wl_buffer;
1011 }
1012
1013 struct wl_resource *
1014 _tdm_voutput_export_buffer(tdm_server_voutput_info *voutput_info,
1015                                                    tbm_surface_h buffer)
1016 {
1017         int bufs[TBM_SURF_PLANE_MAX] = { -1, -1, -1, -1};
1018         struct wl_resource *wl_buffer = NULL;
1019         int num_buf, is_fd = -1, i;
1020         tbm_surface_info_s info;
1021         uint32_t flags = 0;
1022         struct wl_array plane_buf_idx, plane_offset, plane_stride, plane_size;
1023         int *p;
1024
1025         TDM_RETURN_VAL_IF_FAIL(voutput_info != NULL, NULL);
1026         TDM_RETURN_VAL_IF_FAIL(buffer != NULL, NULL);
1027
1028         if (tbm_surface_get_info(buffer, &info) != TBM_SURFACE_ERROR_NONE) {
1029                 TDM_ERR("Failed to create buffer from surface");
1030                 return NULL;
1031         }
1032
1033         if (info.num_planes > 3) {
1034                 TDM_ERR("invalid num_planes(%d)", info.num_planes);
1035                 return NULL;
1036         }
1037
1038         num_buf = tbm_surface_internal_get_num_bos(buffer);
1039         if (num_buf == 0) {
1040                 TDM_ERR("surface doesn't have any bo.");
1041                 return NULL;
1042         }
1043
1044         for (i = 0; i < num_buf; i++) {
1045                 tbm_bo bo = tbm_surface_internal_get_bo(buffer, i);
1046                 if (bo == NULL) {
1047                         TDM_ERR("Failed to get bo from surface");
1048                         goto err;
1049                 }
1050
1051                 /* try to get fd first */
1052                 if (is_fd == -1 || is_fd == 1) {
1053                         bufs[i] = tbm_bo_export_fd(bo);
1054                         if (is_fd == -1 && bufs[i] >= 0)
1055                                 is_fd = 1;
1056                 }
1057
1058                 /* if fail to get fd, try to get name second */
1059                 if (is_fd == -1 || is_fd == 0) {
1060                         bufs[i] = tbm_bo_export(bo);
1061                         if (is_fd == -1 && bufs[i] > 0)
1062                                 is_fd = 0;
1063                 }
1064
1065                 if (is_fd == -1 ||
1066                     (is_fd == 1 && bufs[i] < 0) ||
1067                     (is_fd == 0 && bufs[i] <= 0)) {
1068                         TDM_ERR("Failed to export(is_fd:%d, bufs:%d)", is_fd, bufs[i]);
1069                         goto err;
1070                 }
1071         }
1072
1073         wl_buffer = _tdm_voutput_create_wl_buffer(voutput_info);
1074         if (!wl_buffer) {
1075                 TDM_ERR("Failed to create wl_buffer");
1076                 goto err;
1077         }
1078
1079         wl_array_init(&plane_buf_idx);
1080         wl_array_init(&plane_offset);
1081         wl_array_init(&plane_stride);
1082         wl_array_init(&plane_size);
1083
1084         for (i = 0; i < 3; i++) {
1085                 p = wl_array_add(&plane_buf_idx, sizeof(int));
1086                 *p = tbm_surface_internal_get_plane_bo_idx(buffer, i);
1087                 p = wl_array_add(&plane_offset, sizeof(int));
1088                 *p = info.planes[i].offset;
1089                 p = wl_array_add(&plane_stride, sizeof(int));
1090                 *p = info.planes[i].stride;
1091                 p = wl_array_add(&plane_size, sizeof(int));
1092                 *p = info.planes[i].size;
1093         }
1094
1095         if (is_fd == 1)
1096                 wl_tdm_voutput_send_buffer_set_with_fd(voutput_info->resource,
1097                                 wl_buffer,
1098                                 info.width, info.height, info.format, info.bpp, info.size, info.num_planes,
1099                                 &plane_buf_idx, &plane_offset, &plane_stride, &plane_size,
1100                                 flags, num_buf, bufs[0],
1101                                 (bufs[1] == -1) ? bufs[0] : bufs[1],
1102                                 (bufs[2] == -1) ? bufs[0] : bufs[2]);
1103         else
1104                 wl_tdm_voutput_send_buffer_set_with_id(voutput_info->resource,
1105                                 wl_buffer,
1106                                 info.width, info.height, info.format, info.bpp, info.size, info.num_planes,
1107                                 &plane_buf_idx, &plane_offset, &plane_stride, &plane_size,
1108                                 flags,
1109                                 num_buf, bufs[0], bufs[1], bufs[2]);
1110
1111         wl_array_release(&plane_buf_idx);
1112         wl_array_release(&plane_offset);
1113         wl_array_release(&plane_stride);
1114         wl_array_release(&plane_size);
1115
1116         for (i = 0; i < TBM_SURF_PLANE_MAX; i++) {
1117                 if (is_fd == 1 && (bufs[i] >= 0))
1118                         close(bufs[i]);
1119         }
1120
1121         return wl_buffer;
1122
1123 err:
1124         for (i = 0; i < TBM_SURF_PLANE_MAX; i++) {
1125                 if (is_fd == 1 && (bufs[i] >= 0))
1126                         close(bufs[i]);
1127         }
1128
1129         return NULL;
1130 }
1131
1132 tdm_server_voutput_buffer *
1133 _tdm_output_get_voutput_buffer(tdm_server_voutput_info *voutput_info, tbm_surface_h buffer)
1134 {
1135         tdm_server_voutput_buffer *voutput_buffer = NULL, *vb = NULL;
1136
1137         LIST_FOR_EACH_ENTRY(vb, &voutput_info->buffer_list, link) {
1138                 if (vb && vb->buffer == buffer) {
1139                         tbm_surface_internal_ref(vb->buffer);
1140                         return vb;
1141                 }
1142         }
1143
1144         tbm_surface_internal_ref(buffer);
1145         voutput_buffer = calloc(1, sizeof *voutput_buffer);
1146         if (!voutput_buffer) {
1147                 /* LCOV_EXCL_START */
1148
1149                 TDM_ERR("fail calloc");
1150                 tbm_surface_internal_unref(buffer);
1151                 return NULL;
1152
1153                 /* LCOV_EXCL_STOP */
1154         }
1155
1156         voutput_buffer->wl_buffer = _tdm_voutput_export_buffer(voutput_info, buffer);
1157         if (!voutput_buffer->wl_buffer) {
1158                 /* LCOV_EXCL_START */
1159
1160                 TDM_ERR("fail export buffer");
1161                 free(voutput_buffer);
1162                 tbm_surface_internal_unref(buffer);
1163                 return NULL;
1164
1165                 /* LCOV_EXCL_STOP */
1166         }
1167
1168         voutput_buffer->buffer = buffer;
1169         LIST_ADDTAIL(&voutput_buffer->link, &voutput_info->buffer_list);
1170
1171         return voutput_buffer;
1172 }
1173
1174 tdm_error
1175 tdm_voutput_attach_buffer(tdm_voutput *voutput, tbm_surface_h buffer)
1176 {
1177         tdm_private_server *private_server = keep_private_server;
1178         tdm_server_voutput_info *voutput_info = NULL, *vo = NULL;
1179         tdm_server_voutput_buffer *voutput_buffer = NULL;
1180
1181         TDM_RETURN_VAL_IF_FAIL(keep_private_server != NULL, TDM_ERROR_OPERATION_FAILED);
1182
1183         LIST_FOR_EACH_ENTRY(vo, &private_server->voutput_list, link) {
1184                 if (vo && vo->voutput == voutput) {
1185                         voutput_info = vo;
1186                         break;
1187                 }
1188         }
1189         TDM_RETURN_VAL_IF_FAIL(voutput_info != NULL, TDM_ERROR_INVALID_PARAMETER);
1190         TDM_RETURN_VAL_IF_FAIL(voutput_info->attach_buffer == NULL, TDM_ERROR_OPERATION_FAILED);
1191
1192         voutput_buffer = _tdm_output_get_voutput_buffer(voutput_info, buffer);
1193         TDM_RETURN_VAL_IF_FAIL(voutput_buffer != NULL, TDM_ERROR_OUT_OF_MEMORY);
1194
1195         voutput_info->attach_buffer = voutput_buffer;
1196
1197         wl_tdm_voutput_send_attach_buffer(voutput_info->resource, voutput_buffer->wl_buffer);
1198
1199         return TDM_ERROR_NONE;
1200 }
1201
1202 tdm_error
1203 tdm_voutput_commit_buffer(tdm_voutput *voutput)
1204 {
1205         tdm_private_server *private_server = keep_private_server;
1206         tdm_server_voutput_info *voutput_info = NULL, *vo = NULL;
1207
1208         TDM_RETURN_VAL_IF_FAIL(keep_private_server != NULL, TDM_ERROR_OPERATION_FAILED);
1209
1210         LIST_FOR_EACH_ENTRY(vo, &private_server->voutput_list, link) {
1211                 if (vo && vo->voutput == voutput) {
1212                         voutput_info = vo;
1213                         break;
1214                 }
1215         }
1216         TDM_RETURN_VAL_IF_FAIL(voutput_info != NULL, TDM_ERROR_INVALID_PARAMETER);
1217         TDM_RETURN_VAL_IF_FAIL(voutput_info->attach_buffer != NULL, TDM_ERROR_OPERATION_FAILED);
1218         TDM_RETURN_VAL_IF_FAIL(voutput_info->committing == 0, TDM_ERROR_OPERATION_FAILED);
1219
1220         tbm_surface_internal_ref(voutput_info->attach_buffer->buffer);
1221         voutput_info->committing = 1;
1222
1223         wl_tdm_voutput_send_commit(voutput_info->resource);
1224
1225         return TDM_ERROR_NONE;
1226 }
1227
1228 void
1229 tdm_voutput_cb_resource_destroy(struct wl_resource *resource)
1230 {
1231         tdm_server_voutput_info *voutput_info = wl_resource_get_user_data(resource);
1232         tdm_server_voutput_buffer *vb = NULL;
1233         tdm_voutput *voutput;
1234         tdm_error ret = TDM_ERROR_NONE;
1235
1236         TDM_RETURN_IF_FAIL(voutput_info != NULL);
1237
1238         voutput = voutput_info->voutput;
1239
1240         if (voutput)
1241                 ret = tdm_voutput_destroy(voutput);
1242         if (ret != TDM_ERROR_NONE)
1243                 TDM_ERR("_tdm_voutput_cb_destroy fail");
1244
1245         if (voutput_info->attach_buffer) {
1246                 tbm_surface_h buffer = voutput_info->attach_buffer->buffer;
1247                 tbm_surface_internal_unref(buffer);
1248                 voutput_info->committing = 0;
1249                 voutput_info->attach_buffer = NULL;
1250         }
1251
1252         LIST_FOR_EACH_ENTRY(vb, &voutput_info->buffer_list, link) {
1253                 if (vb->wl_buffer)
1254                         wl_resource_destroy(vb->wl_buffer);
1255         }
1256
1257         LIST_DEL(&voutput_info->link);
1258
1259         /* Do free your own resource */
1260         free(voutput_info);
1261 }
1262
1263 static void
1264 _tdm_server_cb_create_virtual_output(struct wl_client *client, struct wl_resource *resource, const char *name, uint32_t id)
1265 {
1266         struct wl_resource *voutput_resource = NULL;
1267         tdm_private_server *private_server = wl_resource_get_user_data(resource);
1268         tdm_server_voutput_info *voutput_info;
1269         tdm_voutput *voutput;
1270         tdm_output *output;
1271         tdm_error ret;
1272
1273         output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL);
1274         if (output) {
1275                 TDM_ERR("There is '%s' output, cannot create.", name);
1276                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
1277                                                            "There is '%s' output", name);
1278                 return;
1279         }
1280
1281         voutput = tdm_voutput_create(private_server->private_loop->dpy, name, &ret);
1282         if (!voutput) {
1283                 TDM_ERR("voutput creation fail(%s)(%d).", name, ret);
1284                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_NO_MEMORY,
1285                                                            "%s output creation fail", name);
1286                 return;
1287         }
1288
1289         output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL);
1290         if (!output) {
1291                 TDM_ERR("There is no '%s' output.", name);
1292                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
1293                                                            "There is '%s' output", name);
1294                 return;
1295         }
1296
1297         voutput_resource =
1298                 wl_resource_create(client, &wl_tdm_voutput_interface,
1299                                                    wl_resource_get_version(resource), id);
1300         if (!voutput_resource) {
1301                 /* LCOV_EXCL_START */
1302
1303                 wl_resource_post_no_memory(resource);
1304                 TDM_ERR("wl_resource_create failed");
1305                 return;
1306
1307                 /* LCOV_EXCL_STOP */
1308         }
1309
1310         voutput_info = calloc(1, sizeof * voutput_info);
1311         if (!voutput_info) {
1312                 /* LCOV_EXCL_START */
1313
1314                 wl_resource_post_no_memory(resource);
1315                 wl_resource_destroy(voutput_resource);
1316                 TDM_ERR("alloc failed");
1317                 return;
1318
1319                 /* LCOV_EXCL_STOP */
1320         }
1321
1322         LIST_ADDTAIL(&voutput_info->link, &private_server->voutput_list);
1323         voutput_info->private_server = private_server;
1324         voutput_info->resource = voutput_resource;
1325         voutput_info->voutput = voutput;
1326         voutput_info->output = output;
1327         LIST_INITHEAD(&voutput_info->buffer_list);
1328
1329         wl_resource_set_implementation(voutput_resource,
1330                                                                    &tdm_voutput_implementation,
1331                                                                    voutput_info,
1332                                                                    tdm_voutput_cb_resource_destroy);
1333
1334         wl_tdm_voutput_send_ack_message(voutput_resource, WL_TDM_VOUTPUT_MESSAGE_ADDED);
1335 }
1336
1337 /* LCOV_EXCL_START */
1338 static void
1339 _tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, const char *options)
1340 {
1341         tdm_private_server *private_server = wl_resource_get_user_data(resource);
1342         tdm_private_loop *private_loop = private_server->private_loop;
1343         char message[TDM_SERVER_REPLY_MSG_LEN];
1344         char *m;
1345         int len = sizeof(message), size;
1346         uid_t uid;
1347
1348         wl_client_get_credentials(client, NULL, &uid, NULL);
1349
1350         if (uid != 0) {
1351                 snprintf(message, len, "tdm-monitor: SHOULD be a superuser.\n");
1352                 TDM_ERR("%s", message);
1353         } else {
1354                 tdm_monitor_server_command(private_loop->dpy, options, message, &len);
1355         }
1356
1357         size = sizeof(message) - len;
1358         m = message;
1359
1360         wl_client_flush(client);
1361
1362         while (size > 0) {
1363                 char buffer[TDM_DEBUG_REPLY_MSG_LEN];
1364                 int copylen = TDM_MIN(size, sizeof(buffer) - 1);
1365
1366                 strncpy(buffer, m, copylen);
1367                 m += copylen;
1368                 size -= copylen;
1369
1370                 buffer[copylen] = '\0';
1371
1372                 wl_tdm_send_debug_message(resource, buffer);
1373         }
1374
1375         wl_tdm_send_debug_done(resource);
1376 }
1377 /* LCOV_EXCL_STOP */
1378
1379 static const struct wl_tdm_interface tdm_implementation = {
1380         _tdm_server_cb_debug,
1381         _tdm_server_cb_create_output,
1382         _tdm_server_cb_create_virtual_output
1383 };
1384
1385 static void
1386 destroy_client(struct wl_resource *resource)
1387 {
1388         tdm_server_client_info *c = NULL, *cc = NULL;
1389         struct wl_client *client;
1390
1391         client = wl_resource_get_client(resource);
1392         TDM_RETURN_IF_FAIL(client != NULL);
1393
1394         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &client_list, link) {
1395                 if (c->resource == resource) {
1396                         LIST_DEL(&c->link);
1397                         free(c);
1398                         return;
1399                 }
1400         }
1401 }
1402
1403 static void
1404 _tdm_server_bind(struct wl_client *client, void *data,
1405                                  uint32_t version, uint32_t id)
1406 {
1407         struct wl_resource *resource;
1408         tdm_server_client_info *cinfo;
1409
1410         resource = wl_resource_create(client, &wl_tdm_interface, version, id);
1411         if (!resource) {
1412                 /* LCOV_EXCL_START */
1413
1414                 wl_client_post_no_memory(client);
1415                 return;
1416
1417                 /* LCOV_EXCL_STOP */
1418         }
1419
1420         cinfo = calloc(1, sizeof(tdm_server_client_info));
1421         if (!cinfo) {
1422                 /* LCOV_EXCL_START */
1423
1424                 wl_client_post_no_memory(client);
1425                 wl_resource_destroy(resource);
1426                 return;
1427
1428                 /* LCOV_EXCL_STOP */
1429         }
1430
1431         cinfo->resource = resource;
1432
1433         LIST_ADDTAIL(&cinfo->link, &client_list);
1434         wl_client_get_credentials(client, &cinfo->pid, NULL, NULL);
1435         _tdm_server_get_process_name(cinfo->pid, cinfo->name, TDM_NAME_LEN);
1436
1437         wl_resource_set_implementation(resource, &tdm_implementation, data, destroy_client);
1438 }
1439
1440 INTERN tdm_error
1441 tdm_server_init(tdm_private_loop *private_loop)
1442 {
1443         tdm_private_server *private_server;
1444
1445         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_OPERATION_FAILED);
1446         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display, TDM_ERROR_OPERATION_FAILED);
1447
1448         if (private_loop->private_server)
1449                 return TDM_ERROR_NONE;
1450
1451         if (wl_display_add_socket(private_loop->wl_display, "tdm-socket")) {
1452                 /* LCOV_EXCL_START */
1453
1454                 TDM_ERR("createing a tdm-socket failed");
1455                 return TDM_ERROR_OPERATION_FAILED;
1456
1457                 /* LCOV_EXCL_STOP */
1458         }
1459
1460         private_server = calloc(1, sizeof * private_server);
1461         if (!private_server) {
1462                 TDM_ERR("alloc failed");
1463                 return TDM_ERROR_OUT_OF_MEMORY;
1464         }
1465
1466         LIST_INITHEAD(&private_server->output_list);
1467         LIST_INITHEAD(&private_server->voutput_list);
1468         LIST_INITHEAD(&private_server->wait_list);
1469
1470         if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1,
1471                                                   private_server, _tdm_server_bind)) {
1472                 /* LCOV_EXCL_START */
1473
1474                 TDM_ERR("creating a global resource failed");
1475                 free(private_server);
1476                 return TDM_ERROR_OUT_OF_MEMORY;
1477
1478                 /* LCOV_EXCL_STOP */
1479         }
1480
1481         private_server->private_loop = private_loop;
1482         private_loop->private_server = private_server;
1483         keep_private_server = private_server;
1484
1485         LIST_INITHEAD(&client_list);
1486
1487         return TDM_ERROR_NONE;
1488 }
1489
1490 INTERN void
1491 tdm_server_deinit(tdm_private_loop *private_loop)
1492 {
1493         tdm_server_output_info *o = NULL, *oo = NULL;
1494         tdm_server_voutput_info *vo = NULL, *voo = NULL;
1495         tdm_server_wait_info *w = NULL, *ww = NULL;
1496         tdm_server_client_info *c = NULL, *cc = NULL;
1497         tdm_private_server *private_server;
1498
1499         if (!private_loop->private_server)
1500                 return;
1501
1502         private_server = private_loop->private_server;
1503
1504         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_server->wait_list, link) {
1505                 destroy_wait(w);
1506         }
1507
1508         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_server->output_list, link) {
1509                 wl_resource_destroy(o->resource);
1510         }
1511
1512         LIST_FOR_EACH_ENTRY_SAFE(vo, voo, &private_server->voutput_list, link) {
1513                 wl_resource_destroy(vo->resource);
1514         }
1515
1516         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &client_list, link) {
1517                 wl_resource_destroy(c->resource);
1518         }
1519
1520         free(private_server);
1521         private_loop->private_server = NULL;
1522         keep_private_server = NULL;
1523 }
1524
1525 /* LCOV_EXCL_START */
1526 INTERN const char*
1527 tdm_server_get_client_name(pid_t pid)
1528 {
1529         tdm_server_client_info *c = NULL;
1530
1531         LIST_FOR_EACH_ENTRY(c, &client_list, link) {
1532                 if (c->pid == pid)
1533                         return (const char*)c->name;
1534         }
1535
1536         return NULL;
1537 }
1538 /* LCOV_EXCL_STOP */
1539
1540 /* LCOV_EXCL_START */
1541 INTERN tdm_error
1542 tdm_server_enable_ttrace_client_vblank(tdm_display *dpy, tdm_output *output, int enable)
1543 {
1544         tdm_private_server *private_server = keep_private_server;
1545         tdm_server_output_info *output_info = NULL;
1546
1547         if (!keep_private_server)
1548                 return TDM_ERROR_NONE;
1549
1550         LIST_FOR_EACH_ENTRY(output_info, &private_server->output_list, link) {
1551                 tdm_server_vblank_info *vblank_info = NULL;
1552
1553                 if (output && output_info->output != output)
1554                         continue;
1555
1556                 LIST_FOR_EACH_ENTRY(vblank_info, &output_info->vblank_list, link) {
1557                         wl_tdm_vblank_send_ttrace(vblank_info->resource, enable);
1558                 }
1559         }
1560
1561         return TDM_ERROR_NONE;
1562 }
1563 /* LCOV_EXCL_STOP */