server: correct syntax 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 <sc1.lim@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <tdm-server-protocol.h>
41
42 #include "tdm_private.h"
43
44 /* CAUTION:
45  * - tdm server doesn't care about thread things.
46  * - DO NOT use the TDM internal functions here.
47  *     However, the internal function which does lock/unlock the mutex of
48  *     private_display in itself can be called.
49  * - DO NOT use the tdm_private_display structure here.
50  * - The callback function things can be called in main thread.
51  */
52
53 struct _tdm_private_server {
54         tdm_private_loop *private_loop;
55         struct list_head output_list;
56         struct list_head wait_list;
57 };
58
59 typedef struct _tdm_server_output_info {
60         struct list_head link;
61         tdm_private_server *private_server;
62         struct wl_resource *resource;
63         tdm_output *output;
64         struct list_head vblank_list;
65         unsigned int watch_output_changes;
66 } tdm_server_output_info;
67
68 typedef struct _tdm_server_vblank_info {
69         struct list_head link;
70         tdm_server_output_info *output_info;
71         struct wl_resource *resource;
72
73         tdm_vblank *vblank;
74         unsigned int stamp;
75 } tdm_server_vblank_info;
76
77 typedef struct _tdm_server_wait_info {
78         struct list_head link;
79         tdm_server_vblank_info *vblank_info;
80
81         unsigned int req_id;
82         double req_time;
83 } tdm_server_wait_info;
84
85 typedef struct _tdm_server_client_info {
86         struct list_head link;
87         pid_t pid;
88         char name[TDM_NAME_LEN];
89         struct wl_resource *resource;
90 } tdm_server_client_info;
91
92 static tdm_private_server *keep_private_server;
93 static struct list_head client_list;
94
95 static void destroy_wait(tdm_server_wait_info *wait_info);
96
97 static void
98 _tdm_server_get_process_name(pid_t pid, char *name, unsigned int size)
99 {
100         char proc[TDM_NAME_LEN], pname[TDM_NAME_LEN];
101         FILE *h;
102         size_t len;
103
104         snprintf(name, size, "Unknown");
105
106         snprintf(proc, TDM_NAME_LEN, "/proc/%d/cmdline", pid);
107         h = fopen(proc, "r");
108         if (!h)
109                 return;
110
111         len = fread(pname, sizeof(char), TDM_NAME_LEN, h);
112         if (len == 0) {
113                 char *p = strncpy(pname, "NO NAME", sizeof(pname) - 1);
114                 len = p - pname;
115         }
116         pname[len - 1] = '\0';
117
118         strncpy(name, pname, size - 1);
119         name[size - 1] = '\0';
120
121         fclose(h);
122 }
123
124 /* LCOV_EXCL_START */
125 static void
126 _tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error,
127                                           unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
128 {
129         tdm_server_wait_info *found;
130         tdm_server_vblank_info *vblank_info;
131
132         TDM_RETURN_IF_FAIL(keep_private_server != NULL);
133
134         LIST_FIND_ITEM(wait_info, &keep_private_server->wait_list,
135                                    tdm_server_wait_info, link, found);
136         if (!found) {
137                 TDM_ERR("wait_info(%p) is destroyed", wait_info);
138                 return;
139         }
140
141         if (tdm_debug_module & TDM_DEBUG_VBLANK)
142                 TDM_DBG("req_id(%d) done", wait_info->req_id);
143
144         vblank_info = wait_info->vblank_info;
145
146         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
147                 TDM_TRACE_ASYNC_END((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
148
149         wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id,
150                                                         sequence, tv_sec, tv_usec, error);
151
152         destroy_wait(wait_info);
153 }
154 /* LCOV_EXCL_STOP */
155
156 /* LCOV_EXCL_START */
157 static void
158 _tdm_server_cb_vblank(tdm_vblank *vblank, tdm_error error, unsigned int sequence,
159                                           unsigned int tv_sec, unsigned int tv_usec, void *user_data)
160 {
161         _tdm_server_send_done((tdm_server_wait_info*)user_data, error, sequence, tv_sec, tv_usec);
162 }
163 /* LCOV_EXCL_STOP */
164
165 /* LCOV_EXCL_START */
166 static void
167 _tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type,
168                                                          tdm_value value, void *user_data)
169 {
170         tdm_server_output_info *output_info = user_data;
171         struct wl_client *client;
172         pid_t pid = 0;
173
174         TDM_RETURN_IF_FAIL(output_info != NULL);
175
176         client = wl_resource_get_client(output_info->resource);
177         TDM_RETURN_IF_FAIL(client != NULL);
178
179         wl_client_get_credentials(client, &pid, NULL, NULL);
180
181         if (!output_info->watch_output_changes) {
182                 TDM_DBG("skip sending the output changes: pid(%d)", (unsigned int)pid);
183                 return;
184         }
185
186         TDM_DBG("send the output changes: %d", (unsigned int)pid);
187
188         switch (type) {
189         case TDM_OUTPUT_CHANGE_DPMS:
190                 wl_tdm_output_send_dpms(output_info->resource, value.u32, TDM_ERROR_NONE);
191                 break;
192         case TDM_OUTPUT_CHANGE_CONNECTION:
193                 wl_tdm_output_send_connection(output_info->resource, value.u32, TDM_ERROR_NONE);
194                 break;
195         default:
196                 break;
197         }
198 }
199 /* LCOV_EXCL_STOP */
200
201 static void
202 destroy_wait(tdm_server_wait_info *wait_info)
203 {
204         LIST_DEL(&wait_info->link);
205         free(wait_info);
206 }
207
208 static void
209 destroy_vblank_callback(struct wl_resource *resource)
210 {
211         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
212         tdm_server_wait_info *w = NULL, *ww = NULL;
213
214         TDM_RETURN_IF_FAIL(vblank_info != NULL);
215
216         LIST_DEL(&vblank_info->link);
217
218         if (vblank_info->vblank)
219                 tdm_vblank_destroy(vblank_info->vblank);
220
221         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &keep_private_server->wait_list, link) {
222                 if (w->vblank_info == vblank_info)
223                         destroy_wait(w);
224         }
225
226         free(vblank_info);
227 }
228
229 /* LCOV_EXCL_START */
230 static void
231 _tdm_server_vblank_cb_destroy(struct wl_client *client, struct wl_resource *resource)
232 {
233         wl_resource_destroy(resource);
234 }
235 /* LCOV_EXCL_STOP */
236
237 /* LCOV_EXCL_START */
238 static void
239 _tdm_server_vblank_cb_set_name(struct wl_client *client, struct wl_resource *resource, const char *name)
240 {
241         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
242
243         tdm_vblank_set_name(vblank_info->vblank, name);
244 }
245 /* LCOV_EXCL_STOP */
246
247 /* LCOV_EXCL_START */
248 static void
249 _tdm_server_vblank_cb_set_fps(struct wl_client *client, struct wl_resource *resource, uint32_t fps)
250 {
251         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
252
253         tdm_vblank_set_fps(vblank_info->vblank, fps);
254 }
255 /* LCOV_EXCL_STOP */
256
257 /* LCOV_EXCL_START */
258 static void
259 _tdm_server_vblank_cb_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t offset)
260 {
261         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
262
263         tdm_vblank_set_offset(vblank_info->vblank, offset);
264 }
265 /* LCOV_EXCL_STOP */
266
267 static void
268 _tdm_server_vblank_cb_set_enable_fake(struct wl_client *client, struct wl_resource *resource, uint32_t enable_fake)
269 {
270         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
271
272         tdm_vblank_set_enable_fake(vblank_info->vblank, enable_fake);
273 }
274
275 static void
276 _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource *resource,
277                                                                   uint32_t interval, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
278 {
279         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
280         tdm_server_output_info *output_info = vblank_info->output_info;
281         tdm_private_server *private_server = output_info->private_server;
282         tdm_server_wait_info *wait_info;
283         unsigned int enable_fake = 0;
284         tdm_error ret;
285
286         wait_info = calloc(1, sizeof * wait_info);
287         if (!wait_info) {
288                 /* LCOV_EXCL_START */
289
290                 TDM_ERR("alloc failed");
291                 ret = TDM_ERROR_OUT_OF_MEMORY;
292                 goto wait_failed;
293
294                 /* LCOV_EXCL_STOP */
295         }
296
297         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
298         wait_info->vblank_info = vblank_info;
299         wait_info->req_id = req_id;
300         wait_info->req_time = TDM_TIME(req_sec, req_usec);
301
302         if (tdm_debug_module & TDM_DEBUG_VBLANK)
303                 TDM_DBG("req_id(%d) wait", req_id);
304
305         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
306                 TDM_TRACE_ASYNC_BEGIN((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
307
308         ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info);
309
310         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
311         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
312                 goto wait_failed;
313
314         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
315
316         return;
317 wait_failed:
318         /* LCOV_EXCL_START */
319
320         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
321         if (wait_info)
322                 destroy_wait(wait_info);
323
324         /* LCOV_EXCL_STOP */
325 }
326
327 static void
328 _tdm_server_vblank_cb_wait_vblank_seq(struct wl_client *client, struct wl_resource *resource,
329                                                                           uint32_t sequence, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
330 {
331         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
332         tdm_server_output_info *output_info = vblank_info->output_info;
333         tdm_private_server *private_server = output_info->private_server;
334         tdm_server_wait_info *wait_info;
335         unsigned int enable_fake = 0;
336         tdm_error ret;
337
338         wait_info = calloc(1, sizeof * wait_info);
339         if (!wait_info) {
340                 /* LCOV_EXCL_START */
341
342                 TDM_ERR("alloc failed");
343                 ret = TDM_ERROR_OUT_OF_MEMORY;
344                 goto wait_failed;
345
346                 /* LCOV_EXCL_STOP */
347         }
348
349         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
350         wait_info->vblank_info = vblank_info;
351         wait_info->req_id = req_id;
352         wait_info->req_time = TDM_TIME(req_sec, req_usec);
353
354         if (tdm_debug_module & TDM_DEBUG_VBLANK)
355                 TDM_DBG("req_id(%d) wait", req_id);
356
357         if (tdm_ttrace_module & TDM_TTRACE_SERVER_VBLANK)
358                 TDM_TRACE_ASYNC_BEGIN((int)wait_info->req_time, "TDM_Server_Vblank:%u", vblank_info->stamp);
359
360         ret = tdm_vblank_wait_seq(vblank_info->vblank, req_sec, req_usec, sequence, _tdm_server_cb_vblank, wait_info);
361
362         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
363         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
364                 goto wait_failed;
365
366         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
367
368         return;
369 wait_failed:
370         /* LCOV_EXCL_START */
371
372         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
373         if (wait_info)
374                 destroy_wait(wait_info);
375
376         /* LCOV_EXCL_STOP */
377 }
378
379 static const struct wl_tdm_vblank_interface tdm_vblank_implementation = {
380         _tdm_server_vblank_cb_destroy,
381         _tdm_server_vblank_cb_set_name,
382         _tdm_server_vblank_cb_set_fps,
383         _tdm_server_vblank_cb_set_offset,
384         _tdm_server_vblank_cb_set_enable_fake,
385         _tdm_server_vblank_cb_wait_vblank,
386         _tdm_server_vblank_cb_wait_vblank_seq,
387 };
388
389 /* LCOV_EXCL_START */
390 static void
391 _tdm_server_output_cb_destroy(struct wl_client *client, struct wl_resource *resource)
392 {
393         wl_resource_destroy(resource);
394 }
395 /* LCOV_EXCL_STOP */
396
397 static void
398 _tdm_server_output_cb_create_vblank(struct wl_client *client, struct wl_resource *resource, uint32_t id)
399 {
400         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
401         tdm_private_server *private_server = output_info->private_server;
402         tdm_private_loop *private_loop = private_server->private_loop;
403         struct wl_resource *vblank_resource;
404         tdm_vblank *vblank;
405         tdm_server_vblank_info *vblank_info;
406
407         vblank_resource =
408                 wl_resource_create(client, &wl_tdm_vblank_interface,
409                                                    wl_resource_get_version(resource), id);
410         if (!vblank_resource) {
411                 /* LCOV_EXCL_START */
412
413                 wl_resource_post_no_memory(resource);
414                 TDM_ERR("wl_resource_create failed");
415                 return;
416
417                 /* LCOV_EXCL_STOP */
418         }
419
420         vblank = tdm_vblank_create(private_loop->dpy, output_info->output, NULL);
421         if (!vblank) {
422                 /* LCOV_EXCL_START */
423
424                 wl_resource_post_no_memory(resource);
425                 wl_resource_destroy(vblank_resource);
426                 TDM_ERR("tdm_vblank_create failed");
427                 return;
428
429                 /* LCOV_EXCL_STOP */
430         }
431
432         vblank_info = calloc(1, sizeof * vblank_info);
433         if (!vblank_info) {
434                 /* LCOV_EXCL_START */
435
436                 wl_resource_post_no_memory(resource);
437                 wl_resource_destroy(vblank_resource);
438                 tdm_vblank_destroy(vblank);
439                 TDM_ERR("alloc failed");
440                 return;
441
442                 /* LCOV_EXCL_STOP */
443         }
444
445         LIST_ADDTAIL(&vblank_info->link, &output_info->vblank_list);
446         vblank_info->output_info = output_info;
447         vblank_info->resource = vblank_resource;
448         vblank_info->vblank = vblank;
449         vblank_info->stamp = (unsigned int)tdm_vblank_get_stamp(vblank);
450
451         tdm_vblank_set_resource(vblank, vblank_resource);
452
453         wl_resource_set_implementation(vblank_resource, &tdm_vblank_implementation,
454                                                                    vblank_info, destroy_vblank_callback);
455
456         wl_tdm_vblank_send_stamp(vblank_info->resource, vblank_info->stamp);
457
458         if (tdm_ttrace_module & TDM_TTRACE_CLIENT_VBLANK) {
459                 tdm_output *output = tdm_display_get_output(private_loop->dpy, tdm_ttrace_output, NULL);
460                 if (output == output_info->output)
461                         wl_tdm_vblank_send_ttrace(vblank_info->resource, 1);
462         }
463
464         return;
465 }
466
467 /* LCOV_EXCL_START */
468 static void
469 _tdm_server_output_cb_watch_output_changes(struct wl_client *client, struct wl_resource *resource, unsigned int enable)
470 {
471         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
472
473         TDM_RETURN_IF_FAIL(output_info != NULL);
474
475         output_info->watch_output_changes = enable;
476 }
477 /* LCOV_EXCL_STOP */
478
479 static void
480 _tdm_server_output_cb_get_connection(struct wl_client *client, struct wl_resource *resource)
481 {
482         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
483         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
484         tdm_error ret;
485
486         TDM_RETURN_IF_FAIL(output_info != NULL);
487
488         ret = tdm_output_get_conn_status(output_info->output, &status);
489         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
490
491         wl_tdm_output_send_connection(output_info->resource, status, ret);
492
493         return;
494
495 failed:
496         wl_tdm_output_send_connection(output_info->resource, TDM_OUTPUT_CONN_STATUS_DISCONNECTED, ret);
497 }
498
499 static void
500 _tdm_server_output_cb_get_mode(struct wl_client *client, struct wl_resource *resource)
501 {
502         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
503         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
504         tdm_error ret;
505
506         TDM_RETURN_IF_FAIL(output_info != NULL);
507
508         ret = tdm_output_get_conn_status(output_info->output, &status);
509         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
510
511         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
512                 const tdm_output_mode *mode = NULL;
513                 unsigned int hdisplay, vdisplay, vrefresh;
514
515                 ret = tdm_output_get_mode(output_info->output, &mode);
516                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
517
518                 hdisplay = (mode) ? mode->hdisplay : 0;
519                 vdisplay = (mode) ? mode->vdisplay : 0;
520                 vrefresh = (mode) ? mode->vrefresh : 0;
521
522                 wl_tdm_output_send_mode(output_info->resource, hdisplay, vdisplay, vrefresh, ret);
523         } else {
524                 wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, TDM_ERROR_OUTPUT_DISCONNECTED);
525         }
526
527         return;
528 failed:
529         wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, ret);
530 }
531
532 static void
533 _tdm_server_output_cb_get_dpms(struct wl_client *client, struct wl_resource *resource)
534 {
535         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
536         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
537         tdm_error ret;
538
539         TDM_RETURN_IF_FAIL(output_info != NULL);
540
541         ret = tdm_output_get_conn_status(output_info->output, &status);
542         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
543
544         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
545                 tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_OFF;
546
547                 ret = tdm_output_get_dpms(output_info->output, &dpms_value);
548                 TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed);
549
550                 wl_tdm_output_send_dpms(output_info->resource, dpms_value, ret);
551         } else {
552                 wl_tdm_output_send_dpms(output_info->resource, TDM_OUTPUT_DPMS_OFF, TDM_ERROR_OUTPUT_DISCONNECTED);
553         }
554
555         return;
556 failed:
557         wl_tdm_output_send_dpms(output_info->resource, TDM_OUTPUT_DPMS_OFF, ret);
558 }
559
560 static const struct wl_tdm_output_interface tdm_output_implementation = {
561         _tdm_server_output_cb_destroy,
562         _tdm_server_output_cb_create_vblank,
563         _tdm_server_output_cb_watch_output_changes,
564         _tdm_server_output_cb_get_connection,
565         _tdm_server_output_cb_get_mode,
566         _tdm_server_output_cb_get_dpms,
567 };
568
569 static void
570 destroy_output_callback(struct wl_resource *resource)
571 {
572         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
573         tdm_server_vblank_info *v = NULL, *vv = NULL;
574
575         TDM_RETURN_IF_FAIL(output_info != NULL);
576
577         LIST_DEL(&output_info->link);
578
579         tdm_output_remove_change_handler(output_info->output,
580                                                                          _tdm_server_cb_output_change, output_info);
581
582         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &output_info->vblank_list, link) {
583                 wl_resource_destroy(v->resource);
584         }
585
586         free(output_info);
587 }
588
589 static void
590 _tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resource,
591                                                          const char *name, uint32_t id)
592 {
593         tdm_private_server *private_server = wl_resource_get_user_data(resource);
594         tdm_server_output_info *output_info;
595         struct wl_resource *output_resource = NULL;
596         tdm_output *output;
597         tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
598         tdm_error ret;
599
600         output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL);
601         if (!output) {
602                 /* LCOV_EXCL_START */
603
604                 TDM_ERR("There is no '%s' output", name);
605                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
606                                                            "There is no '%s' output", name);
607                 return;
608
609                 /* LCOV_EXCL_STOP */
610         }
611
612         output_resource =
613                 wl_resource_create(client, &wl_tdm_output_interface,
614                                                    wl_resource_get_version(resource), id);
615         if (!output_resource) {
616                 /* LCOV_EXCL_START */
617
618                 wl_resource_post_no_memory(resource);
619                 TDM_ERR("wl_resource_create failed");
620                 return;
621
622                 /* LCOV_EXCL_STOP */
623         }
624
625         output_info = calloc(1, sizeof * output_info);
626         if (!output_info) {
627                 /* LCOV_EXCL_START */
628
629                 wl_resource_post_no_memory(resource);
630                 wl_resource_destroy(output_resource);
631                 TDM_ERR("alloc failed");
632                 return;
633
634                 /* LCOV_EXCL_STOP */
635         }
636
637         LIST_ADDTAIL(&output_info->link, &private_server->output_list);
638         output_info->private_server = private_server;
639         output_info->resource = output_resource;
640         output_info->output = output;
641         LIST_INITHEAD(&output_info->vblank_list);
642
643         tdm_output_add_change_handler(output, _tdm_server_cb_output_change, output_info);
644
645         wl_resource_set_implementation(output_resource, &tdm_output_implementation,
646                                                                    output_info, destroy_output_callback);
647
648         ret = tdm_output_get_conn_status(output, &status);
649         wl_tdm_output_send_connection(output_resource, status, ret);
650
651         if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
652                 tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_OFF;
653                 const tdm_output_mode *mode = NULL;
654                 unsigned int hdisplay, vdisplay, vrefresh;
655
656                 ret = tdm_output_get_mode(output, &mode);
657                 hdisplay = (mode) ? mode->hdisplay : 0;
658                 vdisplay = (mode) ? mode->vdisplay : 0;
659                 vrefresh = (mode) ? mode->vrefresh : 0;
660                 wl_tdm_output_send_mode(output_resource, hdisplay, vdisplay, vrefresh, ret);
661
662                 ret = tdm_output_get_dpms(output, &dpms_value);
663                 wl_tdm_output_send_dpms(output_resource, dpms_value, ret);
664         } else {
665                 wl_tdm_output_send_mode(output_resource, 0, 0, 0, TDM_ERROR_OUTPUT_DISCONNECTED);
666                 wl_tdm_output_send_dpms(output_resource, TDM_OUTPUT_DPMS_OFF, TDM_ERROR_OUTPUT_DISCONNECTED);
667         }
668 }
669
670 /* LCOV_EXCL_START */
671 static void
672 _tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, const char *options)
673 {
674         tdm_private_server *private_server = wl_resource_get_user_data(resource);
675         tdm_private_loop *private_loop = private_server->private_loop;
676         char message[TDM_SERVER_REPLY_MSG_LEN];
677         char *m;
678         int len = sizeof(message), size;
679         uid_t uid;
680
681         wl_client_get_credentials(client, NULL, &uid, NULL);
682
683         if (uid != 0) {
684                 snprintf(message, len, "tdm-monitor: SHOULD be a superuser.\n");
685                 TDM_ERR("%s", message);
686         } else {
687                 tdm_monitor_server_command(private_loop->dpy, options, message, &len);
688         }
689
690         size = sizeof(message) - len;
691         m = message;
692
693         wl_client_flush(client);
694
695         while (size > 0) {
696                 char buffer[TDM_DEBUG_REPLY_MSG_LEN];
697                 int copylen = TDM_MIN(size, sizeof(buffer) - 1);
698
699                 strncpy(buffer, m, copylen);
700                 m += copylen;
701                 size -= copylen;
702
703                 buffer[copylen] = '\0';
704
705                 wl_tdm_send_debug_message(resource, buffer);
706         }
707
708         wl_tdm_send_debug_done(resource);
709 }
710 /* LCOV_EXCL_STOP */
711
712 static const struct wl_tdm_interface tdm_implementation = {
713         _tdm_server_cb_debug,
714         _tdm_server_cb_create_output,
715 };
716
717 static void
718 destroy_client(struct wl_resource *resource)
719 {
720         tdm_server_client_info *c = NULL, *cc = NULL;
721         struct wl_client *client;
722
723         client = wl_resource_get_client(resource);
724         TDM_RETURN_IF_FAIL(client != NULL);
725
726         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &client_list, link) {
727                 if (c->resource == resource) {
728                         LIST_DEL(&c->link);
729                         free(c);
730                         return;
731                 }
732         }
733 }
734
735 static void
736 _tdm_server_bind(struct wl_client *client, void *data,
737                                  uint32_t version, uint32_t id)
738 {
739         struct wl_resource *resource;
740         tdm_server_client_info *cinfo;
741
742         resource = wl_resource_create(client, &wl_tdm_interface, version, id);
743         if (!resource) {
744                 /* LCOV_EXCL_START */
745
746                 wl_client_post_no_memory(client);
747                 return;
748
749                 /* LCOV_EXCL_STOP */
750         }
751
752         cinfo = calloc(1, sizeof(tdm_server_client_info));
753         if (!cinfo) {
754                 /* LCOV_EXCL_START */
755
756                 wl_client_post_no_memory(client);
757                 wl_resource_destroy(resource);
758                 return;
759
760                 /* LCOV_EXCL_STOP */
761         }
762
763         cinfo->resource = resource;
764
765         LIST_ADDTAIL(&cinfo->link, &client_list);
766         wl_client_get_credentials(client, &cinfo->pid, NULL, NULL);
767         _tdm_server_get_process_name(cinfo->pid, cinfo->name, TDM_NAME_LEN);
768
769         wl_resource_set_implementation(resource, &tdm_implementation, data, destroy_client);
770 }
771
772 INTERN tdm_error
773 tdm_server_init(tdm_private_loop *private_loop)
774 {
775         tdm_private_server *private_server;
776
777         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_OPERATION_FAILED);
778         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display, TDM_ERROR_OPERATION_FAILED);
779
780         if (private_loop->private_server)
781                 return TDM_ERROR_NONE;
782
783         if (wl_display_add_socket(private_loop->wl_display, "tdm-socket")) {
784                 /* LCOV_EXCL_START */
785
786                 TDM_ERR("createing a tdm-socket failed");
787                 return TDM_ERROR_OPERATION_FAILED;
788
789                 /* LCOV_EXCL_STOP */
790         }
791
792         private_server = calloc(1, sizeof * private_server);
793         if (!private_server) {
794                 TDM_ERR("alloc failed");
795                 return TDM_ERROR_OUT_OF_MEMORY;
796         }
797
798         LIST_INITHEAD(&private_server->output_list);
799         LIST_INITHEAD(&private_server->wait_list);
800
801         if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1,
802                                                   private_server, _tdm_server_bind)) {
803                 /* LCOV_EXCL_START */
804
805                 TDM_ERR("creating a global resource failed");
806                 free(private_server);
807                 return TDM_ERROR_OUT_OF_MEMORY;
808
809                 /* LCOV_EXCL_STOP */
810         }
811
812         private_server->private_loop = private_loop;
813         private_loop->private_server = private_server;
814         keep_private_server = private_server;
815
816         LIST_INITHEAD(&client_list);
817
818         return TDM_ERROR_NONE;
819 }
820
821 INTERN void
822 tdm_server_deinit(tdm_private_loop *private_loop)
823 {
824         tdm_server_output_info *o = NULL, *oo = NULL;
825         tdm_server_wait_info *w = NULL, *ww = NULL;
826         tdm_server_client_info *c = NULL, *cc = NULL;
827         tdm_private_server *private_server;
828
829         if (!private_loop->private_server)
830                 return;
831
832         private_server = private_loop->private_server;
833
834         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_server->wait_list, link) {
835                 destroy_wait(w);
836         }
837
838         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_server->output_list, link) {
839                 wl_resource_destroy(o->resource);
840         }
841
842         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &client_list, link) {
843                 wl_resource_destroy(c->resource);
844         }
845
846         free(private_server);
847         private_loop->private_server = NULL;
848         keep_private_server = NULL;
849 }
850
851 /* LCOV_EXCL_START */
852 INTERN const char*
853 tdm_server_get_client_name(pid_t pid)
854 {
855         tdm_server_client_info *c = NULL;
856
857         LIST_FOR_EACH_ENTRY(c, &client_list, link) {
858                 if (c->pid == pid)
859                         return (const char*)c->name;
860         }
861
862         return NULL;
863 }
864 /* LCOV_EXCL_STOP */
865
866 /* LCOV_EXCL_START */
867 INTERN tdm_error
868 tdm_server_enable_ttrace_client_vblank(tdm_display *dpy, tdm_output *output, int enable)
869 {
870         tdm_private_server *private_server = keep_private_server;
871         tdm_server_output_info *output_info = NULL;
872
873         if (!keep_private_server)
874                 return TDM_ERROR_NONE;
875
876         LIST_FOR_EACH_ENTRY(output_info, &private_server->output_list, link) {
877                 tdm_server_vblank_info *vblank_info = NULL;
878
879                 if (output && output_info->output != output)
880                         continue;
881
882                 LIST_FOR_EACH_ENTRY(vblank_info, &output_info->vblank_list, link) {
883                         wl_tdm_vblank_send_ttrace(vblank_info->resource, enable);
884                 }
885         }
886
887         return TDM_ERROR_NONE;
888 }
889 /* LCOV_EXCL_STOP */