4 * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
5 * Copyright (c) 2008 Kristian Høgsberg (screenshooter)
9 * Lyupa Anastasia <a.lyupa@samsung.com>
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
24 * - Samsung RnD Institute Russia
28 #include <stdlib.h> // for system
29 #include <sys/types.h> // for stat, getpid
30 #include <sys/stat.h> // fot stat, chmod
31 #include <unistd.h> // fot stat, getpid
32 #include <pthread.h> // for mutex
33 #include <sys/param.h> // MIN, MAX
34 #include <sys/mman.h> // mmap
38 #include <Evas_Engine_Buffer.h>
39 #include <Elementary.h>
41 #include <wayland-client.h>
42 #include <wayland-tbm-client.h>
43 #include <tbm_bufmgr.h>
44 #include <tbm_surface.h>
45 #include <tbm_surface_internal.h>
46 #include <tizen-extension-client-protocol.h>
47 #include <screenshooter-client-protocol.h>
49 #include "ui_viewer_utils.h"
51 #define MAX_PATH_LENGTH 256
53 static pthread_mutex_t captureScreenLock = PTHREAD_MUTEX_INITIALIZER;
56 struct wl_display *wl_display;
57 struct wl_registry *wl_registry;
58 struct wl_event_queue *wl_queue;
59 struct screenshooter *screenshooter;
60 struct wl_list output_list;
61 int min_x, min_y, max_x, max_y;
66 struct screenshot_data {
68 struct screenshooter *screenshooter;
69 struct wl_list output_list;
70 int min_x, min_y, max_x, max_y;
74 struct screenshooter_output {
75 struct wl_output *output;
76 struct wl_buffer *buffer;
77 int width, height, offset_x, offset_y;
83 display_handle_geometry(void __attribute__((unused)) *data,
84 struct wl_output *wl_output,
87 int __attribute__((unused)) physical_width,
88 int __attribute__((unused)) physical_height,
89 int __attribute__((unused)) subpixel,
90 const char __attribute__((unused)) *make,
91 const char __attribute__((unused)) *model,
92 int __attribute__((unused)) transform)
94 struct screenshooter_output *output;
96 output = wl_output_get_user_data(wl_output);
98 if (wl_output == output->output) {
100 output->offset_y = y;
105 display_handle_mode(void __attribute__((unused)) *data,
106 struct wl_output *wl_output, uint32_t flags, int width,
107 int height, int __attribute__((unused)) refresh)
109 struct screenshooter_output *output;
111 output = wl_output_get_user_data(wl_output);
113 if ((wl_output == output->output) && (flags & WL_OUTPUT_MODE_CURRENT)) {
114 output->width = width;
115 output->height = height;
119 static const struct wl_output_listener output_listener = {
120 display_handle_geometry,
127 screenshot_done(void *data,
128 struct screenshooter __attribute__((unused)) *screenshooter)
130 struct efl_data *sdata = data;
131 sdata->buffer_copy_done = 1;
134 static const struct screenshooter_listener screenshooter_listener = {
139 handle_global(void *data, struct wl_registry *registry,
140 uint32_t name, const char *interface,
141 uint32_t __attribute__((unused)) version)
143 struct efl_data *sdata = data;
145 if (strcmp(interface, "wl_output") == 0) {
146 struct screenshooter_output *output = malloc(sizeof(*output));
149 PRINTMSG("allocate %p", output);
150 output->output = wl_registry_bind(registry, name,
151 &wl_output_interface,
153 wl_list_insert(&sdata->output_list, &output->link);
154 wl_output_add_listener(output->output,
155 &output_listener, output);
157 } else if (strcmp(interface, "screenshooter") == 0) {
158 sdata->screenshooter = wl_registry_bind(registry, name,
159 &screenshooter_interface,
161 screenshooter_add_listener(sdata->screenshooter,
162 &screenshooter_listener,
167 static const struct wl_registry_listener registry_listener = {
172 static struct efl_data *__edata = NULL;
174 void wayland_deinit(void)
176 struct screenshooter_output *output, *next;
181 wl_list_for_each_safe(output, next, &__edata->output_list, link)
184 wl_registry_destroy(__edata->wl_registry);
185 wl_event_queue_destroy(__edata->wl_queue);
186 wl_display_disconnect(__edata->wl_display);
191 static struct efl_data *__wayland_init(void)
193 struct wl_event_queue *queue;
194 struct wl_display *display = NULL;
195 struct wl_registry *registry;
196 struct efl_data *data;
197 const char *wayland_socket = NULL;
202 wayland_socket = getenv("WAYLAND_SOCKET");
204 wayland_socket = getenv("WAYLAND_DISPLAY");
206 if (!wayland_socket) {
207 PRINTERR("must be launched by wayland");
211 data = malloc(sizeof(*data));
215 data->screenshooter = NULL;
216 wl_list_init(&data->output_list);
221 data->buffer_copy_done = 0;
223 display = wl_display_connect(wayland_socket);
224 if (display == NULL) {
225 PRINTERR("failed to create display: %m\n");
229 queue = wl_display_create_queue(display);
231 PRINTERR("failed to create display queue: %m\n");
235 /* wl_list_init(&output_list); */
236 registry = wl_display_get_registry(display);
237 wl_proxy_set_queue((struct wl_proxy*)registry, queue);
238 wl_registry_add_listener(registry, ®istry_listener, data);
239 wl_display_dispatch_queue(display, queue);
240 wl_display_roundtrip_queue(display, queue);
242 data->wl_display = display;
243 data->wl_queue = queue;
244 data->wl_registry = registry;
250 wl_display_disconnect(display);
257 static void __set_buffer_size(struct efl_data *data)
259 struct screenshooter_output *output;
261 data->min_x = data->min_y = INT_MAX;
262 data->max_x = data->max_y = INT_MIN;
265 wl_list_for_each_reverse(output, &data->output_list, link) {
266 output->offset_x = position;
267 position += output->width;
270 wl_list_for_each(output, &data->output_list, link) {
271 data->min_x = MIN(data->min_x, output->offset_x);
272 data->min_y = MIN(data->min_y, output->offset_y);
273 data->max_x = MAX(data->max_x, output->offset_x + output->width);
274 data->max_y = MAX(data->max_y, output->offset_y + output->height);
277 if (data->max_x <= data->min_x || data->max_y <= data->min_y) {
281 data->width = data->max_x - data->min_x;
282 data->height = data->max_y - data->min_y;
286 static int min(int a, int b)
293 static int max(int a, int b)
300 static void *__tbm_surface_to_buf(unsigned char *src, uint32_t src_stride,
301 uint32_t src_size, int x, int y,
302 int width, int height)
310 int dest_x, dest_y, dest_width, dest_height;
311 int src_x, src_y, src_width, src_height;
317 src_width = src_stride / 4;
318 src_height = src_size / src_stride;
320 src_width = max(0, min(x + width, src_width) - src_x);
321 src_height = max(0, min(y + height, src_height) - src_y);
326 dest_height = height;
327 dest_stride = dest_width * 4;
328 dest_size = dest_stride * dest_height;
330 data = calloc(1, dest_size);
334 dest = data + dest_x * 4 + dest_y * dest_stride;
335 src = src + src_x * 4 + src_y * src_stride;
336 dest_copy_size = src_width * 4;
338 for (n = 0; n < src_height; n++) {
339 memcpy(dest, src, dest_copy_size);
346 static void *__capture_screnshot_wayland(int x, int y, int width, int height)
348 struct screenshooter_output *output;
349 struct efl_data *data;
350 tbm_surface_h t_surface;
351 struct wl_buffer *buffer;
352 struct wayland_tbm_client *tbm_client;
353 tbm_surface_info_s tsuri;
358 data = __wayland_init();
360 PRINTERR("failed to init wayland protocol\n");
364 if (!data->screenshooter) {
365 PRINTERR("display doesn't support screenshooter\n");
369 __set_buffer_size(data);
371 tbm_client = wayland_tbm_client_init(data->wl_display);
373 PRINTERR("failed to init tbm client\n");
377 t_surface = tbm_surface_create(data->width, data->height, TBM_FORMAT_XRGB8888);
379 PRINTERR("failed to create tbm_surface\n");
380 goto fail_tbm_client;
383 buffer = wayland_tbm_client_create_buffer(tbm_client, t_surface);
385 PRINTERR("failed to create wl_buffer for screenshot\n");
386 goto fail_tbm_surface;
389 wl_list_for_each(output, &data->output_list, link) {
390 screenshooter_shoot(data->screenshooter,
393 data->buffer_copy_done = 0;
394 while (!data->buffer_copy_done) {
395 wl_display_roundtrip_queue(data->wl_display,
400 err = tbm_surface_map(t_surface,
401 TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE,
403 if (err != TBM_SURFACE_ERROR_NONE) {
404 PRINTERR("failed to map tbm_surface\n");
405 goto fail_map_surface;
408 buf = __tbm_surface_to_buf(tsuri.planes[plane_idx].ptr,
409 tsuri.planes[plane_idx].stride,
410 tsuri.planes[plane_idx].size,
411 x, y, width, height);
413 tbm_surface_unmap(t_surface);
416 wayland_tbm_client_destroy_buffer(tbm_client, buffer);
419 tbm_surface_destroy(t_surface);
422 wayland_tbm_client_deinit(tbm_client);
426 static int capture_object(const char *screenshot_path, int width, int height,
427 Evas_Object *obj, const char *type_name)
430 Evas *canvas, *sub_canvas;
431 Ecore_Evas *ee, *sub_ee;
433 char *image_data = NULL;
435 canvas = evas_object_evas_get(obj);
436 ee = ecore_evas_ecore_evas_get(canvas);
437 img = ecore_evas_object_image_new(ee);
438 evas_object_image_filled_set(img, EINA_TRUE);
439 evas_object_image_size_set(img, width, height);
440 sub_ee = ecore_evas_object_ecore_evas_get(img);
441 sub_canvas = ecore_evas_object_evas_get(img);
442 evas_object_resize(img, width, height);
443 ecore_evas_resize(sub_ee, width, height);
445 if (!strcmp(type_name, "rectangle")) {
449 rect = evas_object_rectangle_add(sub_canvas);
450 evas_object_color_get(obj, &r, &g, &b, &a);
451 evas_object_color_set(rect, r, g, b, a);
452 evas_object_resize(rect, width, height);
453 evas_object_show(rect);
454 } else if (!strcmp(type_name, "image")) {
459 img_data = evas_object_image_data_get(obj, EINA_FALSE);
460 evas_object_image_size_get(obj, &w, &h);
461 image = evas_object_image_filled_add(sub_canvas);
462 evas_object_image_size_set(image, w, h);
463 evas_object_image_data_set(image, img_data);
464 evas_object_image_data_update_add(image, 0, 0, w, h);
465 evas_object_resize(image, width, height);
467 evas_object_show(image);
472 evas_object_geometry_get(obj, &x, &y, NULL, NULL);
473 image_data = __capture_screnshot_wayland(x, y, width, height);
474 if (image_data == NULL)
476 image = evas_object_image_filled_add(sub_canvas);
477 evas_object_image_size_set(image, width, height);
478 evas_object_image_data_set(image, image_data);
479 evas_object_image_data_update_add(image, 0, 0, width, height);
480 evas_object_resize(image, width, height);
481 evas_object_show(image);
485 ecore_evas_manual_render(sub_ee);
487 if (!evas_object_image_save(img, screenshot_path, NULL, "compress=5")) {
488 ui_viewer_log("ERROR: capture_object : can't save image\n");
492 evas_object_del(img);
498 int ui_viewer_capture_screen(const char *screenshot_path, Evas_Object *obj)
501 int obj_x, obj_y, obj_w, obj_h;
504 char type_name[MAX_PATH_LENGTH];
506 pthread_mutex_lock(&captureScreenLock);
508 evas = evas_object_evas_get(obj);
510 PRINTERR("Bad object evas");
511 pthread_mutex_unlock(&captureScreenLock);
514 _strncpy(type_name, evas_object_type_get(obj), MAX_PATH_LENGTH);
516 evas_output_viewport_get(evas, NULL, NULL, &view_w, &view_h);
517 evas_object_geometry_get(obj, &obj_x, &obj_y, &obj_w, &obj_h);
519 if (obj_w == 0 || obj_h == 0) {
520 ui_viewer_log("ui_viewer_capture_screen : object %p[%d,%d]"
521 "has zero width or height\n", obj, obj_w, obj_h);
523 } else if (!strcmp(type_name, "rectangle")) {
526 // restrict rectangle area
527 width = (obj_w <= view_w) ? obj_w : view_w;
528 height = (obj_h <= view_h) ? obj_h : view_h;
530 ret = capture_object(screenshot_path, width, height, obj,
532 } else if (!strcmp(type_name, "image")) {
533 ret = capture_object(screenshot_path, obj_w, obj_h, obj,
535 } else if (!strcmp(type_name, "vectors")) {
537 } else if (!strcmp(type_name, "elm_image") ||
538 !strcmp(type_name, "elm_icon")) {
539 Evas_Object *internal_img;
542 internal_img = elm_image_object_get(obj);
543 evas_object_geometry_get(internal_img, NULL, NULL, &img_w,
546 ret = capture_object(screenshot_path, img_w, img_h,
547 internal_img, type_name);
548 } else if (obj_x > view_w || obj_y > view_h) {
549 ui_viewer_log("ui_viewer_capture_screen:"
550 "object %p lies beside view area\n", obj);
552 } else if (!evas_object_visible_get(obj)) {
553 ui_viewer_log("ui_viewer_capture_screen:"
554 "object %p is unvisible\n", obj);
559 // take visible on screen part of object
560 width = (view_w < obj_w + obj_x) ? (view_w - obj_x) : obj_w;
561 height = (view_h < obj_h + obj_y) ? (view_h - obj_y) : obj_h;
563 ret = capture_object(screenshot_path, width, height, obj,
567 pthread_mutex_unlock(&captureScreenLock);