blob: b09526212a5223a34eb8267a7b4dc61105a6f14b [file] [log] [blame]
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +03001/*
2 * Copyright © 2012 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +030023#define _GNU_SOURCE
24
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +030025#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <math.h>
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +030029#include <sys/types.h>
30#include <dirent.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <unistd.h>
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +030034
35#include <EGL/egl.h>
36#include <GLES2/gl2.h>
37
38#include "compositor.h"
39#include "android-framebuffer.h"
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +030040#include "evdev.h"
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +030041
42struct android_compositor;
43
44struct android_output {
45 struct android_compositor *compositor;
46 struct weston_output base;
47
48 struct weston_mode mode;
49 struct android_framebuffer *fb;
50 EGLSurface egl_surface;
51};
52
53struct android_seat {
54 struct weston_seat base;
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +030055 struct wl_list devices_list;
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +030056};
57
58struct android_compositor {
59 struct weston_compositor base;
60
61 struct android_seat *seat;
62};
63
64static inline struct android_output *
65to_android_output(struct weston_output *base)
66{
67 return container_of(base, struct android_output, base);
68}
69
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +030070static inline struct android_seat *
71to_android_seat(struct weston_seat *base)
72{
73 return container_of(base, struct android_seat, base);
74}
75
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +030076static inline struct android_compositor *
77to_android_compositor(struct weston_compositor *base)
78{
79 return container_of(base, struct android_compositor, base);
80}
81
82static const char *
83egl_error_string(EGLint code)
84{
85#define MYERRCODE(x) case x: return #x;
86 switch (code) {
87 MYERRCODE(EGL_SUCCESS)
88 MYERRCODE(EGL_NOT_INITIALIZED)
89 MYERRCODE(EGL_BAD_ACCESS)
90 MYERRCODE(EGL_BAD_ALLOC)
91 MYERRCODE(EGL_BAD_ATTRIBUTE)
92 MYERRCODE(EGL_BAD_CONTEXT)
93 MYERRCODE(EGL_BAD_CONFIG)
94 MYERRCODE(EGL_BAD_CURRENT_SURFACE)
95 MYERRCODE(EGL_BAD_DISPLAY)
96 MYERRCODE(EGL_BAD_SURFACE)
97 MYERRCODE(EGL_BAD_MATCH)
98 MYERRCODE(EGL_BAD_PARAMETER)
99 MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
100 MYERRCODE(EGL_BAD_NATIVE_WINDOW)
101 MYERRCODE(EGL_CONTEXT_LOST)
102 default:
103 return "unknown";
104 }
105#undef MYERRCODE
106}
107
108static void
109print_egl_error_state(void)
110{
111 EGLint code;
112
113 code = eglGetError();
Martin Minarik6d118362012-06-07 18:01:59 +0200114 weston_log("EGL error state: %s (0x%04lx)\n",
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300115 egl_error_string(code), (long)code);
116}
117
118static int
119android_output_make_current(struct android_output *output)
120{
121 struct android_compositor *compositor = output->compositor;
122 EGLBoolean ret;
123 static int errored;
124
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400125 ret = eglMakeCurrent(compositor->base.egl_display, output->egl_surface,
126 output->egl_surface, compositor->base.egl_context);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300127 if (ret == EGL_FALSE) {
128 if (errored)
129 return -1;
130 errored = 1;
Martin Minarik6d118362012-06-07 18:01:59 +0200131 weston_log("Failed to make EGL context current.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300132 print_egl_error_state();
133 return -1;
134 }
135
136 return 0;
137}
138
139static void
140android_finish_frame(void *data)
141{
142 struct android_output *output = data;
143
144 weston_output_finish_frame(&output->base,
145 weston_compositor_get_time());
146}
147
148static void
149android_output_repaint(struct weston_output *base, pixman_region32_t *damage)
150{
151 struct android_output *output = to_android_output(base);
152 struct android_compositor *compositor = output->compositor;
153 struct weston_surface *surface;
154 struct wl_event_loop *loop;
155 EGLBoolean ret;
156 static int errored;
157
158 if (android_output_make_current(output) < 0)
159 return;
160
161 wl_list_for_each_reverse(surface, &compositor->base.surface_list, link)
162 weston_surface_draw(surface, &output->base, damage);
163
Kristian Høgsberge0f832b2012-06-20 00:13:18 -0400164 wl_signal_emit(&output->base.frame_signal, output);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300165
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400166 ret = eglSwapBuffers(compositor->base.egl_display, output->egl_surface);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300167 if (ret == EGL_FALSE && !errored) {
168 errored = 1;
Martin Minarik6d118362012-06-07 18:01:59 +0200169 weston_log("Failed in eglSwapBuffers.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300170 print_egl_error_state();
171 }
172
173 /* FIXME: does Android have a way to signal page flip done? */
174 loop = wl_display_get_event_loop(compositor->base.wl_display);
175 wl_event_loop_add_idle(loop, android_finish_frame, output);
176}
177
178static void
179android_output_destroy(struct weston_output *base)
180{
181 struct android_output *output = to_android_output(base);
182
183 wl_list_remove(&output->base.link);
184 weston_output_destroy(&output->base);
185
186 android_framebuffer_destroy(output->fb);
187
188 free(output);
189}
190
191static struct android_output *
192android_output_create(struct android_compositor *compositor)
193{
194 struct android_output *output;
195
196 output = calloc(1, sizeof *output);
197 if (!output)
198 return NULL;
199
200 output->fb = android_framebuffer_create();
201 if (!output->fb) {
202 free(output);
203 return NULL;
204 }
205
206 output->compositor = compositor;
207 return output;
208}
209
210static void
211android_compositor_add_output(struct android_compositor *compositor,
212 struct android_output *output)
213{
214 float mm_width, mm_height;
215
216 output->base.repaint = android_output_repaint;
217 output->base.destroy = android_output_destroy;
218 output->base.assign_planes = NULL;
219 output->base.set_backlight = NULL;
220 output->base.set_dpms = NULL;
221 output->base.switch_mode = NULL;
222
223 /* only one static mode in list */
224 output->mode.flags =
225 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
226 output->mode.width = output->fb->width;
227 output->mode.height = output->fb->height;
228 output->mode.refresh = ceilf(1000.0f * output->fb->refresh_rate);
229 wl_list_init(&output->base.mode_list);
230 wl_list_insert(&output->base.mode_list, &output->mode.link);
231
232 output->base.current = &output->mode;
233 output->base.origin = &output->mode;
234 output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
235 output->base.make = "unknown";
236 output->base.model = "unknown";
237
238 mm_width = output->fb->width / output->fb->xdpi * 25.4f;
239 mm_height = output->fb->height / output->fb->ydpi * 25.4f;
240 weston_output_init(&output->base, &compositor->base,
Scott Moreau1bad5db2012-08-18 01:04:05 -0600241 0, 0, round(mm_width), round(mm_height),
242 WL_OUTPUT_TRANSFORM_NORMAL);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300243 wl_list_insert(compositor->base.output_list.prev, &output->base.link);
244}
245
246static void
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300247android_led_update(struct weston_seat *seat_base, enum weston_led leds)
248{
249 struct android_seat *seat = to_android_seat(seat_base);
Pekka Paalanen3eb47612012-08-06 14:57:08 +0300250 struct evdev_device *device;
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300251
Pekka Paalanenb9d38f42012-08-06 14:57:07 +0300252 wl_list_for_each(device, &seat->devices_list, link)
253 evdev_led_update(device, leds);
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300254}
255
256static void
257android_seat_open_device(struct android_seat *seat, const char *devnode)
258{
Pekka Paalanen3eb47612012-08-06 14:57:08 +0300259 struct evdev_device *device;
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300260 int fd;
261
262 /* XXX: check the Android excluded list */
263
264 fd = open(devnode, O_RDWR | O_NONBLOCK | O_CLOEXEC);
265 if (fd < 0) {
266 weston_log_continue("opening '%s' failed: %s\n", devnode,
267 strerror(errno));
268 return;
269 }
270
Pekka Paalanen3eb47612012-08-06 14:57:08 +0300271 device = evdev_device_create(&seat->base, devnode, fd);
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300272 if (!device) {
273 close(fd);
274 return;
275 }
276
277 wl_list_insert(seat->devices_list.prev, &device->link);
278}
279
280static int
281is_dot_or_dotdot(const char *str)
282{
283 return (str[0] == '.' &&
284 (str[1] == 0 || (str[1] == '.' && str[2] == 0)));
285}
286
287static void
288android_seat_scan_devices(struct android_seat *seat, const char *dirpath)
289{
290 int ret;
291 DIR *dir;
292 struct dirent *dent;
293 char *devnode = NULL;
294
295 dir = opendir(dirpath);
296 if (!dir) {
297 weston_log("Could not open input device directory '%s': %s\n",
298 dirpath, strerror(errno));
299 return;
300 }
301
302 while ((dent = readdir(dir)) != NULL) {
303 if (is_dot_or_dotdot(dent->d_name))
304 continue;
305
306 ret = asprintf(&devnode, "%s/%s", dirpath, dent->d_name);
307 if (ret < 0)
308 continue;
309
310 android_seat_open_device(seat, devnode);
311 free(devnode);
312 }
313
314 closedir(dir);
315}
316
317static void
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300318android_seat_destroy(struct android_seat *seat)
319{
Pekka Paalanen3eb47612012-08-06 14:57:08 +0300320 struct evdev_device *device, *next;
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300321
322 wl_list_for_each_safe(device, next, &seat->devices_list, link)
Pekka Paalanen3eb47612012-08-06 14:57:08 +0300323 evdev_device_destroy(device);
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300324
325 if (seat->base.seat.keyboard)
Kristian Høgsbergcb3eaae2012-08-10 09:50:11 -0400326 notify_keyboard_focus_out(&seat->base);
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300327
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300328 weston_seat_release(&seat->base);
329 free(seat);
330}
331
332static struct android_seat *
333android_seat_create(struct android_compositor *compositor)
334{
335 struct android_seat *seat;
336
337 seat = calloc(1, sizeof *seat);
338 if (!seat)
339 return NULL;
340
341 weston_seat_init(&seat->base, &compositor->base);
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300342 seat->base.led_update = android_led_update;
343 wl_list_init(&seat->devices_list);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300344
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300345 android_seat_scan_devices(seat, "/dev/input");
346
347 evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
348
349 if (wl_list_empty(&seat->devices_list))
350 weston_log("Warning: no input devices found.\n");
351
352 /* XXX: implement hotplug support */
353
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300354 return seat;
355}
356
357static int
358android_egl_choose_config(struct android_compositor *compositor,
359 struct android_framebuffer *fb,
360 const EGLint *attribs)
361{
362 EGLBoolean ret;
363 EGLint count = 0;
364 EGLint matched = 0;
365 EGLConfig *configs;
366 int i;
367
368 /*
369 * The logic is copied from Android frameworks/base/services/
370 * surfaceflinger/DisplayHardware/DisplayHardware.cpp
371 */
372
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400373 compositor->base.egl_config = NULL;
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300374
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400375 ret = eglGetConfigs(compositor->base.egl_display, NULL, 0, &count);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300376 if (ret == EGL_FALSE || count < 1)
377 return -1;
378
379 configs = calloc(count, sizeof *configs);
380 if (!configs)
381 return -1;
382
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400383 ret = eglChooseConfig(compositor->base.egl_display, attribs, configs,
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300384 count, &matched);
385 if (ret == EGL_FALSE || matched < 1)
386 goto out;
387
388 for (i = 0; i < matched; ++i) {
389 EGLint id;
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400390 ret = eglGetConfigAttrib(compositor->base.egl_display,
391 configs[i], EGL_NATIVE_VISUAL_ID,
392 &id);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300393 if (ret == EGL_FALSE)
394 continue;
395 if (id > 0 && fb->format == id) {
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400396 compositor->base.egl_config = configs[i];
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300397 break;
398 }
399 }
400
401out:
402 free(configs);
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400403 if (!compositor->base.egl_config)
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300404 return -1;
405
406 return 0;
407}
408
409static int
410android_init_egl(struct android_compositor *compositor,
411 struct android_output *output)
412{
413 EGLint eglmajor, eglminor;
414 int ret;
415
416 static const EGLint context_attrs[] = {
417 EGL_CONTEXT_CLIENT_VERSION, 2,
418 EGL_NONE
419 };
420
421 static const EGLint config_attrs[] = {
422 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
423 EGL_RED_SIZE, 1,
424 EGL_GREEN_SIZE, 1,
425 EGL_BLUE_SIZE, 1,
426 EGL_ALPHA_SIZE, 0,
427 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
428 EGL_NONE
429 };
430
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400431 compositor->base.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
432 if (compositor->base.egl_display == EGL_NO_DISPLAY) {
Martin Minarik6d118362012-06-07 18:01:59 +0200433 weston_log("Failed to create EGL display.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300434 print_egl_error_state();
435 return -1;
436 }
437
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400438 ret = eglInitialize(compositor->base.egl_display, &eglmajor, &eglminor);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300439 if (!ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200440 weston_log("Failed to initialise EGL.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300441 print_egl_error_state();
442 return -1;
443 }
444
445 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
Martin Minarik6d118362012-06-07 18:01:59 +0200446 weston_log("Failed to bind EGL_OPENGL_ES_API.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300447 print_egl_error_state();
448 return -1;
449 }
450
451 ret = android_egl_choose_config(compositor, output->fb, config_attrs);
452 if (ret < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200453 weston_log("Failed to find an EGL config.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300454 print_egl_error_state();
455 return -1;
456 }
457
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400458 compositor->base.egl_context =
459 eglCreateContext(compositor->base.egl_display,
460 compositor->base.egl_config,
461 EGL_NO_CONTEXT,
462 context_attrs);
463 if (compositor->base.egl_context == EGL_NO_CONTEXT) {
Martin Minarik6d118362012-06-07 18:01:59 +0200464 weston_log("Failed to create a GL ES 2 context.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300465 print_egl_error_state();
466 return -1;
467 }
468
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400469 output->egl_surface =
470 eglCreateWindowSurface(compositor->base.egl_display,
471 compositor->base.egl_config,
472 output->fb->native_window,
473 NULL);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300474 if (output->egl_surface == EGL_NO_SURFACE) {
Martin Minarik6d118362012-06-07 18:01:59 +0200475 weston_log("Failed to create FB EGLSurface.\n");
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300476 print_egl_error_state();
477 return -1;
478 }
479
480 if (android_output_make_current(output) < 0)
481 return -1;
482
483 return 0;
484}
485
486static void
487android_fini_egl(struct android_compositor *compositor)
488{
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400489 eglMakeCurrent(compositor->base.egl_display,
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300490 EGL_NO_SURFACE, EGL_NO_SURFACE,
491 EGL_NO_CONTEXT);
492
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400493 eglTerminate(compositor->base.egl_display);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300494 eglReleaseThread();
495}
496
497static void
498android_compositor_destroy(struct weston_compositor *base)
499{
500 struct android_compositor *compositor = to_android_compositor(base);
501
502 android_seat_destroy(compositor->seat);
503
504 /* destroys outputs, too */
505 weston_compositor_shutdown(&compositor->base);
506
507 android_fini_egl(compositor);
508
509 free(compositor);
510}
511
512static struct weston_compositor *
Daniel Stonec1be8e52012-06-01 11:14:02 -0400513android_compositor_create(struct wl_display *display, int argc, char *argv[],
514 const char *config_file)
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300515{
516 struct android_compositor *compositor;
517 struct android_output *output;
518
Pekka Paalanen4ddf1b22012-08-03 14:39:13 +0300519 weston_log("initializing android backend\n");
520
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300521 compositor = calloc(1, sizeof *compositor);
522 if (compositor == NULL)
523 return NULL;
524
Daniel Stone725c2c32012-06-22 14:04:36 +0100525 if (weston_compositor_init(&compositor->base, display, argc, argv,
526 config_file) < 0)
Martin Olssonc5da0992012-07-08 03:03:42 +0200527 goto err_free;
Daniel Stone725c2c32012-06-22 14:04:36 +0100528
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300529 compositor->base.destroy = android_compositor_destroy;
530
531 compositor->base.focus = 1;
532
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300533 output = android_output_create(compositor);
534 if (!output)
Martin Olssonc5da0992012-07-08 03:03:42 +0200535 goto err_compositor;
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300536
537 if (android_init_egl(compositor, output) < 0)
Martin Olssonc5da0992012-07-08 03:03:42 +0200538 goto err_output;
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300539
Daniel Stone725c2c32012-06-22 14:04:36 +0100540 if (weston_compositor_init_gl(&compositor->base) < 0)
Martin Olssonc5da0992012-07-08 03:03:42 +0200541 goto err_egl;
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300542
543 android_compositor_add_output(compositor, output);
544
545 compositor->seat = android_seat_create(compositor);
546 if (!compositor->seat)
Martin Olssonc5da0992012-07-08 03:03:42 +0200547 goto err_egl;
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300548
549 return &compositor->base;
Martin Olssonc5da0992012-07-08 03:03:42 +0200550
551err_egl:
552 android_fini_egl(compositor);
553err_output:
554 android_output_destroy(&output->base);
555err_compositor:
556 weston_compositor_shutdown(&compositor->base);
557err_free:
558 free(compositor);
559 return NULL;
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300560}
561
562WL_EXPORT struct weston_compositor *
Daniel Stonec1be8e52012-06-01 11:14:02 -0400563backend_init(struct wl_display *display, int argc, char *argv[],
564 const char *config_file)
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300565{
Daniel Stonec1be8e52012-06-01 11:14:02 -0400566 return android_compositor_create(display, argc, argv, config_file);
Pekka Paalanen3ae50bb2012-05-30 15:53:45 +0300567}