tdm: don't send output changes if client doesn't watch them
[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.h"
41 #include "tdm_private.h"
42 #include "tdm_list.h"
43 #include "tdm-server-protocol.h"
44
45 /* CAUTION:
46  * - tdm server doesn't care about thread things.
47  * - DO NOT use the TDM internal functions here.
48  *     However, the internal function which does lock/unlock the mutex of
49  *     private_display in itself can be called.
50  * - DO NOT use the tdm_private_display structure here.
51  * - The callback function things can be called in main thread.
52  */
53
54 struct _tdm_private_server {
55         tdm_private_loop *private_loop;
56         struct list_head output_list;
57         struct list_head wait_list;
58 };
59
60 typedef struct _tdm_server_output_info {
61         struct list_head link;
62         tdm_private_server *private_server;
63         struct wl_resource *resource;
64         tdm_output *output;
65         struct list_head vblank_list;
66         unsigned int watch_output_changes;
67 } tdm_server_output_info;
68
69 typedef struct _tdm_server_vblank_info {
70         struct list_head link;
71         tdm_server_output_info *output_info;
72         struct wl_resource *resource;
73
74         tdm_vblank *vblank;
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 } tdm_server_wait_info;
83
84 static tdm_private_server *keep_private_server;
85
86 static void destroy_wait(tdm_server_wait_info *wait_info);
87
88 static tdm_output*
89 _tdm_server_find_output(tdm_private_server *private_server, const char *name)
90 {
91         tdm_private_loop *private_loop = private_server->private_loop;
92         tdm_output *found = NULL;
93
94         if (!strncasecmp(name, "primary", 7) || !strncasecmp(name, "default", 7))
95                 found = tdm_display_get_output(private_loop->dpy, 0, NULL);
96
97         if (!found) {
98                 int count = 0, i;
99
100                 tdm_display_get_output_count(private_loop->dpy, &count);
101
102                 for (i = 0; i < count; i++) {
103                         tdm_output *output = tdm_display_get_output(private_loop->dpy, i, NULL);
104                         tdm_output_conn_status status;
105                         const char *model = NULL;
106                         tdm_error ret;
107
108                         ret = tdm_output_get_conn_status(output, &status);
109                         if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
110                                 continue;
111
112                         ret = tdm_output_get_model_info(output, NULL, &model, NULL);
113                         if (ret || !model)
114                                 continue;
115
116                         if (strncmp(model, name, TDM_NAME_LEN))
117                                 continue;
118
119                         found = output;
120                         break;
121                 }
122         }
123
124         return found;
125 }
126
127 static void
128 _tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error,
129                                           unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
130 {
131         tdm_server_wait_info *found;
132         tdm_server_vblank_info *vblank_info;
133
134         if (!keep_private_server)
135                 return;
136
137         LIST_FIND_ITEM(wait_info, &keep_private_server->wait_list,
138                                    tdm_server_wait_info, link, found);
139         if (!found) {
140                 TDM_DBG("wait_info(%p) is destroyed", wait_info);
141                 return;
142         }
143
144         if (tdm_debug_module & TDM_DEBUG_VBLANK)
145                 TDM_INFO("req_id(%d) done", wait_info->req_id);
146
147         TDM_TRACE_COUNT(ServerDoneVBlank, wait_info->req_id);
148
149         vblank_info = wait_info->vblank_info;
150         wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id,
151                                                         sequence, tv_sec, tv_usec, error);
152         destroy_wait(wait_info);
153 }
154
155 static void
156 _tdm_server_cb_vblank(tdm_vblank *vblank, tdm_error error, unsigned int sequence,
157                                           unsigned int tv_sec, unsigned int tv_usec, void *user_data)
158 {
159         _tdm_server_send_done((tdm_server_wait_info*)user_data, error, sequence, tv_sec, tv_usec);
160 }
161
162 static void
163 _tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type,
164                                                          tdm_value value, void *user_data)
165 {
166         tdm_server_output_info *output_info = user_data;
167         struct wl_client *client;
168         pid_t pid = 0;
169
170         TDM_RETURN_IF_FAIL(output_info != NULL);
171
172         client = wl_resource_get_client(output_info->resource);
173         TDM_RETURN_IF_FAIL(client != NULL);
174
175         wl_client_get_credentials(client, &pid, NULL, NULL);
176
177         if (!output_info->watch_output_changes) {
178                 TDM_DBG("skip sending the output changes: pid(%d)", (unsigned int)pid);
179                 return;
180         }
181
182         TDM_DBG("send the output changes: %d", (unsigned int)pid);
183
184         switch (type) {
185         case TDM_OUTPUT_CHANGE_DPMS:
186                 wl_tdm_output_send_dpms(output_info->resource, value.u32);
187                 break;
188         case TDM_OUTPUT_CHANGE_CONNECTION:
189                 wl_tdm_output_send_connection(output_info->resource, value.u32);
190                 break;
191         default:
192                 break;
193         }
194 }
195
196 static void
197 destroy_wait(tdm_server_wait_info *wait_info)
198 {
199         LIST_DEL(&wait_info->link);
200         free(wait_info);
201 }
202
203 static void
204 destroy_vblank_callback(struct wl_resource *resource)
205 {
206         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
207         tdm_server_wait_info *w = NULL, *ww = NULL;
208
209         TDM_RETURN_IF_FAIL(vblank_info != NULL);
210
211         if (vblank_info->vblank)
212                 tdm_vblank_destroy(vblank_info->vblank);
213
214         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &keep_private_server->wait_list, link) {
215                 if (w->vblank_info == vblank_info)
216                         destroy_wait(w);
217         }
218
219         LIST_DEL(&vblank_info->link);
220         free(vblank_info);
221 }
222
223 static void
224 _tdm_server_vblank_cb_destroy(struct wl_client *client, struct wl_resource *resource)
225 {
226         wl_resource_destroy(resource);
227 }
228
229 static void
230 _tdm_server_vblank_cb_set_name(struct wl_client *client, struct wl_resource *resource, const char *name)
231 {
232         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
233
234         tdm_vblank_set_name(vblank_info->vblank, name);
235 }
236
237 static void
238 _tdm_server_vblank_cb_set_fps(struct wl_client *client, struct wl_resource *resource, uint32_t fps)
239 {
240         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
241
242         tdm_vblank_set_fps(vblank_info->vblank, fps);
243 }
244
245 static void
246 _tdm_server_vblank_cb_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t offset)
247 {
248         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
249
250         tdm_vblank_set_offset(vblank_info->vblank, offset);
251 }
252
253 static void
254 _tdm_server_vblank_cb_set_enable_fake(struct wl_client *client, struct wl_resource *resource, uint32_t enable_fake)
255 {
256         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
257
258         tdm_vblank_set_enable_fake(vblank_info->vblank, enable_fake);
259 }
260
261 static void
262 _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource *resource,
263                                                                   uint32_t interval, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
264 {
265         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
266         tdm_server_output_info *output_info = vblank_info->output_info;
267         tdm_private_server *private_server = output_info->private_server;
268         tdm_server_wait_info *wait_info;
269         unsigned int enable_fake = 0;
270         tdm_error ret;
271
272         TDM_TRACE_COUNT(ServerWaitVBlank, req_id);
273
274         wait_info = calloc(1, sizeof * wait_info);
275         if (!wait_info) {
276                 TDM_ERR("alloc failed");
277                 ret = TDM_ERROR_OUT_OF_MEMORY;
278                 goto wait_failed;
279         }
280
281         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
282         wait_info->vblank_info = vblank_info;
283         wait_info->req_id = req_id;
284
285         if (tdm_debug_module & TDM_DEBUG_VBLANK)
286                 TDM_INFO("req_id(%d) wait", req_id);
287
288         ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info);
289
290         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
291         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
292                 goto wait_failed;
293
294         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
295
296         return;
297 wait_failed:
298         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
299         if (wait_info)
300                 destroy_wait(wait_info);
301 }
302
303 static void
304 _tdm_server_vblank_cb_wait_vblank_seq(struct wl_client *client, struct wl_resource *resource,
305                                                                           uint32_t sequence, uint32_t req_id, uint32_t req_sec, uint32_t req_usec)
306 {
307         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
308         tdm_server_output_info *output_info = vblank_info->output_info;
309         tdm_private_server *private_server = output_info->private_server;
310         tdm_server_wait_info *wait_info;
311         unsigned int enable_fake = 0;
312         tdm_error ret;
313
314         TDM_TRACE_COUNT(ServerWaitVBlank, req_id);
315
316         wait_info = calloc(1, sizeof * wait_info);
317         if (!wait_info) {
318                 TDM_ERR("alloc failed");
319                 ret = TDM_ERROR_OUT_OF_MEMORY;
320                 goto wait_failed;
321         }
322
323         LIST_ADDTAIL(&wait_info->link, &private_server->wait_list);
324         wait_info->vblank_info = vblank_info;
325         wait_info->req_id = req_id;
326
327         if (tdm_debug_module & TDM_DEBUG_VBLANK)
328                 TDM_INFO("req_id(%d) wait", req_id);
329
330         ret = tdm_vblank_wait_seq(vblank_info->vblank, req_sec, req_usec, sequence, _tdm_server_cb_vblank, wait_info);
331
332         tdm_vblank_get_enable_fake(vblank_info->vblank, &enable_fake);
333         if (!enable_fake && ret == TDM_ERROR_DPMS_OFF)
334                 goto wait_failed;
335
336         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
337
338         return;
339 wait_failed:
340         wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret);
341         if (wait_info)
342                 destroy_wait(wait_info);
343 }
344
345 static const struct wl_tdm_vblank_interface tdm_vblank_implementation = {
346         _tdm_server_vblank_cb_destroy,
347         _tdm_server_vblank_cb_set_name,
348         _tdm_server_vblank_cb_set_fps,
349         _tdm_server_vblank_cb_set_offset,
350         _tdm_server_vblank_cb_set_enable_fake,
351         _tdm_server_vblank_cb_wait_vblank,
352         _tdm_server_vblank_cb_wait_vblank_seq,
353 };
354
355 static void
356 _tdm_server_output_cb_destroy(struct wl_client *client, struct wl_resource *resource)
357 {
358         wl_resource_destroy(resource);
359 }
360
361 static void
362 _tdm_server_output_cb_create_vblank(struct wl_client *client, struct wl_resource *resource, uint32_t id)
363 {
364         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
365         tdm_private_server *private_server = output_info->private_server;
366         tdm_private_loop *private_loop = private_server->private_loop;
367         struct wl_resource *vblank_resource;
368         tdm_vblank *vblank;
369         tdm_server_vblank_info *vblank_info;
370
371         vblank_resource =
372                 wl_resource_create(client, &wl_tdm_vblank_interface,
373                                                    wl_resource_get_version(resource), id);
374         if (!vblank_resource) {
375                 wl_resource_post_no_memory(resource);
376                 TDM_ERR("wl_resource_create failed");
377                 return;
378         }
379
380         vblank = tdm_vblank_create(private_loop->dpy, output_info->output, NULL);
381         if (!vblank) {
382                 wl_resource_post_no_memory(resource);
383                 wl_resource_destroy(vblank_resource);
384                 TDM_ERR("tdm_vblank_create failed");
385                 return;
386         }
387
388         vblank_info = calloc(1, sizeof * vblank_info);
389         if (!vblank_info) {
390                 wl_resource_post_no_memory(resource);
391                 wl_resource_destroy(vblank_resource);
392                 tdm_vblank_destroy(vblank);
393                 TDM_ERR("alloc failed");
394                 return;
395         }
396
397         LIST_ADDTAIL(&vblank_info->link, &output_info->vblank_list);
398         vblank_info->output_info = output_info;
399         vblank_info->resource = vblank_resource;
400         vblank_info->vblank = vblank;
401
402         tdm_vblank_set_resource(vblank, vblank_resource);
403
404         wl_resource_set_implementation(vblank_resource, &tdm_vblank_implementation,
405                                                                    vblank_info, destroy_vblank_callback);
406
407         return;
408 }
409
410 static void
411 _tdm_server_output_cb_watch_output_changes(struct wl_client *client, struct wl_resource *resource, unsigned int enable)
412 {
413         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
414
415         TDM_RETURN_IF_FAIL(output_info != NULL);
416
417         output_info->watch_output_changes = enable;
418 }
419
420 static const struct wl_tdm_output_interface tdm_output_implementation = {
421         _tdm_server_output_cb_destroy,
422         _tdm_server_output_cb_create_vblank,
423         _tdm_server_output_cb_watch_output_changes,
424 };
425
426 static void
427 destroy_output_callback(struct wl_resource *resource)
428 {
429         tdm_server_output_info *output_info = wl_resource_get_user_data(resource);
430         tdm_server_vblank_info *v = NULL, *vv = NULL;
431
432         TDM_RETURN_IF_FAIL(output_info != NULL);
433
434         tdm_output_remove_change_handler(output_info->output,
435                                                                          _tdm_server_cb_output_change, output_info);
436
437         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &output_info->vblank_list, link) {
438                 wl_resource_destroy(v->resource);
439         }
440
441         LIST_DEL(&output_info->link);
442         free(output_info);
443 }
444
445 static void
446 _tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resource,
447                                                          const char *name, uint32_t id)
448 {
449         tdm_private_server *private_server = wl_resource_get_user_data(resource);
450         tdm_server_output_info *output_info;
451         struct wl_resource *output_resource = NULL;
452         tdm_output *output;
453         const tdm_output_mode *mode = NULL;
454         tdm_output_dpms dpms_value;
455         tdm_output_conn_status status;
456
457         output = _tdm_server_find_output(private_server, name);
458         if (!output) {
459                 TDM_ERR("There is no '%s' output", name);
460                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
461                                                            "There is no '%s' output", name);
462                 return;
463         }
464
465         tdm_output_get_mode(output, &mode);
466         if (!mode) {
467                 TDM_ERR("no mode for '%s' output", name);
468                 wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
469                                                            "no mode for '%s' output", name);
470                 return;
471         }
472
473         tdm_output_get_dpms(output, &dpms_value);
474         tdm_output_get_conn_status(output, &status);
475
476         output_resource =
477                 wl_resource_create(client, &wl_tdm_output_interface,
478                                                    wl_resource_get_version(resource), id);
479         if (!output_resource) {
480                 wl_resource_post_no_memory(resource);
481                 TDM_ERR("wl_resource_create failed");
482                 return;
483         }
484
485         output_info = calloc(1, sizeof * output_info);
486         if (!output_info) {
487                 wl_resource_post_no_memory(resource);
488                 wl_resource_destroy(output_resource);
489                 TDM_ERR("alloc failed");
490                 return;
491         }
492
493         LIST_ADDTAIL(&output_info->link, &private_server->output_list);
494         output_info->private_server = private_server;
495         output_info->resource = output_resource;
496         output_info->output = output;
497         LIST_INITHEAD(&output_info->vblank_list);
498
499         tdm_output_add_change_handler(output, _tdm_server_cb_output_change, output_info);
500
501         wl_resource_set_implementation(output_resource, &tdm_output_implementation,
502                                                                    output_info, destroy_output_callback);
503
504         wl_tdm_output_send_mode(output_resource, mode->hdisplay, mode->vdisplay, mode->vrefresh);
505         wl_tdm_output_send_dpms(output_resource, dpms_value);
506         wl_tdm_output_send_connection(output_resource, status);
507 }
508
509 static void
510 _tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, const char *options)
511 {
512         tdm_private_server *private_server = wl_resource_get_user_data(resource);
513         tdm_private_loop *private_loop = private_server->private_loop;
514         char message[TDM_SERVER_REPLY_MSG_LEN];
515         int size = sizeof(message);
516         uid_t uid;
517
518         wl_client_get_credentials(client, NULL, &uid, NULL);
519
520         if (uid != 0) {
521                 snprintf(message, size, "tdm-monitor: SHOULD be a superuser.\n");
522                 TDM_ERR("%s", message);
523         } else {
524                 tdm_monitor_server_command(private_loop->dpy, options, message, &size);
525         }
526
527         wl_tdm_send_debug_done(resource, message);
528 }
529
530 static void
531 _tdm_server_cb_set_client_vblank_fps(struct wl_client *client, struct wl_resource *resource,
532                                                                          unsigned int pid, const char *name, unsigned int fps)
533 {
534         tdm_error ret = tdm_vblank_set_client_vblank_fps(pid, name, fps);
535         TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
536
537         TDM_INFO("'%s' vblank fps(PID: '%u'): %u", name, pid, fps);
538 }
539
540 static const struct wl_tdm_interface tdm_implementation = {
541         _tdm_server_cb_debug,
542         _tdm_server_cb_create_output,
543         _tdm_server_cb_set_client_vblank_fps,
544 };
545
546 static void
547 _tdm_server_bind(struct wl_client *client, void *data,
548                                  uint32_t version, uint32_t id)
549 {
550         struct wl_resource *resource;
551
552         resource = wl_resource_create(client, &wl_tdm_interface, version, id);
553         if (!resource) {
554                 wl_client_post_no_memory(client);
555                 return;
556         }
557
558         wl_resource_set_implementation(resource, &tdm_implementation, data, NULL);
559 }
560
561 INTERN tdm_error
562 tdm_server_init(tdm_private_loop *private_loop)
563 {
564         tdm_private_server *private_server;
565
566         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_OPERATION_FAILED);
567         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display, TDM_ERROR_OPERATION_FAILED);
568
569         if (private_loop->private_server)
570                 return TDM_ERROR_NONE;
571
572         if (wl_display_add_socket(private_loop->wl_display, "tdm-socket")) {
573                 TDM_ERR("createing a tdm-socket failed");
574                 return TDM_ERROR_OPERATION_FAILED;
575         }
576
577         private_server = calloc(1, sizeof * private_server);
578         if (!private_server) {
579                 TDM_ERR("alloc failed");
580                 return TDM_ERROR_OUT_OF_MEMORY;
581         }
582
583         LIST_INITHEAD(&private_server->output_list);
584         LIST_INITHEAD(&private_server->wait_list);
585
586         if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1,
587                                                   private_server, _tdm_server_bind)) {
588                 TDM_ERR("creating a global resource failed");
589                 free(private_server);
590                 return TDM_ERROR_OUT_OF_MEMORY;
591         }
592
593         private_server->private_loop = private_loop;
594         private_loop->private_server = private_server;
595         keep_private_server = private_server;
596
597         return TDM_ERROR_NONE;
598 }
599
600 INTERN void
601 tdm_server_deinit(tdm_private_loop *private_loop)
602 {
603         tdm_server_output_info *o = NULL, *oo = NULL;
604         tdm_server_wait_info *w = NULL, *ww = NULL;
605         tdm_private_server *private_server;
606
607         if (!private_loop->private_server)
608                 return;
609
610         private_server = private_loop->private_server;
611
612         LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_server->wait_list, link) {
613                 destroy_wait(w);
614         }
615
616         LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_server->output_list, link) {
617                 wl_resource_destroy(o->resource);
618         }
619
620         free(private_server);
621         private_loop->private_server = NULL;
622         keep_private_server = NULL;
623 }
624