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