12896d5ac0942c5973d378a2561549afd8594626
[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 } tdm_server_voutput_info;
93
94 typedef struct _tdm_server_vblank_info {
95         struct list_head link;
96         tdm_server_output_info *output_info;
97         struct wl_resource *resource;
98
99         tdm_vblank *vblank;
100         unsigned int stamp;
101 } tdm_server_vblank_info;
102
103 typedef struct _tdm_server_wait_info {
104         struct list_head link;
105         tdm_server_vblank_info *vblank_info;
106
107         unsigned int req_id;
108         double req_time;
109 } tdm_server_wait_info;
110
111 typedef struct _tdm_server_client_info {
112         struct list_head link;
113         pid_t pid;
114         char name[TDM_NAME_LEN];
115         struct wl_resource *resource;
116 } tdm_server_client_info;
117
118 static tdm_private_server *keep_private_server;
119 static struct list_head client_list;
120
121 static void destroy_wait(tdm_server_wait_info *wait_info);
122
123 static void
124 _tdm_server_get_process_name(pid_t pid, char *name, unsigned int size)
125 {
126         char proc[TDM_NAME_LEN], pname[TDM_NAME_LEN];
127         FILE *h;
128         size_t len;
129
130         snprintf(name, size, "Unknown");
131
132         snprintf(proc, TDM_NAME_LEN, "/proc/%d/cmdline", pid);
133         h = fopen(proc, "r");
134         if (!h)
135                 return;
136
137         len = fread(pname, sizeof(char), TDM_NAME_LEN, h);
138         if (len == 0) {
139                 char *p = strncpy(pname, "NO NAME", sizeof(pname) - 1);
140                 len = p - pname;
141         }
142         pname[len - 1] = '\0';
143
144         strncpy(name, pname, size - 1);
145         name[size - 1] = '\0';
146
147         fclose(h);
148 }
149
150 /* LCOV_EXCL_START */
151 static void
152 _tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error,
153                                           unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
154 {
155         tdm_server_wait_info *found;
156         tdm_server_vblank_info *vblank_info;
157
158         TDM_RETURN_IF_FAIL(keep_private_server != NULL);
159
160         LIST_FIND_ITEM(wait_info, &keep_private_server->wait_list,
161                                    tdm_server_wait_info, link, found);
162         if (!found) {
163                 TDM_ERR("wait_info(%p) is destroyed", wait_info);
164                 return;
165         }
166
167         if (tdm_debug_module & TDM_DEBUG_VBLANK)
168                 TDM_DBG("req_id(%d) done", wait_info->req_id);
169
170         vblank_info = wait_info->vblank_info;
171
172         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
173                 TDM_TRACE_ASYNC_END((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
174
175         wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id,
176                                                         sequence, tv_sec, tv_usec, error);
177
178         destroy_wait(wait_info);
179 }
180 /* LCOV_EXCL_STOP */
181
182 /* LCOV_EXCL_START */
183 static void
184 _tdm_server_cb_vblank(tdm_vblank *vblank, tdm_error error, unsigned int sequence,
185                                           unsigned int tv_sec, unsigned int tv_usec, void *user_data)
186 {
187         _tdm_server_send_done((tdm_server_wait_info*)user_data, error, sequence, tv_sec, tv_usec);
188 }
189 /* LCOV_EXCL_STOP */
190
191 /* LCOV_EXCL_START */
192 static void
193 _tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type,
194                                                          tdm_value value, void *user_data)
195 {
196         tdm_server_output_info *output_info = user_data;
197         struct wl_client *client;
198         pid_t pid = 0;
199
200         TDM_RETURN_IF_FAIL(output_info != NULL);
201
202         client = wl_resource_get_client(output_info->resource);
203         TDM_RETURN_IF_FAIL(client != NULL);
204
205         wl_client_get_credentials(client, &pid, NULL, NULL);
206
207         if (!output_info->watch_output_changes) {
208                 TDM_DBG("skip sending the output changes: pid(%d)", (unsigned int)pid);
209                 return;
210         }
211
212         TDM_DBG("send the output changes: %d", (unsigned int)pid);
213
214         switch (type) {
215         case TDM_OUTPUT_CHANGE_DPMS:
216                 wl_tdm_output_send_dpms(output_info->resource, value.u32, TDM_ERROR_NONE);
217                 break;
218         case TDM_OUTPUT_CHANGE_CONNECTION:
219                 wl_tdm_output_send_connection(output_info->resource, value.u32, TDM_ERROR_NONE);
220                 break;
221         default:
222                 break;
223         }
224 }
225 /* LCOV_EXCL_STOP */
226
227 static void
228 destroy_wait(tdm_server_wait_info *wait_info)
229 {
230         LIST_DEL(&wait_info->link);
231         free(wait_info);
232 }
233
234 static void
235 destroy_vblank_callback(struct wl_resource *resource)
236 {
237         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
238         tdm_server_wait_info *w = NULL, *ww = NULL;
239
240         TDM_RETURN_IF_FAIL(vblank_info != NULL);
241
242         LIST_DEL(&vblank_info->link);
243
244         if (vblank_info->vblank)
245                 tdm_vblank_destroy(vblank_info->vblank);
246
247         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &keep_private_server->wait_list, link) {
248                 if (w->vblank_info == vblank_info)
249                         destroy_wait(w);
250         }
251
252         free(vblank_info);
253 }
254
255 /* LCOV_EXCL_START */
256 static void
257 _tdm_server_vblank_cb_destroy(struct wl_client *client, struct wl_resource *resource)
258 {
259         wl_resource_destroy(resource);
260 }
261 /* LCOV_EXCL_STOP */
262
263 /* LCOV_EXCL_START */
264 static void
265 _tdm_server_vblank_cb_set_name(struct wl_client *client, struct wl_resource *resource, const char *name)
266 {
267         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
268         tdm_error ret;
269
270         ret = tdm_vblank_set_name(vblank_info->vblank, name);
271         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
272 }
273 /* LCOV_EXCL_STOP */
274
275 /* LCOV_EXCL_START */
276 static void
277 _tdm_server_vblank_cb_set_fps(struct wl_client *client, struct wl_resource *resource, uint32_t fps)
278 {
279         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
280         tdm_error ret;
281
282         ret = tdm_vblank_set_fps(vblank_info->vblank, fps);
283         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
284 }
285 /* LCOV_EXCL_STOP */
286
287 /* LCOV_EXCL_START */
288 static void
289 _tdm_server_vblank_cb_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t offset)
290 {
291         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
292         tdm_error ret;
293
294         ret = tdm_vblank_set_offset(vblank_info->vblank, offset);
295         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
296 }
297 /* LCOV_EXCL_STOP */
298
299 static void
300 _tdm_server_vblank_cb_set_enable_fake(struct wl_client *client, struct wl_resource *resource, uint32_t enable_fake)
301 {
302         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
303         tdm_error ret;
304
305         ret = tdm_vblank_set_enable_fake(vblank_info->vblank, enable_fake);
306         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
307 }
308
309 static void
310 _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource *resource,
311                                                                   uint32_t interval, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
312 {
313         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
314         tdm_server_output_info *output_info = vblank_info->output_info;
315         tdm_private_server *private_server = output_info->private_server;
316         tdm_server_wait_info *wait_info;
317         unsigned int enable_fake = 0;
318         tdm_error ret;
319
320         wait_info = calloc(1, sizeof * wait_info);
321         if (!wait_info) {
322                 /* LCOV_EXCL_START */
323
324                 TDM_ERR("alloc failed");
325                 ret = TDM_ERROR_OUT_OF_MEMORY;
326                 goto wait_failed;
327
328                 /* LCOV_EXCL_STOP */
329         }
330
331         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
332         wait_info->vblank_info = vblank_info;
333         wait_info->req_id = req_id;
334         wait_info->req_time = TDM_TIME(req_sec, req_usec);
335
336         if (tdm_debug_module & TDM_DEBUG_VBLANK)
337                 TDM_DBG("req_id(%d) wait", req_id);
338
339         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
340                 TDM_TRACE_ASYNC_BEGIN((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
341
342         ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info);
343
344         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
345         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
346                 goto wait_failed;
347
348         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
349
350         return;
351 wait_failed:
352         /* LCOV_EXCL_START */
353
354         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
355         if (wait_info)
356                 destroy_wait(wait_info);
357
358         /* LCOV_EXCL_STOP */
359 }
360
361 static void
362 _tdm_server_vblank_cb_wait_vblank_seq(struct wl_client *client, struct wl_resource *resource,
363                                                                           uint32_t sequence, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
364 {
365         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
366         tdm_server_output_info *output_info = vblank_info->output_info;
367         tdm_private_server *private_server = output_info->private_server;
368         tdm_server_wait_info *wait_info;
369         unsigned int enable_fake = 0;
370         tdm_error ret;
371
372         wait_info = calloc(1, sizeof * wait_info);
373         if (!wait_info) {
374                 /* LCOV_EXCL_START */
375
376                 TDM_ERR("alloc failed");
377                 ret = TDM_ERROR_OUT_OF_MEMORY;
378                 goto wait_failed;
379
380                 /* LCOV_EXCL_STOP */
381         }
382
383         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
384         wait_info->vblank_info = vblank_info;
385         wait_info->req_id = req_id;
386         wait_info->req_time = TDM_TIME(req_sec, req_usec);
387
388         if (tdm_debug_module & TDM_DEBUG_VBLANK)
389                 TDM_DBG("req_id(%d) wait", req_id);
390
391         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
392                 TDM_TRACE_ASYNC_BEGIN((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
393
394         ret = tdm_vblank_wait_seq(vblank_info->vblank, req_sec, req_usec, sequence, _tdm_server_cb_vblank, wait_info);
395
396         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
397         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
398                 goto wait_failed;
399
400         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
401
402         return;
403 wait_failed:
404         /* LCOV_EXCL_START */
405
406         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
407         if (wait_info)
408                 destroy_wait(wait_info);
409
410         /* LCOV_EXCL_STOP */
411 }
412
413 static const struct wl_tdm_vblank_interface tdm_vblank_implementation = {
414         _tdm_server_vblank_cb_destroy,
415         _tdm_server_vblank_cb_set_name,
416         _tdm_server_vblank_cb_set_fps,
417         _tdm_server_vblank_cb_set_offset,
418         _tdm_server_vblank_cb_set_enable_fake,
419         _tdm_server_vblank_cb_wait_vblank,
420         _tdm_server_vblank_cb_wait_vblank_seq,
421 };
422
423 /* LCOV_EXCL_START */
424 static void
425 _tdm_server_output_cb_destroy(struct wl_client *client, struct wl_resource *resource)
426 {
427         wl_resource_destroy(resource);
428 }
429 /* LCOV_EXCL_STOP */
430
431 static void
432 _tdm_server_output_cb_create_vblank(struct wl_client *client, struct wl_resource *resource, uint32_t id)
433 {
434         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
435         tdm_private_server *private_server = output_info->private_server;
436         tdm_private_loop *private_loop = private_server->private_loop;
437         struct wl_resource *vblank_resource;
438         tdm_vblank *vblank;
439         tdm_server_vblank_info *vblank_info;
440
441         vblank_resource =
442                 wl_resource_create(client, &wl_tdm_vblank_interface,
443                                                    wl_resource_get_version(resource), id);
444         if (!vblank_resource) {
445                 /* LCOV_EXCL_START */
446
447                 wl_resource_post_no_memory(resource);
448                 TDM_ERR("wl_resource_create failed");
449                 return;
450
451                 /* LCOV_EXCL_STOP */
452         }
453
454         vblank = tdm_vblank_create(private_loop->dpy, output_info->output, NULL);
455         if (!vblank) {
456                 /* LCOV_EXCL_START */
457
458                 wl_resource_post_no_memory(resource);
459                 wl_resource_destroy(vblank_resource);
460                 TDM_ERR("tdm_vblank_create failed");
461                 return;
462
463                 /* LCOV_EXCL_STOP */
464         }
465
466         vblank_info = calloc(1, sizeof * vblank_info);
467         if (!vblank_info) {
468                 /* LCOV_EXCL_START */
469
470                 wl_resource_post_no_memory(resource);
471                 wl_resource_destroy(vblank_resource);
472                 tdm_vblank_destroy(vblank);
473                 TDM_ERR("alloc failed");
474                 return;
475
476                 /* LCOV_EXCL_STOP */
477         }
478
479         LIST_ADDTAIL(&vblank_info->link, &output_info->vblank_list);
480         vblank_info->output_info = output_info;
481         vblank_info->resource = vblank_resource;
482         vblank_info->vblank = vblank;
483         vblank_info->stamp = (unsigned int)tdm_vblank_get_stamp(vblank);
484
485         tdm_vblank_set_resource(vblank, vblank_resource);
486
487         wl_resource_set_implementation(vblank_resource, &tdm_vblank_implementation,
488                                                                    vblank_info, destroy_vblank_callback);
489
490         wl_tdm_vblank_send_stamp(vblank_info->resource, vblank_info->stamp);
491
492         if (tdm_ttrace_module & TDM_TTRACE_CLIENT_VBLANK) {
493                 tdm_output *output = tdm_display_get_output(private_loop->dpy, tdm_ttrace_output, NULL);
494                 if (output == output_info->output)
495                         wl_tdm_vblank_send_ttrace(vblank_info->resource, 1);
496         }
497
498         return;
499 }
500
501 /* LCOV_EXCL_START */
502 static void
503 _tdm_server_output_cb_watch_output_changes(struct wl_client *client, struct wl_resource *resource, unsigned int enable)
504 {
505         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
506
507         TDM_RETURN_IF_FAIL(output_info != NULL);
508
509         output_info->watch_output_changes = enable;
510 }
511 /* LCOV_EXCL_STOP */
512
513 static void
514 _tdm_server_output_cb_get_connection(struct wl_client *client, struct wl_resource *resource)
515 {
516         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
517         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
518         tdm_error ret;
519
520         TDM_RETURN_IF_FAIL(output_info != NULL);
521
522         ret = tdm_output_get_conn_status(output_info->output, &status);
523         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
524
525         wl_tdm_output_send_connection(output_info->resource, status, ret);
526
527         return;
528
529 failed:
530         wl_tdm_output_send_connection(output_info->resource, TDM_OUTPUT_CONN_STATUS_DISCONNECTED, ret);
531 }
532
533 static void
534 _tdm_server_output_cb_get_mode(struct wl_client *client, struct wl_resource *resource)
535 {
536         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
537         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
538         tdm_error ret;
539
540         TDM_RETURN_IF_FAIL(output_info != NULL);
541
542         ret = tdm_output_get_conn_status(output_info->output, &status);
543         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
544
545         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
546                 const tdm_output_mode *mode = NULL;
547                 unsigned int hdisplay, vdisplay, vrefresh;
548
549                 ret = tdm_output_get_mode(output_info->output, &mode);
550                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
551
552                 hdisplay = (mode) ? mode->hdisplay : 0;
553                 vdisplay = (mode) ? mode->vdisplay : 0;
554                 vrefresh = (mode) ? mode->vrefresh : 0;
555
556                 wl_tdm_output_send_mode(output_info->resource, hdisplay, vdisplay, vrefresh, ret);
557         } else {
558                 wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, TDM_ERROR_OUTPUT_DISCONNECTED);
559         }
560
561         return;
562 failed:
563         wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, ret);
564 }
565
566 static void
567 _tdm_server_output_cb_get_dpms(struct wl_client *client, struct wl_resource *resource)
568 {
569         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
570         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
571         tdm_error ret;
572
573         TDM_RETURN_IF_FAIL(output_info != NULL);
574
575         ret = tdm_output_get_conn_status(output_info->output, &status);
576         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
577
578         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
579                 tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_OFF;
580
581                 ret = tdm_output_get_dpms(output_info->output, &dpms_value);
582                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
583
584                 wl_tdm_output_send_dpms(output_info->resource, dpms_value, ret);
585         } else {
586                 wl_tdm_output_send_dpms(output_info->resource, TDM_OUTPUT_DPMS_OFF, TDM_ERROR_OUTPUT_DISCONNECTED);
587         }
588
589         return;
590 failed:
591         wl_tdm_output_send_dpms(output_info->resource, TDM_OUTPUT_DPMS_OFF, ret);
592 }
593
594 static const struct wl_tdm_output_interface tdm_output_implementation = {
595         _tdm_server_output_cb_destroy,
596         _tdm_server_output_cb_create_vblank,
597         _tdm_server_output_cb_watch_output_changes,
598         _tdm_server_output_cb_get_connection,
599         _tdm_server_output_cb_get_mode,
600         _tdm_server_output_cb_get_dpms,
601 };
602
603 static void
604 destroy_output_callback(struct wl_resource *resource)
605 {
606         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
607         tdm_server_vblank_info *v = NULL, *vv = NULL;
608
609         TDM_RETURN_IF_FAIL(output_info != NULL);
610
611         LIST_DEL(&output_info->link);
612
613         tdm_output_remove_change_handler(output_info->output,
614                                                                          _tdm_server_cb_output_change, output_info);
615
616         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &output_info->vblank_list, link) {
617                 wl_resource_destroy(v->resource);
618         }
619
620         free(output_info);
621 }
622
623 static void
624 _tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resource,
625                                                          const char *name, uint32_t id)
626 {
627         tdm_private_server *private_server = wl_resource_get_user_data(resource);
628         tdm_server_output_info *output_info;
629         struct wl_resource *output_resource = NULL;
630         tdm_output *output;
631         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
632         tdm_error ret;
633
634         output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL);
635         if (!output) {
636                 /* LCOV_EXCL_START */
637
638                 TDM_ERR("There is no '%s' output", name);
639                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
640                                                            "There is no '%s' output", name);
641                 return;
642
643                 /* LCOV_EXCL_STOP */
644         }
645
646         output_resource =
647                 wl_resource_create(client, &wl_tdm_output_interface,
648                                                    wl_resource_get_version(resource), id);
649         if (!output_resource) {
650                 /* LCOV_EXCL_START */
651
652                 wl_resource_post_no_memory(resource);
653                 TDM_ERR("wl_resource_create failed");
654                 return;
655
656                 /* LCOV_EXCL_STOP */
657         }
658
659         output_info = calloc(1, sizeof * output_info);
660         if (!output_info) {
661                 /* LCOV_EXCL_START */
662
663                 wl_resource_post_no_memory(resource);
664                 wl_resource_destroy(output_resource);
665                 TDM_ERR("alloc failed");
666                 return;
667
668                 /* LCOV_EXCL_STOP */
669         }
670
671         ret = tdm_output_add_change_handler(output, _tdm_server_cb_output_change, output_info);
672         if (ret != TDM_ERROR_NONE) {
673                 wl_resource_post_no_memory(resource);
674                 wl_resource_destroy(output_resource);
675                 free(output_info);
676                 TDM_ERR("tdm_output_add_change_handler failed");
677                 return;
678         }
679
680         LIST_ADDTAIL(&output_info->link, &private_server->output_list);
681         output_info->private_server = private_server;
682         output_info->resource = output_resource;
683         output_info->output = output;
684         LIST_INITHEAD(&output_info->vblank_list);
685
686         wl_resource_set_implementation(output_resource, &tdm_output_implementation,
687                                                                    output_info, destroy_output_callback);
688
689         ret = tdm_output_get_conn_status(output, &status);
690         wl_tdm_output_send_connection(output_resource, status, ret);
691
692         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
693                 tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_OFF;
694                 const tdm_output_mode *mode = NULL;
695                 unsigned int hdisplay, vdisplay, vrefresh;
696
697                 ret = tdm_output_get_mode(output, &mode);
698                 hdisplay = (mode) ? mode->hdisplay : 0;
699                 vdisplay = (mode) ? mode->vdisplay : 0;
700                 vrefresh = (mode) ? mode->vrefresh : 0;
701                 wl_tdm_output_send_mode(output_resource, hdisplay, vdisplay, vrefresh, ret);
702
703                 ret = tdm_output_get_dpms(output, &dpms_value);
704                 wl_tdm_output_send_dpms(output_resource, dpms_value, ret);
705         } else {
706                 wl_tdm_output_send_mode(output_resource, 0, 0, 0, TDM_ERROR_OUTPUT_DISCONNECTED);
707                 wl_tdm_output_send_dpms(output_resource, TDM_OUTPUT_DPMS_OFF, TDM_ERROR_OUTPUT_DISCONNECTED);
708         }
709 }
710
711 static void _tdm_voutput_cb_destroy(struct wl_client *client, struct wl_resource *resource)
712 {
713         wl_resource_destroy(resource);
714 }
715
716 static void
717 _tdm_voutput_cb_set_available_modes(struct wl_client *client,
718                                                                         struct wl_resource *resource,
719                                                                         struct wl_array *modes)
720 {
721         /* TODO */
722 }
723
724 static void
725 _tdm_voutput_cb_set_physical_size(struct wl_client *client, struct wl_resource *resource,
726                                                                   unsigned int mmwidth, unsigned int mmheight)
727 {
728         /* TODO */
729 }
730
731 static void
732 _tdm_voutput_cb_set_mode(struct wl_client *client, struct wl_resource *resource, unsigned int index)
733 {
734         /* TODO */
735 }
736
737 static void
738 _tdm_voutput_cb_connect(struct wl_client *client, struct wl_resource *resource)
739 {
740         /* TODO */
741 }
742
743 static void
744 _tdm_voutput_cb_disconnect(struct wl_client *client, struct wl_resource *resource)
745 {
746         /* TODO */
747 }
748
749 static void
750 _tdm_voutput_cb_commit_done(struct wl_client *client, struct wl_resource *resource)
751 {
752         /* TODO */
753 }
754
755 static const struct wl_tdm_voutput_interface tdm_voutput_implementation = {
756         _tdm_voutput_cb_destroy,
757         _tdm_voutput_cb_set_available_modes,
758         _tdm_voutput_cb_set_physical_size,
759         _tdm_voutput_cb_set_mode,
760         _tdm_voutput_cb_connect,
761         _tdm_voutput_cb_disconnect,
762         _tdm_voutput_cb_commit_done
763 };
764
765 void
766 tdm_voutput_cb_resource_destroy(struct wl_resource *resource)
767 {
768         tdm_server_voutput_info *voutput_info = wl_resource_get_user_data(resource);
769         tdm_voutput *voutput;
770         tdm_error ret = TDM_ERROR_NONE;
771
772         TDM_RETURN_IF_FAIL(voutput_info != NULL);
773
774         voutput = voutput_info->voutput;
775
776         if (voutput)
777                 ret = tdm_voutput_destroy(voutput);
778         if (ret != TDM_ERROR_NONE)
779                 TDM_ERR("_tdm_voutput_cb_destroy fail");
780
781         LIST_DEL(&voutput_info->link);
782
783         /* Do free your own resource */
784         free(voutput_info);
785 }
786
787 static void
788 _tdm_server_cb_create_virtual_output(struct wl_client *client, struct wl_resource *resource, const char *name, uint32_t id)
789 {
790         struct wl_resource *voutput_resource = NULL;
791         tdm_private_server *private_server = wl_resource_get_user_data(resource);
792         tdm_server_voutput_info *voutput_info;
793         tdm_voutput *voutput;
794         tdm_output *output;
795         tdm_error ret;
796
797         output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL);
798         if (output) {
799                 TDM_ERR("There is '%s' output, cannot create.", name);
800                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
801                                                            "There is '%s' output", name);
802                 return;
803         }
804
805         voutput = tdm_voutput_create(private_server->private_loop->dpy, name, &ret);
806         if (!voutput) {
807                 TDM_ERR("voutput creation fail(%s)(%d).", name, ret);
808                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_NO_MEMORY,
809                                                            "%s output creation fail", name);
810                 return;
811         }
812
813         output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL);
814         if (!output) {
815                 TDM_ERR("There is no '%s' output.", name);
816                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
817                                                            "There is '%s' output", name);
818                 return;
819         }
820
821         voutput_resource =
822                 wl_resource_create(client, &wl_tdm_voutput_interface,
823                                                    wl_resource_get_version(resource), id);
824         if (!voutput_resource) {
825                 /* LCOV_EXCL_START */
826
827                 wl_resource_post_no_memory(resource);
828                 TDM_ERR("wl_resource_create failed");
829                 return;
830
831                 /* LCOV_EXCL_STOP */
832         }
833
834         voutput_info = calloc(1, sizeof * voutput_info);
835         if (!voutput_info) {
836                 /* LCOV_EXCL_START */
837
838                 wl_resource_post_no_memory(resource);
839                 wl_resource_destroy(voutput_resource);
840                 TDM_ERR("alloc failed");
841                 return;
842
843                 /* LCOV_EXCL_STOP */
844         }
845
846         LIST_ADDTAIL(&voutput_info->link, &private_server->voutput_list);
847         voutput_info->private_server = private_server;
848         voutput_info->resource = voutput_resource;
849         voutput_info->voutput = voutput;
850         voutput_info->output = output;
851
852         wl_resource_set_implementation(voutput_resource,
853                                                                    &tdm_voutput_implementation,
854                                                                    voutput_info,
855                                                                    tdm_voutput_cb_resource_destroy);
856
857         wl_tdm_voutput_send_ack_message(voutput_resource, WL_TDM_VOUTPUT_MESSAGE_ADDED);
858 }
859
860 /* LCOV_EXCL_START */
861 static void
862 _tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, const char *options)
863 {
864         tdm_private_server *private_server = wl_resource_get_user_data(resource);
865         tdm_private_loop *private_loop = private_server->private_loop;
866         char message[TDM_SERVER_REPLY_MSG_LEN];
867         char *m;
868         int len = sizeof(message), size;
869         uid_t uid;
870
871         wl_client_get_credentials(client, NULL, &uid, NULL);
872
873         if (uid != 0) {
874                 snprintf(message, len, "tdm-monitor: SHOULD be a superuser.\n");
875                 TDM_ERR("%s", message);
876         } else {
877                 tdm_monitor_server_command(private_loop->dpy, options, message, &len);
878         }
879
880         size = sizeof(message) - len;
881         m = message;
882
883         wl_client_flush(client);
884
885         while (size > 0) {
886                 char buffer[TDM_DEBUG_REPLY_MSG_LEN];
887                 int copylen = TDM_MIN(size, sizeof(buffer) - 1);
888
889                 strncpy(buffer, m, copylen);
890                 m += copylen;
891                 size -= copylen;
892
893                 buffer[copylen] = '\0';
894
895                 wl_tdm_send_debug_message(resource, buffer);
896         }
897
898         wl_tdm_send_debug_done(resource);
899 }
900 /* LCOV_EXCL_STOP */
901
902 static const struct wl_tdm_interface tdm_implementation = {
903         _tdm_server_cb_debug,
904         _tdm_server_cb_create_output,
905         _tdm_server_cb_create_virtual_output
906 };
907
908 static void
909 destroy_client(struct wl_resource *resource)
910 {
911         tdm_server_client_info *c = NULL, *cc = NULL;
912         struct wl_client *client;
913
914         client = wl_resource_get_client(resource);
915         TDM_RETURN_IF_FAIL(client != NULL);
916
917         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &client_list, link) {
918                 if (c->resource == resource) {
919                         LIST_DEL(&c->link);
920                         free(c);
921                         return;
922                 }
923         }
924 }
925
926 static void
927 _tdm_server_bind(struct wl_client *client, void *data,
928                                  uint32_t version, uint32_t id)
929 {
930         struct wl_resource *resource;
931         tdm_server_client_info *cinfo;
932
933         resource = wl_resource_create(client, &wl_tdm_interface, version, id);
934         if (!resource) {
935                 /* LCOV_EXCL_START */
936
937                 wl_client_post_no_memory(client);
938                 return;
939
940                 /* LCOV_EXCL_STOP */
941         }
942
943         cinfo = calloc(1, sizeof(tdm_server_client_info));
944         if (!cinfo) {
945                 /* LCOV_EXCL_START */
946
947                 wl_client_post_no_memory(client);
948                 wl_resource_destroy(resource);
949                 return;
950
951                 /* LCOV_EXCL_STOP */
952         }
953
954         cinfo->resource = resource;
955
956         LIST_ADDTAIL(&cinfo->link, &client_list);
957         wl_client_get_credentials(client, &cinfo->pid, NULL, NULL);
958         _tdm_server_get_process_name(cinfo->pid, cinfo->name, TDM_NAME_LEN);
959
960         wl_resource_set_implementation(resource, &tdm_implementation, data, destroy_client);
961 }
962
963 INTERN tdm_error
964 tdm_server_init(tdm_private_loop *private_loop)
965 {
966         tdm_private_server *private_server;
967
968         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_OPERATION_FAILED);
969         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display, TDM_ERROR_OPERATION_FAILED);
970
971         if (private_loop->private_server)
972                 return TDM_ERROR_NONE;
973
974         if (wl_display_add_socket(private_loop->wl_display, "tdm-socket")) {
975                 /* LCOV_EXCL_START */
976
977                 TDM_ERR("createing a tdm-socket failed");
978                 return TDM_ERROR_OPERATION_FAILED;
979
980                 /* LCOV_EXCL_STOP */
981         }
982
983         private_server = calloc(1, sizeof * private_server);
984         if (!private_server) {
985                 TDM_ERR("alloc failed");
986                 return TDM_ERROR_OUT_OF_MEMORY;
987         }
988
989         LIST_INITHEAD(&private_server->output_list);
990         LIST_INITHEAD(&private_server->voutput_list);
991         LIST_INITHEAD(&private_server->wait_list);
992
993         if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1,
994                                                   private_server, _tdm_server_bind)) {
995                 /* LCOV_EXCL_START */
996
997                 TDM_ERR("creating a global resource failed");
998                 free(private_server);
999                 return TDM_ERROR_OUT_OF_MEMORY;
1000
1001                 /* LCOV_EXCL_STOP */
1002         }
1003
1004         private_server->private_loop = private_loop;
1005         private_loop->private_server = private_server;
1006         keep_private_server = private_server;
1007
1008         LIST_INITHEAD(&client_list);
1009
1010         return TDM_ERROR_NONE;
1011 }
1012
1013 INTERN void
1014 tdm_server_deinit(tdm_private_loop *private_loop)
1015 {
1016         tdm_server_output_info *o = NULL, *oo = NULL;
1017         tdm_server_voutput_info *vo = NULL, *voo = NULL;
1018         tdm_server_wait_info *w = NULL, *ww = NULL;
1019         tdm_server_client_info *c = NULL, *cc = NULL;
1020         tdm_private_server *private_server;
1021
1022         if (!private_loop->private_server)
1023                 return;
1024
1025         private_server = private_loop->private_server;
1026
1027         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_server->wait_list, link) {
1028                 destroy_wait(w);
1029         }
1030
1031         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_server->output_list, link) {
1032                 wl_resource_destroy(o->resource);
1033         }
1034
1035         LIST_FOR_EACH_ENTRY_SAFE(vo, voo, &private_server->voutput_list, link) {
1036                 wl_resource_destroy(vo->resource);
1037         }
1038
1039         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &client_list, link) {
1040                 wl_resource_destroy(c->resource);
1041         }
1042
1043         free(private_server);
1044         private_loop->private_server = NULL;
1045         keep_private_server = NULL;
1046 }
1047
1048 /* LCOV_EXCL_START */
1049 INTERN const char*
1050 tdm_server_get_client_name(pid_t pid)
1051 {
1052         tdm_server_client_info *c = NULL;
1053
1054         LIST_FOR_EACH_ENTRY(c, &client_list, link) {
1055                 if (c->pid == pid)
1056                         return (const char*)c->name;
1057         }
1058
1059         return NULL;
1060 }
1061 /* LCOV_EXCL_STOP */
1062
1063 /* LCOV_EXCL_START */
1064 INTERN tdm_error
1065 tdm_server_enable_ttrace_client_vblank(tdm_display *dpy, tdm_output *output, int enable)
1066 {
1067         tdm_private_server *private_server = keep_private_server;
1068         tdm_server_output_info *output_info = NULL;
1069
1070         if (!keep_private_server)
1071                 return TDM_ERROR_NONE;
1072
1073         LIST_FOR_EACH_ENTRY(output_info, &private_server->output_list, link) {
1074                 tdm_server_vblank_info *vblank_info = NULL;
1075
1076                 if (output && output_info->output != output)
1077                         continue;
1078
1079                 LIST_FOR_EACH_ENTRY(vblank_info, &output_info->vblank_list, link) {
1080                         wl_tdm_vblank_send_ttrace(vblank_info->resource, enable);
1081                 }
1082         }
1083
1084         return TDM_ERROR_NONE;
1085 }
1086 /* LCOV_EXCL_STOP */