*
*/
-#include <stdlib.h>
-#include <stdbool.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/shm.h>
-#include <pthread.h>
-#include <dlfcn.h>
-
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/extensions/XShm.h>
+#include <stdlib.h> // for system
+#include <sys/types.h> // for stat, getpid
+#include <sys/stat.h> // fot stat, chmod
+#include <unistd.h> // fot stat, getpid
+#include <pthread.h> // for mutex
+#include <sys/param.h> // MIN, MAX
+#include <sys/mman.h> // mmap
#include <Ecore.h>
#include <Evas.h>
#include <Evas_Engine_Buffer.h>
#include <Elementary.h>
+#include <wayland-client.h>
+#include "wayland-api.h"
+
#include "ui_viewer_utils.h"
-typedef struct _screenshot_data
-{
- XImage* ximage;
- Display* dpy;
- XShmSegmentInfo x_shm_info;
-} screenshot_data;
+#define MAX_HEIGHT 720
+#define CAPTURE_TIMEOUT 2.0
+#define MAX_PATH_LENGTH 256
static int screenshotIndex = 0;
static pthread_mutex_t captureScreenLock = PTHREAD_MUTEX_INITIALIZER;
-static char* captureScreenShotX(screenshot_data* sdata)
+struct screenshot_data {
+ struct wl_shm *shm;
+ struct screenshooter *screenshooter;
+ struct wl_list output_list;
+ int min_x, min_y, max_x, max_y;
+ int buffer_copy_done;
+};
+
+struct screenshooter_output {
+ struct wl_output *output;
+ struct wl_buffer *buffer;
+ int width, height, offset_x, offset_y;
+ void *data;
+ struct wl_list link;
+};
+
+static void
+display_handle_geometry(void __attribute__((unused)) *data,
+ struct wl_output *wl_output,
+ int x,
+ int y,
+ int __attribute__((unused)) physical_width,
+ int __attribute__((unused)) physical_height,
+ int __attribute__((unused)) subpixel,
+ const char __attribute__((unused)) *make,
+ const char __attribute__((unused)) *model,
+ int __attribute__((unused)) transform)
{
- Window root;
- int width, height;
+ struct screenshooter_output *output;
- sdata->dpy = XOpenDisplay(NULL);
- if(sdata->dpy == NULL)
- {
- ui_viewer_log("ERROR: XOpenDisplay failed\n");
- return NULL;
+ output = wl_output_get_user_data(wl_output);
+
+ if (wl_output == output->output) {
+ output->offset_x = x;
+ output->offset_y = y;
}
+}
- width = DisplayWidth(sdata->dpy, DefaultScreen(sdata->dpy));
- height = DisplayHeight(sdata->dpy, DefaultScreen(sdata->dpy));
+static void
+display_handle_mode(void __attribute__((unused)) *data,
+ struct wl_output *wl_output, uint32_t flags, int width,
+ int height, int __attribute__((unused)) refresh)
+{
+ struct screenshooter_output *output;
- root = RootWindow(sdata->dpy, DefaultScreen(sdata->dpy));
+ output = wl_output_get_user_data(wl_output);
- sdata->ximage = XShmCreateImage(sdata->dpy, DefaultVisualOfScreen (DefaultScreenOfDisplay (sdata->dpy)), 24,
- ZPixmap, NULL, &sdata->x_shm_info, (unsigned int)width, (unsigned int)height);
+ if ((wl_output == output->output) && (flags & WL_OUTPUT_MODE_CURRENT)) {
+ output->width = width;
+ output->height = height;
+ }
+}
- if (sdata->ximage != NULL)
- {
- sdata->x_shm_info.shmid = shmget(IPC_PRIVATE, sdata->ximage->bytes_per_line * sdata->ximage->height, IPC_CREAT | 0777);
- sdata->x_shm_info.shmaddr = sdata->ximage->data = shmat(sdata->x_shm_info.shmid, 0, 0);
- sdata->x_shm_info.readOnly = False;
+static const struct wl_output_listener output_listener = {
+ display_handle_geometry,
+ display_handle_mode,
+ NULL,
+ NULL
+};
- if (XShmAttach(sdata->dpy, &sdata->x_shm_info))
- {
- if (XShmGetImage(sdata->dpy, root, sdata->ximage, 0, 0, AllPlanes))
- {
- XSync(sdata->dpy, False);
- return sdata->ximage->data;
- }
- else
- {
- ui_viewer_log("ERROR: XShmGetImage failed\n");
- }
+static void
+screenshot_done(void *data,
+ struct screenshooter __attribute__((unused)) *screenshooter)
+{
+ struct screenshot_data *sdata = data;
+ sdata->buffer_copy_done = 1;
+}
+
+static const struct screenshooter_listener screenshooter_listener = {
+ screenshot_done
+};
- XShmDetach(sdata->dpy, &sdata->x_shm_info);
+static void
+handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface,
+ uint32_t __attribute__((unused)) version)
+{
+ struct screenshot_data *sdata = data;
+
+ if (strcmp(interface, "wl_output") == 0) {
+ struct screenshooter_output *output = malloc(sizeof(*output));
+
+ if (output) {
+ PRINTMSG("allocate %p", output);
+ output->output = wl_registry_bind(registry, name,
+ &wl_output_interface,
+ 1);
+ wl_list_insert(&sdata->output_list, &output->link);
+ wl_output_add_listener(output->output,
+ &output_listener, output);
}
- else
- {
- ui_viewer_log("ERROR: XShmAttach failed\n");
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ sdata->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ } else if (strcmp(interface, "screenshooter") == 0) {
+ sdata->screenshooter = wl_registry_bind(registry, name,
+ &screenshooter_interface,
+ 1);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ handle_global,
+ NULL
+};
+
+static struct wl_buffer *create_shm_buffer(struct wl_shm *shm, int width,
+ int height, void **data_out)
+{
+ char filename[] = "/tmp/wayland-shm-XXXXXX";
+ struct wl_shm_pool *pool;
+ struct wl_buffer *buffer;
+ int fd, size, stride;
+ void *data;
+
+ stride = width * 4;
+ size = stride * height;
+
+ fd = mkstemp(filename);
+ if (fd < 0)
+ return NULL;
+
+ if (ftruncate(fd, size) < 0) {
+ close(fd);
+ return NULL;
+ }
+
+ data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ unlink(filename);
+
+ if (data == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ pool = wl_shm_create_pool(shm, fd, size);
+ close(fd);
+ buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
+ WL_SHM_FORMAT_XRGB8888);
+ wl_shm_pool_destroy(pool);
+ *data_out = data;
+
+ return buffer;
+}
+
+static void *__screenshot_to_buf(struct screenshot_data *sdata, int x, int y,
+ int width, int height)
+{
+ int dest_stride, src_stride, i;
+ void *data, *dest, *src;
+ struct screenshooter_output *output, *next;
+ dest_stride = width * 4;
+
+ data = malloc(width * height * 4);
+ if (!data)
+ return NULL;
+
+ wl_list_for_each_safe(output, next, &sdata->output_list, link) {
+ src_stride = output->width * 4;
+ dest = data;
+ if (y + height <= output->height) {
+ src = output->data + y * src_stride + x * 4;
+
+ for (i = 0; i < height; i++) {
+ memcpy(dest, src, dest_stride);
+ dest += dest_stride;
+ src += src_stride;
+ }
+ } else {
+ PRINTERR("Cannot get screenshot. wrong height."
+ "src_height=%d [y=%d, h=%d]", output->height,
+ y, height);
+ memset(dest, 0xab, height * width * 4);
}
- shmdt (sdata->x_shm_info.shmaddr);
- shmctl (sdata->x_shm_info.shmid, IPC_RMID, NULL);
- XDestroyImage(sdata->ximage);
- sdata->ximage = NULL;
+ wl_list_remove(&output->link);
+ free(output);
+ }
+
+ return data;
+}
+
+static int
+set_buffer_size(struct screenshot_data *sdata, int *width, int *height)
+{
+ struct screenshooter_output *output;
+
+ sdata->min_x = sdata->min_y = INT_MAX;
+ sdata->max_x = sdata->max_y = INT_MIN;
+ int position = 0;
+
+ wl_list_for_each_reverse(output, &sdata->output_list, link) {
+ output->offset_x = position;
+ position += output->width;
}
- else
- {
- ui_viewer_log("ERROR: XShmCreateImage failed\n");
+
+ wl_list_for_each(output, &sdata->output_list, link) {
+ sdata->min_x = MIN(sdata->min_x, output->offset_x);
+ sdata->min_y = MIN(sdata->min_y, output->offset_y);
+ sdata->max_x = MAX(sdata->max_x,
+ output->offset_x + output->width);
+ sdata->max_y = MAX(sdata->max_y,
+ output->offset_y + output->height);
}
- return NULL;
+ if (sdata->max_x <= sdata->min_x || sdata->max_y <= sdata->min_y)
+ return -1;
+
+ *width = sdata->max_x - sdata->min_x;
+ *height = sdata->max_y - sdata->min_y;
+
+ return 0;
}
-static void releaseScreenShotX(screenshot_data* sdata)
+static void *__capture_screnshot_wayland(int x, int y, int width, int height)
{
- if(sdata->ximage)
- {
- XShmDetach(sdata->dpy, &sdata->x_shm_info);
- shmdt (sdata->x_shm_info.shmaddr);
- shmctl (sdata->x_shm_info.shmid, IPC_RMID, NULL);
- XDestroyImage(sdata->ximage);
+ struct wl_display *display = NULL;
+ struct wl_registry *registry;
+ struct screenshooter_output *output;
+ void *buf = NULL;
+ struct screenshot_data *sdata;
+ const char *wayland_socket = NULL;
+
+ wayland_socket = getenv("WAYLAND_SOCKET");
+ if (!wayland_socket)
+ wayland_socket = getenv("WAYLAND_DISPLAY");
+
+ if (!wayland_socket) {
+ PRINTERR("must be launched by wayland");
+ return NULL;
}
- else { }
- if(sdata->dpy)
- {
- XCloseDisplay(sdata->dpy);
+ sdata = malloc(sizeof(*sdata));
+ if (!sdata)
+ return NULL;
+
+ sdata->shm = NULL;
+ sdata->screenshooter = NULL;
+ wl_list_init(&sdata->output_list);
+ sdata->min_x = 0;
+ sdata->min_y = 0;
+ sdata->max_x = 0;
+ sdata->max_y = 0;
+ sdata->buffer_copy_done = 0;
+
+ display = wl_display_connect(wayland_socket);
+ if (display == NULL)
+ goto out;
+
+ /* wl_list_init(&output_list); */
+ registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, ®istry_listener, sdata);
+ wl_display_dispatch(display);
+ wl_display_roundtrip(display);
+
+ if (sdata->screenshooter == NULL) {
+ PRINTERR("display doesn't support screenshooter");
+ return NULL;
}
+
+ screenshooter_add_listener(sdata->screenshooter,
+ &screenshooter_listener, sdata);
+
+ int buf_width, buf_height;
+ if (set_buffer_size(sdata, &buf_width, &buf_height))
+ return NULL;
+
+ wl_list_for_each(output, &sdata->output_list, link) {
+ output->buffer = create_shm_buffer(sdata->shm, output->width,
+ output->height,
+ &output->data);
+ screenshooter_shoot(sdata->screenshooter, output->output,
+ output->buffer);
+ sdata->buffer_copy_done = 0;
+ while (!sdata->buffer_copy_done) {
+ wl_display_roundtrip(display);
+ }
+ }
+
+ buf = __screenshot_to_buf(sdata, x, y, width, height);
+
+out:
+ if (display)
+ wl_display_disconnect(display);
+ free(sdata);
+ return buf;
}
static int capture_object(char *screenshot_path, int width, int height,
- Evas_Object *obj, const char *type_name) {
+ Evas_Object *obj, const char *type_name)
+{
char dstpath[MAX_PATH_LENGTH];
Evas_Object *img;
Evas *canvas, *sub_canvas;
ecore_evas_resize(sub_ee, width, height);
if (!strcmp(type_name, "rectangle")) {
- Evas_Object* rect;
+ Evas_Object *rect;
int r, g, b, a;
rect = evas_object_rectangle_add(sub_canvas);
evas_object_show(image);
} else {
- char* scrimage;
- screenshot_data sdata;
int x, y;
- int screen_w;
Evas_Object *image;
- int i, j;
- int bytes_per_pixel;
evas_object_geometry_get(obj, &x, &y, NULL, NULL);
-
- sdata.ximage = NULL;
- scrimage = captureScreenShotX(&sdata);
- if (!scrimage) {
- ui_viewer_log("ERROR: capture_object : no scrimage\n");
- ret = -1;
- goto finish;
- }
- bytes_per_pixel = sdata.ximage->bits_per_pixel / 8;
- screen_w = sdata.ximage->width;
-
- image_data = malloc(width * height * bytes_per_pixel);
- if (!image_data) {
- ui_viewer_log("ERROR: capture_object : can't allocate %d bytes\n",
- width * height * bytes_per_pixel);
- ret = -1;
+ image_data = __capture_screnshot_wayland(x, y, width, height);
+ if (image_data == NULL)
goto finish;
- }
-
- // remove invisible image parts
- if (x < 0)
- x = 0;
-
- if (y < 0)
- y = 0;
-
- // crop image
- for (j = 0; j < height; j++) {
- for (i = 0; i < width * bytes_per_pixel; i++) {
- image_data[i + j * width * bytes_per_pixel] =
- scrimage[(i + x * bytes_per_pixel) + (j + y) * screen_w * bytes_per_pixel];
- }
- }
-
image = evas_object_image_filled_add(sub_canvas);
evas_object_image_size_set(image, width, height);
evas_object_image_data_set(image, image_data);
evas_object_image_data_update_add(image, 0, 0, width, height);
evas_object_resize(image, width, height);
evas_object_show(image);
-finish:
- releaseScreenShotX(&sdata);
}
+finish:
ecore_evas_manual_render(sub_ee);
- snprintf(dstpath, sizeof(dstpath), TMP_DIR"/%d_%d.png", _getpid(),
+ snprintf(dstpath, sizeof(dstpath), TMP_DIR "/%d_%d.png", _getpid(),
screenshotIndex);
if (evas_object_image_save(img, dstpath, NULL, "compress=5") != 0) {
char type_name[MAX_PATH_LENGTH];
if (!screenshot_path) {
- ui_viewer_log("ui_viewer_capture_screen : no screenshot path\n");
+ ui_viewer_log("ui_viewer_capture_screen: no screenshot path\n");
ret = -1;
return ret;
}
evas_object_geometry_get(obj, &obj_x, &obj_y, &obj_w, &obj_h);
if (obj_w == 0 || obj_h == 0) {
- ui_viewer_log("ui_viewer_capture_screen : object %p[%d,%d] has zero width or height\n",
- obj, obj_w, obj_h);
+ ui_viewer_log("ui_viewer_capture_screen : object %p[%d,%d]"
+ "has zero width or height\n", obj, obj_w, obj_h);
ret = -1;
} else if (!strcmp(type_name, "rectangle")) {
int width, height;
width = (obj_w <= view_w) ? obj_w : view_w;
height = (obj_h <= view_h) ? obj_h : view_h;
- ret = capture_object(screenshot_path, width, height, obj, type_name);
+ ret = capture_object(screenshot_path, width, height, obj,
+ type_name);
} else if (!strcmp(type_name, "image")) {
- ret = capture_object(screenshot_path, obj_w, obj_h, obj, type_name);
+ ret = capture_object(screenshot_path, obj_w, obj_h, obj,
+ type_name);
} else if (!strcmp(type_name, "vectors")) {
ret = -1;
} else if (!strcmp(type_name, "elm_image") ||
- !strcmp(type_name, "elm_icon")) {
+ !strcmp(type_name, "elm_icon")) {
Evas_Object *internal_img;
int img_w, img_h;
internal_img = elm_image_object_get(obj);
- evas_object_geometry_get(internal_img, NULL, NULL, &img_w, &img_h);
+ evas_object_geometry_get(internal_img, NULL, NULL, &img_w,
+ &img_h);
- ret = capture_object(screenshot_path, img_w, img_h, internal_img, type_name);
+ ret = capture_object(screenshot_path, img_w, img_h,
+ internal_img, type_name);
} else if (obj_x > view_w || obj_y > view_h) {
- ui_viewer_log("ui_viewer_capture_screen : object %p lies beside view area\n",
- obj);
+ ui_viewer_log("ui_viewer_capture_screen:"
+ "object %p lies beside view area\n", obj);
ret = -1;
} else if (!evas_object_visible_get(obj)) {
- ui_viewer_log("ui_viewer_capture_screen : object %p is unvisible\n",
- obj);
+ ui_viewer_log("ui_viewer_capture_screen:"
+ "object %p is unvisible\n", obj);
ret = -1;
} else {
int width, height;
width = (view_w < obj_w + obj_x) ? (view_w - obj_x) : obj_w;
height = (view_h < obj_h + obj_y) ? (view_h - obj_y) : obj_h;
- ret = capture_object(screenshot_path, width, height, obj, type_name);
+ ret = capture_object(screenshot_path, width, height, obj,
+ type_name);
}
if (ret)