add wl_tdm_client interface
[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  */
49
50 struct _tdm_private_server {
51         tdm_private_loop *private_loop;
52         struct list_head client_list;
53         struct list_head vblank_list;
54 };
55
56 typedef struct _tdm_server_client_info {
57         struct list_head link;
58         tdm_private_server *private_server;
59         struct wl_resource *resource;
60 } tdm_server_client_info;
61
62 typedef struct _tdm_server_vblank_info {
63         struct list_head link;
64         tdm_server_client_info *client_info;
65         struct wl_resource *resource;
66 } tdm_server_vblank_info;
67
68 static tdm_private_server *keep_private_server;
69 static int tdm_debug_server;
70
71 static void
72 _tdm_server_cb_output_vblank(tdm_output *output, unsigned int sequence,
73                              unsigned int tv_sec, unsigned int tv_usec,
74                              void *user_data)
75 {
76         tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data;
77         tdm_server_vblank_info *found;
78
79         if (!keep_private_server)
80                 return;
81
82         LIST_FIND_ITEM(vblank_info, &keep_private_server->vblank_list,
83                        tdm_server_vblank_info, link, found);
84         if (!found) {
85                 TDM_DBG("vblank_info(%p) is destroyed", vblank_info);
86                 return;
87         }
88
89         if (tdm_debug_server) {
90                 unsigned long curr = tdm_helper_get_time_in_micros();
91                 unsigned long vtime = (tv_sec * 1000000) + tv_usec;
92                 if (curr - vtime > 1000) /* 1ms */
93                         TDM_WRN("delay: %d us", (int)(curr - vtime));
94         }
95
96         TDM_DBG("wl_tdm_vblank@%d done", wl_resource_get_id(vblank_info->resource));
97
98         wl_tdm_vblank_send_done(vblank_info->resource, sequence, tv_sec, tv_usec);
99         wl_resource_destroy(vblank_info->resource);
100 }
101
102 static void
103 destroy_vblank_callback(struct wl_resource *resource)
104 {
105         tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
106         LIST_DEL(&vblank_info->link);
107         free(vblank_info);
108 }
109
110 static void
111 _tdm_server_client_cb_destroy(struct wl_client *client, struct wl_resource *resource)
112 {
113         wl_resource_destroy(resource);
114 }
115
116 static void
117 _tdm_server_client_cb_wait_vblank(struct wl_client *client,
118                                   struct wl_resource *resource,
119                                   uint32_t id, const char *name,
120                                   int32_t sw_timer, int32_t interval,
121                                   uint32_t req_sec, uint32_t req_usec)
122 {
123         tdm_server_client_info *client_info = wl_resource_get_user_data(resource);
124         tdm_private_server *private_server = client_info->private_server;
125         tdm_private_loop *private_loop = private_server->private_loop;
126         tdm_server_vblank_info *vblank_info;
127         struct wl_resource *vblank_resource;
128         tdm_output *found = NULL;
129         tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON;
130         int count = 0, i;
131         tdm_error ret;
132
133         TDM_DBG("The tdm client requests vblank");
134
135         if (tdm_debug_server) {
136                 unsigned long curr = tdm_helper_get_time_in_micros();
137                 unsigned long reqtime = (req_sec * 1000000) + req_usec;
138                 if (curr - reqtime > 1000) /* 1ms */
139                         TDM_WRN("delay(req): %d us", (int)(curr - reqtime));
140         }
141
142         tdm_display_get_output_count(private_loop->dpy, &count);
143
144         for (i = 0; i < count; i++) {
145                 tdm_output *output= tdm_display_get_output(private_loop->dpy, i, NULL);
146                 tdm_output_conn_status status;
147                 const char *model = NULL;
148
149                 ret = tdm_output_get_conn_status(output, &status);
150                 if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
151                         continue;
152
153                 ret = tdm_output_get_model_info(output, NULL, &model, NULL);
154                 if (ret || !model)
155                         continue;
156
157                 if (strncmp(model, name, TDM_NAME_LEN))
158                         continue;
159
160                 found = output;
161                 break;
162         }
163
164         if (!found) {
165                 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_INVALID_NAME,
166                                        "There is no '%s' output", name);
167                 TDM_ERR("There is no '%s' output", name);
168                 return;
169         }
170
171         tdm_output_get_dpms(found, &dpms_value);
172
173         if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) {
174                 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_DPMS_OFF,
175                                        "dpms '%s'", tdm_get_dpms_str(dpms_value));
176                 TDM_ERR("dpms '%s'", tdm_get_dpms_str(dpms_value));
177                 return;
178         }
179
180         vblank_info = calloc(1, sizeof *vblank_info);
181         if (!vblank_info) {
182                 wl_resource_post_no_memory(resource);
183                 TDM_ERR("alloc failed");
184                 return;
185         }
186
187         TDM_DBG("wl_tdm_vblank@%d output(%s) interval(%d)", id, name, interval);
188
189         vblank_resource =
190                 wl_resource_create(client, &wl_tdm_vblank_interface,
191                                    wl_resource_get_version(resource), id);
192         if (!vblank_resource) {
193                 wl_resource_post_no_memory(resource);
194                 TDM_ERR("wl_resource_create failed");
195                 goto free_info;
196         }
197
198         if (dpms_value == TDM_OUTPUT_DPMS_ON) {
199                 ret = tdm_output_wait_vblank(found, interval, 0,
200                                              _tdm_server_cb_output_vblank, vblank_info);
201                 if (ret != TDM_ERROR_NONE) {
202                         wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED,
203                                                "couldn't wait vblank for %s", name);
204                         TDM_ERR("couldn't wait vblank for %s", name);
205                         goto destroy_resource;
206                 }
207         } else if (sw_timer) {
208                 /* TODO: need to implement things related with sw_timer */
209                 if (ret != TDM_ERROR_NONE) {
210                         wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED,
211                                                "not implemented yet");
212                         TDM_ERR("not implemented yet");
213                         goto destroy_resource;
214                 }
215         } else {
216                 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED,
217                                        "bad implementation");
218                 TDM_NEVER_GET_HERE();
219                 goto destroy_resource;
220         }
221
222         vblank_info->resource = vblank_resource;
223         vblank_info->client_info = client_info;
224
225         wl_resource_set_implementation(vblank_resource, NULL, vblank_info,
226                                        destroy_vblank_callback);
227
228         LIST_ADDTAIL(&vblank_info->link, &private_server->vblank_list);
229         return;
230 destroy_resource:
231         wl_resource_destroy(vblank_resource);
232 free_info:
233         free(vblank_info);
234 }
235
236 static const struct wl_tdm_client_interface tdm_client_implementation = {
237         _tdm_server_client_cb_destroy,
238         _tdm_server_client_cb_wait_vblank,
239 };
240
241 static void
242 destroy_client_callback(struct wl_resource *resource)
243 {
244         tdm_server_client_info *client_info = wl_resource_get_user_data(resource);
245
246         LIST_DEL(&client_info->link);
247         free(client_info);
248 }
249
250 static void
251 _tdm_server_cb_create_client(struct wl_client *client,
252                              struct wl_resource *resource, uint32_t id)
253 {
254         tdm_private_server *private_server = wl_resource_get_user_data(resource);
255         tdm_server_client_info *client_info;
256         struct wl_resource *client_resource;
257
258         client_info = calloc(1, sizeof *client_info);
259         if (!client_info) {
260                 wl_resource_post_no_memory(resource);
261                 TDM_ERR("alloc failed");
262                 return;
263         }
264
265         client_resource =
266                 wl_resource_create(client, &wl_tdm_client_interface,
267                                    wl_resource_get_version(resource), id);
268         if (!client_resource) {
269                 wl_resource_post_no_memory(resource);
270                 free(client_info);
271                 TDM_ERR("wl_resource_create failed");
272                 return;
273         }
274
275         client_info->private_server = private_server;
276         client_info->resource = client_resource;
277
278         wl_resource_set_implementation(client_resource, &tdm_client_implementation,
279                                        client_info, destroy_client_callback);
280
281         LIST_ADDTAIL(&client_info->link, &private_server->client_list);
282 }
283
284 static const struct wl_tdm_interface tdm_implementation = {
285         _tdm_server_cb_create_client,
286 };
287
288 static void
289 _tdm_server_bind(struct wl_client *client, void *data,
290                  uint32_t version, uint32_t id)
291 {
292         struct wl_resource *resource;
293
294         resource = wl_resource_create(client, &wl_tdm_interface, version, id);
295         if (!resource) {
296                 wl_client_post_no_memory(client);
297                 return;
298         }
299
300         TDM_DBG("tdm server binding");
301
302         wl_resource_set_implementation(resource, &tdm_implementation, data, NULL);
303 }
304
305 INTERN tdm_error
306 tdm_server_init(tdm_private_loop *private_loop)
307 {
308         tdm_private_server *private_server;
309         const char *debug;
310
311         debug = getenv("TDM_DEBUG_SERVER");
312         if (debug && (strstr(debug, "1")))
313                 tdm_debug_server = 1;
314
315         if (private_loop->private_server)
316                 return TDM_ERROR_NONE;
317
318         TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_OPERATION_FAILED);
319         TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display, TDM_ERROR_OPERATION_FAILED);
320
321         if(wl_display_add_socket(private_loop->wl_display, "tdm-socket")) {
322                 TDM_ERR("createing a tdm-socket failed");
323                 return TDM_ERROR_OPERATION_FAILED;
324         }
325
326         private_server = calloc(1, sizeof *private_server);
327         if (!private_server) {
328                 TDM_ERR("alloc failed");
329                 return TDM_ERROR_OUT_OF_MEMORY;
330         }
331
332         LIST_INITHEAD(&private_server->client_list);
333         LIST_INITHEAD(&private_server->vblank_list);
334
335         if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1,
336                               private_server, _tdm_server_bind)) {
337                 TDM_ERR("creating a global resource failed");
338                 free(private_server);
339                 return TDM_ERROR_OUT_OF_MEMORY;
340         }
341
342         private_server->private_loop = private_loop;
343         private_loop->private_server = private_server;
344         keep_private_server = private_server;
345
346         return TDM_ERROR_NONE;
347 }
348
349 INTERN void
350 tdm_server_deinit(tdm_private_loop *private_loop)
351 {
352         tdm_server_vblank_info *v = NULL, *vv = NULL;
353         tdm_server_client_info *c = NULL, *cc = NULL;
354         tdm_private_server *private_server;
355
356         if (!private_loop->private_server)
357                 return;
358
359         private_server = private_loop->private_server;
360
361         LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_server->vblank_list, link) {
362                 wl_resource_destroy(v->resource);
363         }
364
365         LIST_FOR_EACH_ENTRY_SAFE(c, cc, &private_server->client_list, link) {
366                 wl_resource_destroy(c->resource);
367         }
368
369         free(private_server);
370         private_loop->private_server = NULL;
371         keep_private_server = NULL;
372 }