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 int screenshotIndex = 0;
54 static pthread_mutex_t captureScreenLock = PTHREAD_MUTEX_INITIALIZER;
57 struct wl_display *wl_display;
58 struct wl_registry *wl_registry;
59 struct wl_event_queue *wl_queue;
60 struct screenshooter *screenshooter;
61 struct wl_list output_list;
62 int min_x, min_y, max_x, max_y;
67 struct screenshot_data {
69 struct screenshooter *screenshooter;
70 struct wl_list output_list;
71 int min_x, min_y, max_x, max_y;
75 struct screenshooter_output {
76 struct wl_output *output;
77 struct wl_buffer *buffer;
78 int width, height, offset_x, offset_y;
84 display_handle_geometry(void __attribute__((unused)) *data,
85 struct wl_output *wl_output,
88 int __attribute__((unused)) physical_width,
89 int __attribute__((unused)) physical_height,
90 int __attribute__((unused)) subpixel,
91 const char __attribute__((unused)) *make,
92 const char __attribute__((unused)) *model,
93 int __attribute__((unused)) transform)
95 struct screenshooter_output *output;
97 output = wl_output_get_user_data(wl_output);
99 if (wl_output == output->output) {
100 output->offset_x = x;
101 output->offset_y = y;
106 display_handle_mode(void __attribute__((unused)) *data,
107 struct wl_output *wl_output, uint32_t flags, int width,
108 int height, int __attribute__((unused)) refresh)
110 struct screenshooter_output *output;
112 output = wl_output_get_user_data(wl_output);
114 if ((wl_output == output->output) && (flags & WL_OUTPUT_MODE_CURRENT)) {
115 output->width = width;
116 output->height = height;
120 static const struct wl_output_listener output_listener = {
121 display_handle_geometry,
128 screenshot_done(void *data,
129 struct screenshooter __attribute__((unused)) *screenshooter)
131 struct efl_data *sdata = data;
132 sdata->buffer_copy_done = 1;
135 static const struct screenshooter_listener screenshooter_listener = {
140 handle_global(void *data, struct wl_registry *registry,
141 uint32_t name, const char *interface,
142 uint32_t __attribute__((unused)) version)
144 struct efl_data *sdata = data;
146 if (strcmp(interface, "wl_output") == 0) {
147 struct screenshooter_output *output = malloc(sizeof(*output));
150 PRINTMSG("allocate %p", output);
151 output->output = wl_registry_bind(registry, name,
152 &wl_output_interface,
154 wl_list_insert(&sdata->output_list, &output->link);
155 wl_output_add_listener(output->output,
156 &output_listener, output);
158 } else if (strcmp(interface, "screenshooter") == 0) {
159 sdata->screenshooter = wl_registry_bind(registry, name,
160 &screenshooter_interface,
162 screenshooter_add_listener(sdata->screenshooter,
163 &screenshooter_listener,
168 static const struct wl_registry_listener registry_listener = {
173 static struct efl_data *__edata = NULL;
175 void wayland_deinit(void)
177 struct screenshooter_output *output, *next;
182 wl_list_for_each_safe(output, next, &__edata->output_list, link)
185 wl_registry_destroy(__edata->wl_registry);
186 wl_event_queue_destroy(__edata->wl_queue);
187 wl_display_disconnect(__edata->wl_display);
192 static struct efl_data *__wayland_init(void)
194 struct wl_event_queue *queue;
195 struct wl_display *display = NULL;
196 struct wl_registry *registry;
197 struct efl_data *data;
198 const char *wayland_socket = NULL;
203 wayland_socket = getenv("WAYLAND_SOCKET");
205 wayland_socket = getenv("WAYLAND_DISPLAY");
207 if (!wayland_socket) {
208 PRINTERR("must be launched by wayland");
212 data = malloc(sizeof(*data));
216 data->screenshooter = NULL;
217 wl_list_init(&data->output_list);
222 data->buffer_copy_done = 0;
224 display = wl_display_connect(wayland_socket);
225 if (display == NULL) {
226 PRINTERR("failed to create display: %m\n");
230 queue = wl_display_create_queue(display);
232 PRINTERR("failed to create display queue: %m\n");
236 /* wl_list_init(&output_list); */
237 registry = wl_display_get_registry(display);
238 wl_proxy_set_queue((struct wl_proxy*)registry, queue);
239 wl_registry_add_listener(registry, ®istry_listener, data);
240 wl_display_dispatch_queue(display, queue);
241 wl_display_roundtrip_queue(display, queue);
243 data->wl_display = display;
244 data->wl_queue = queue;
245 data->wl_registry = registry;
251 wl_display_disconnect(display);
258 static void __set_buffer_size(struct efl_data *data)
260 struct screenshooter_output *output;
262 data->min_x = data->min_y = INT_MAX;
263 data->max_x = data->max_y = INT_MIN;
266 wl_list_for_each_reverse(output, &data->output_list, link) {
267 output->offset_x = position;
268 position += output->width;
271 wl_list_for_each(output, &data->output_list, link) {
272 data->min_x = MIN(data->min_x, output->offset_x);
273 data->min_y = MIN(data->min_y, output->offset_y);
274 data->max_x = MAX(data->max_x, output->offset_x + output->width);
275 data->max_y = MAX(data->max_y, output->offset_y + output->height);
278 if (data->max_x <= data->min_x || data->max_y <= data->min_y) {
282 data->width = data->max_x - data->min_x;
283 data->height = data->max_y - data->min_y;
287 static int min(int a, int b)
294 static int max(int a, int b)
301 static void *__tbm_surface_to_buf(unsigned char *src, uint32_t src_stride,
302 uint32_t src_size, int x, int y,
303 int width, int height)
311 int dest_x, dest_y, dest_width, dest_height;
312 int src_x, src_y, src_width, src_height;
318 src_width = src_stride / 4;
319 src_height = src_size / src_stride;
321 src_width = max(0, min(x + width, src_width) - src_x);
322 src_height = max(0, min(y + height, src_height) - src_y);
327 dest_height = height;
328 dest_stride = dest_width * 4;
329 dest_size = dest_stride * dest_height;
331 data = calloc(1, dest_size);
335 dest = data + dest_x * 4 + dest_y * dest_stride;
336 src = src + src_x * 4 + src_y * src_stride;
337 dest_copy_size = src_width * 4;
339 for (n = 0; n < src_height; n++) {
340 memcpy(dest, src, dest_copy_size);
347 static void *__capture_screnshot_wayland(int x, int y, int width, int height)
349 struct screenshooter_output *output;
350 struct efl_data *data;
351 tbm_surface_h t_surface;
352 struct wl_buffer *buffer;
353 struct wayland_tbm_client *tbm_client;
354 tbm_surface_info_s tsuri;
359 data = __wayland_init();
361 PRINTERR("failed to init wayland protocol\n");
365 if (!data->screenshooter) {
366 PRINTERR("display doesn't support screenshooter\n");
370 __set_buffer_size(data);
372 tbm_client = wayland_tbm_client_init(data->wl_display);
374 PRINTERR("failed to init tbm client\n");
378 t_surface = tbm_surface_create(data->width, data->height, TBM_FORMAT_XRGB8888);
380 PRINTERR("failed to create tbm_surface\n");
381 goto fail_tbm_client;
384 buffer = wayland_tbm_client_create_buffer(tbm_client, t_surface);
386 PRINTERR("failed to create wl_buffer for screenshot\n");
387 goto fail_tbm_surface;
390 wl_list_for_each(output, &data->output_list, link) {
391 screenshooter_shoot(data->screenshooter,
394 data->buffer_copy_done = 0;
395 while (!data->buffer_copy_done) {
396 wl_display_roundtrip_queue(data->wl_display,
401 err = tbm_surface_map(t_surface,
402 TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE,
404 if (err != TBM_SURFACE_ERROR_NONE) {
405 PRINTERR("failed to map tbm_surface\n");
406 goto fail_map_surface;
409 buf = __tbm_surface_to_buf(tsuri.planes[plane_idx].ptr,
410 tsuri.planes[plane_idx].stride,
411 tsuri.planes[plane_idx].size,
412 x, y, width, height);
414 tbm_surface_unmap(t_surface);
417 wayland_tbm_client_destroy_buffer(tbm_client, buffer);
420 tbm_surface_destroy(t_surface);
423 wayland_tbm_client_deinit(tbm_client);
427 static int capture_object(char *screenshot_path, int width, int height,
428 Evas_Object *obj, const char *type_name)
430 char dstpath[MAX_PATH_LENGTH];
432 Evas *canvas, *sub_canvas;
433 Ecore_Evas *ee, *sub_ee;
435 char *image_data = NULL;
439 canvas = evas_object_evas_get(obj);
440 ee = ecore_evas_ecore_evas_get(canvas);
441 img = ecore_evas_object_image_new(ee);
442 evas_object_image_filled_set(img, EINA_TRUE);
443 evas_object_image_size_set(img, width, height);
444 sub_ee = ecore_evas_object_ecore_evas_get(img);
445 sub_canvas = ecore_evas_object_evas_get(img);
446 evas_object_resize(img, width, height);
447 ecore_evas_resize(sub_ee, width, height);
449 if (!strcmp(type_name, "rectangle")) {
453 rect = evas_object_rectangle_add(sub_canvas);
454 evas_object_color_get(obj, &r, &g, &b, &a);
455 evas_object_color_set(rect, r, g, b, a);
456 evas_object_resize(rect, width, height);
457 evas_object_show(rect);
458 } else if (!strcmp(type_name, "image")) {
463 img_data = evas_object_image_data_get(obj, EINA_FALSE);
464 evas_object_image_size_get(obj, &w, &h);
465 image = evas_object_image_filled_add(sub_canvas);
466 evas_object_image_size_set(image, w, h);
467 evas_object_image_data_set(image, img_data);
468 evas_object_image_data_update_add(image, 0, 0, w, h);
469 evas_object_resize(image, width, height);
471 evas_object_show(image);
476 evas_object_geometry_get(obj, &x, &y, NULL, NULL);
477 image_data = __capture_screnshot_wayland(x, y, width, height);
478 if (image_data == NULL)
480 image = evas_object_image_filled_add(sub_canvas);
481 evas_object_image_size_set(image, width, height);
482 evas_object_image_data_set(image, image_data);
483 evas_object_image_data_update_add(image, 0, 0, width, height);
484 evas_object_resize(image, width, height);
485 evas_object_show(image);
489 ecore_evas_manual_render(sub_ee);
491 snprintf(dstpath, sizeof(dstpath), TMP_DIR "/%d_%d.png", _getpid(),
494 if (evas_object_image_save(img, dstpath, NULL, "compress=5") != 0) {
495 strncpy(screenshot_path, dstpath, MAX_PATH_LENGTH);
497 ui_viewer_log("ERROR: capture_object : can't save image\n");
501 evas_object_del(img);
507 int ui_viewer_capture_screen(char *screenshot_path, Evas_Object *obj)
510 int obj_x, obj_y, obj_w, obj_h;
513 char type_name[MAX_PATH_LENGTH];
515 if (!screenshot_path) {
516 ui_viewer_log("ui_viewer_capture_screen: no screenshot path\n");
521 pthread_mutex_lock(&captureScreenLock);
523 evas = evas_object_evas_get(obj);
525 PRINTERR("Bad object evas");
526 pthread_mutex_unlock(&captureScreenLock);
529 _strncpy(type_name, evas_object_type_get(obj), MAX_PATH_LENGTH);
531 evas_output_viewport_get(evas, NULL, NULL, &view_w, &view_h);
532 evas_object_geometry_get(obj, &obj_x, &obj_y, &obj_w, &obj_h);
534 if (obj_w == 0 || obj_h == 0) {
535 ui_viewer_log("ui_viewer_capture_screen : object %p[%d,%d]"
536 "has zero width or height\n", obj, obj_w, obj_h);
538 } else if (!strcmp(type_name, "rectangle")) {
541 // restrict rectangle area
542 width = (obj_w <= view_w) ? obj_w : view_w;
543 height = (obj_h <= view_h) ? obj_h : view_h;
545 ret = capture_object(screenshot_path, width, height, obj,
547 } else if (!strcmp(type_name, "image")) {
548 ret = capture_object(screenshot_path, obj_w, obj_h, obj,
550 } else if (!strcmp(type_name, "vectors")) {
552 } else if (!strcmp(type_name, "elm_image") ||
553 !strcmp(type_name, "elm_icon")) {
554 Evas_Object *internal_img;
557 internal_img = elm_image_object_get(obj);
558 evas_object_geometry_get(internal_img, NULL, NULL, &img_w,
561 ret = capture_object(screenshot_path, img_w, img_h,
562 internal_img, type_name);
563 } else if (obj_x > view_w || obj_y > view_h) {
564 ui_viewer_log("ui_viewer_capture_screen:"
565 "object %p lies beside view area\n", obj);
567 } else if (!evas_object_visible_get(obj)) {
568 ui_viewer_log("ui_viewer_capture_screen:"
569 "object %p is unvisible\n", obj);
574 // take visible on screen part of object
575 width = (view_w < obj_w + obj_x) ? (view_w - obj_x) : obj_w;
576 height = (view_h < obj_h + obj_y) ? (view_h - obj_y) : obj_h;
578 ret = capture_object(screenshot_path, width, height, obj,
583 screenshot_path[0] = '\0';
585 pthread_mutex_unlock(&captureScreenLock);