/* * Copyright © 2008 Kristian Høgsberg * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "weston-screenshooter-client-protocol.h" #include "shared/os-compatibility.h" #include "shared/xalloc.h" #include "shared/file-util.h" #include "shared/cairo-util.h" #include "shared/helpers.h" #include "window.h" /* The screenshooter is a good example of a custom object exposed by * the compositor and serves as a test bed for implementing client * side marshalling outside libwayland.so */ int isloop = 1; struct image *pimage; struct screenshooter_output { struct wl_output *output; struct wl_buffer *buffer; int width, height, offset_x, offset_y; void *data; struct wl_list link; }; struct buffer_size { int width, height; int min_x, min_y; int max_x, max_y; }; struct screenshooter_data { struct wl_shm *shm; struct wl_list output_list; struct weston_screenshooter *screenshooter; int buffer_copy_done; }; struct image { struct window *window; struct widget *widget; struct display *display; char *filename; cairo_surface_t *image; int fullscreen; int *image_counter; int32_t width, height; struct { double x; double y; } pointer; bool button_pressed; bool initialized; cairo_matrix_t matrix; }; struct display { struct wl_display *display; struct wl_registry *registry; struct wl_compositor *compositor; struct wl_subcompositor *subcompositor; struct wl_shm *shm; struct wl_data_device_manager *data_device_manager; struct text_cursor_position *text_cursor_position; struct xdg_wm_base *xdg_shell; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; struct zwp_pointer_constraints_v1 *pointer_constraints; EGLDisplay dpy; EGLConfig argb_config; EGLContext argb_ctx; cairo_device_t *argb_device; uint32_t serial; int display_fd; uint32_t display_fd_events; struct task display_task; int epoll_fd; struct wl_list deferred_list; int running; struct wl_list global_list; struct wl_list window_list; struct wl_list input_list; struct wl_list output_list; struct theme *theme; struct wl_cursor_theme *cursor_theme; struct wl_cursor **cursors; display_output_handler_t output_configure_handler; display_global_handler_t global_handler; display_global_handler_t global_handler_remove; void *user_data; struct xkb_context *xkb_context; /* A hack to get text extents for tooltips */ cairo_surface_t *dummy_surface; void *dummy_surface_data; int data_device_manager_version; struct wp_viewporter *viewporter; }; static void handler_sigint(int sig) { isloop = 0; } static void display_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int physical_width, int physical_height, int subpixel, const char *make, const char *model, int transform) { struct screenshooter_output *output; output = wl_output_get_user_data(wl_output); if (wl_output == output->output) { output->offset_x = x; output->offset_y = y; } } static void display_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int width, int height, int refresh) { struct screenshooter_output *output; output = wl_output_get_user_data(wl_output); if ((wl_output == output->output) && (flags & WL_OUTPUT_MODE_CURRENT)) { output->width = width; output->height = height; } } static const struct wl_output_listener output_listener = { display_handle_geometry, display_handle_mode }; static void screenshot_done(void *data, struct weston_screenshooter *screenshooter) { struct screenshooter_data *sh_data = data; sh_data->buffer_copy_done = 1; } static const struct weston_screenshooter_listener screenshooter_listener = { screenshot_done }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { static struct screenshooter_output *output; struct screenshooter_data *sh_data = data; if (strcmp(interface, "wl_output") == 0) { output = xmalloc(sizeof *output); output->output = wl_registry_bind(registry, name, &wl_output_interface, 1); wl_list_insert(&sh_data->output_list, &output->link); wl_output_add_listener(output->output, &output_listener, output); } else if (strcmp(interface, "wl_shm") == 0) { sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, "weston_screenshooter") == 0) { sh_data->screenshooter = wl_registry_bind(registry, name, &weston_screenshooter_interface, 1); } } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { /* XXX: unimplemented */ } static const struct wl_registry_listener registry_listener = { handle_global, handle_global_remove }; static struct wl_buffer * screenshot_create_shm_buffer(int width, int height, void **data_out, struct wl_shm *shm) { struct wl_shm_pool *pool; struct wl_buffer *buffer; int fd, size, stride; void *data; stride = width * 4; size = stride * height; // printf("screenshot_create_shm_buffer width[%d] height[%d] stride[%d] size[%d]\n",width,height,stride,size); fd = os_create_anonymous_file(size); if (fd < 0) { fprintf(stderr, "creating a buffer file for %d B failed: %s\n", size, strerror(errno)); return NULL; } data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { fprintf(stderr, "mmap failed: %s\n", strerror(errno)); 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 double get_scale(struct image *image) { assert(image->matrix.xy == 0.0 && image->matrix.yx == 0.0 && image->matrix.xx == image->matrix.yy); return image->matrix.xx; } static void clamp_view(struct image *image) { struct rectangle allocation; double scale = get_scale(image); double sw, sh; // printf("clamp scale[%f]\n",scale); // printf("clamp image->width[%d] height[%d]\n",image->width,image->height); sw = image->width * scale; sh = image->height * scale; widget_get_allocation(image->widget, &allocation); // printf("clamp sw[%f] sh[%f]\n",sw,sh); // printf("clamp allocation.width[%d] height[%d]\n"),allocation.width,allocation.height; if (sw < allocation.width) { image->matrix.x0 = (allocation.width - image->width * scale) / 2; } else { if (image->matrix.x0 > 0.0) { image->matrix.x0 = 0.0; } if (sw + image->matrix.x0 < allocation.width) { image->matrix.x0 = allocation.width - sw; } } if (sh < allocation.height) { image->matrix.y0 = (allocation.height - image->height * scale) / 2; } else { if (image->matrix.y0 > 0.0) { image->matrix.y0 = 0.0; } if (sh + image->matrix.y0 < allocation.height) { image->matrix.y0 = allocation.height - sh; } } // printf("clamp matrix x0:%f y0:%fxx:%f xy:%f yx:%f yy:%f\n",image->matrix.x0,image->matrix.y0, image->matrix.xx,image->matrix.xy,image->matrix.yx,image->matrix.yy); // printf("x0[%f] y0[%f]\n",image->matrix.x0, image->matrix.y0); } static void redraw_handler(struct widget *widget, void *data) { struct image *image = data; struct rectangle allocation; cairo_t *cr; cairo_surface_t *surface; double width, height, doc_aspect, window_aspect, scale; cairo_matrix_t matrix; cairo_matrix_t translate; surface = window_get_surface(image->window); cr = cairo_create(surface); widget_get_allocation(image->widget, &allocation); // printf("allocation x[%d] y[%d] width[%d] height[%d]\n", allocation.x, allocation.y, allocation.width,allocation.height); cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height); cairo_clip(cr); cairo_push_group(cr); cairo_translate(cr, allocation.x, allocation.y); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // cairo_set_source_rgba(cr, 1, 0, 1, 0.5); // magenta transparency 50% cairo_set_source_rgba(cr, 0, 0, 0, 0); cairo_paint(cr); if (!image->initialized) { image->initialized = true; width = cairo_image_surface_get_width(image->image); height = cairo_image_surface_get_height(image->image); doc_aspect = width / height; window_aspect = (double) allocation.width / allocation.height; if (doc_aspect < window_aspect) { scale = allocation.height / height; } else { scale = allocation.width / width; } // printf("doc_aspect[%f]\n",doc_aspect); // printf("window_aspect[%f]\n",window_aspect); // printf("scale[%f]\n",scale); image->width = width; image->height = height; cairo_matrix_init_scale(&image->matrix, scale, scale); clamp_view(image); } matrix = image->matrix; cairo_matrix_init_translate(&translate, allocation.x, allocation.y); // printf("init matrix x0:%f y0:%f xx:%f xy:%f yx:%f yy:%f\n",matrix.x0,matrix.y0,matrix.xx,matrix.xy,matrix.yx,matrix.yy); cairo_matrix_multiply(&matrix, &matrix, &translate); // printf("mult matrix x0:%f y0:%f xx:%f xy:%f yx:%f yy:%f\n",matrix.x0,matrix.y0,matrix.xx,matrix.xy,matrix.yx,matrix.yy); cairo_set_matrix(cr, &matrix); cairo_set_source_surface(cr, image->image, 0, 0); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_paint(cr); cairo_pop_group_to_source(cr); cairo_paint(cr); cairo_destroy(cr); cairo_surface_destroy(surface); } static void resize_handler(struct widget *widget, int32_t width, int32_t height, void *data) { struct image *image = data; clamp_view(image); } static void keyboard_focus_handler(struct window *window, struct input *device, void *data) { struct image *image = data; window_schedule_redraw(image->window); } static int enter_handler(struct widget *widget, struct input *input, float x, float y, void *data) { struct image *image = data; struct rectangle allocation; widget_get_allocation(image->widget, &allocation); x -= allocation.x; y -= allocation.y; image->pointer.x = x; image->pointer.y = y; return 1; } static void move_viewport(struct image *image, double dx, double dy) { double scale = get_scale(image); if (!image->initialized) { return; } cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale); clamp_view(image); window_schedule_redraw(image->window); } static int motion_handler(struct widget *widget, struct input *input, uint32_t time, float x, float y, void *data) { struct image *image = data; struct rectangle allocation; widget_get_allocation(image->widget, &allocation); x -= allocation.x; y -= allocation.y; if (image->button_pressed) { move_viewport(image, image->pointer.x - x, image->pointer.y - y); } image->pointer.x = x; image->pointer.y = y; return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR; } static void button_handler(struct widget *widget, struct input *input, uint32_t time, uint32_t button, enum wl_pointer_button_state state, void *data) { struct image *image = data; if (button == BTN_LEFT) { image->button_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED; if (state == WL_POINTER_BUTTON_STATE_PRESSED) { input_set_pointer_image(input, CURSOR_DRAGGING); } else { input_set_pointer_image(input, CURSOR_LEFT_PTR); } } } static void zoom(struct image *image, double scale) { double x = image->pointer.x; double y = image->pointer.y; cairo_matrix_t scale_matrix; if (!image->initialized) { return; } if ((get_scale(image) * scale > 20.0) || (get_scale(image) * scale < 0.02)) { return; } cairo_matrix_init_identity(&scale_matrix); cairo_matrix_translate(&scale_matrix, x, y); cairo_matrix_scale(&scale_matrix, scale, scale); cairo_matrix_translate(&scale_matrix, -x, -y); cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix); clamp_view(image); } static void key_handler(struct window *window, struct input *input, uint32_t time, uint32_t key, uint32_t sym, enum wl_keyboard_key_state state, void *data) { struct image *image = data; int transform; if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { return; } // printf("XKB_KEY_f[%x] XKB_KEY_t[%x]\n",XKB_KEY_f,XKB_KEY_t); // printf("sym[%x]\n",sym); switch (sym) { /* case XKB_KEY_minus: zoom(image, 0.8); window_schedule_redraw(image->window); break; case XKB_KEY_equal: case XKB_KEY_plus: zoom(image, 1.2); window_schedule_redraw(image->window); break; case XKB_KEY_1: image->matrix.xx = 1.0; image->matrix.xy = 0.0; image->matrix.yx = 0.0; image->matrix.yy = 1.0; clamp_view(image); window_schedule_redraw(image->window); break; case XKB_KEY_2: image->matrix.xx = 0.5; image->matrix.xy = 0.0; image->matrix.yx = 0.0; image->matrix.yy = 0.5; clamp_view(image); window_schedule_redraw(image->window); break; */ case XKB_KEY_f: image->fullscreen ^= 1; printf("image->fullscreen[%d]\n",image->fullscreen); window_set_fullscreen(image->window, image->fullscreen); clamp_view(image); window_schedule_redraw(image->window); break; /* case XKB_KEY_t: transform = window_get_buffer_transform (image->window); printf("transform[%d]\n",transform); transform = (transform + 1) % 8; window_set_buffer_transform(image->window, transform); window_schedule_redraw(image->window); break; */ case XKB_KEY_q: exit(0); break; } } static void axis_handler(struct widget *widget, struct input *input, uint32_t time, uint32_t axis, wl_fixed_t value, void *data) { struct image *image = data; if ((axis == WL_POINTER_AXIS_VERTICAL_SCROLL) && (input_get_modifiers(input) == MOD_CONTROL_MASK)) { /* set zoom level to 2% per 10 axis units */ zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0)); window_schedule_redraw(image->window); } else if (input_get_modifiers(input) == 0) { if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { move_viewport(image, 0, wl_fixed_to_double(value)); } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { move_viewport(image, wl_fixed_to_double(value), 0); } } } static void fullscreen_handler(struct window *window, void *data) { struct image *image = data; // image->fullscreen ^= 1; window_set_fullscreen(window, image->fullscreen); } int is_pass_close_handler = 0; static void close_handler(void *data) { struct image *image = data; *image->image_counter -= 1; if (*image->image_counter == 0) { display_exit(image->display); } widget_destroy(image->widget); window_destroy(image->window); free(image); is_pass_close_handler = 1; } static char * get_screenshot(const struct buffer_size *buff_size, struct wl_list *output_list) { int output_stride, buffer_stride, i; cairo_surface_t *surface; char *data, *d, *s; struct screenshooter_output *output, *next; FILE *fp; char filepath[PATH_MAX]; buffer_stride = buff_size->width * 4; // printf("buffer_stride[%d] * buff_size->height[%d] = %d\n",buffer_stride, buff_size->height, buffer_stride * buff_size->height); data = xmalloc(1024 * 4 * 768); if (!data) { return; } wl_list_for_each_safe(output, next, output_list, link) { if (output->offset_x == 0) { output_stride = 4; d = data + 1024 * 4 * 767 + 1023 * 4; s = output->data; for (i = 0; i < 1024*768; i++) { memcpy(d, s, output_stride); d -= output_stride; s += output_stride; } wl_buffer_destroy(output->buffer); int ret = munmap(output->data, 1024*4*768); // printf("munmap error[%d]\n",ret); // printf("%s\n",strerror(errno)); // free(output->buffer); } wl_registry_destroy(output->output); free(output); } // fp = file_create_dated("/tmp", "raw-", // ".png", filepath, sizeof(filepath)); // if (fp) { // fwrite(data, 1, 1024 * 4 * 768, fp); // fclose (fp); // } return data; } static struct image * image_create(struct display *display, const char *filename, int *image_counter, char *cap_img) { struct image *image; char *b, *copy, title[512]; image = zalloc(sizeof *image); if (image == NULL) { return image; } sprintf(title,"weston_clone_app"); image->filename = strdup(title); image->image = cairo_image_surface_create_for_data(cap_img, CAIRO_FORMAT_ARGB32, 1024, 768, 1024*4); double width = cairo_image_surface_get_width(image->image); double height = cairo_image_surface_get_height(image->image); printf("1 width[%f] height[%f]\n",width,height); if (!image->image) { free(image->filename); free(image); printf("image->image failed\n"); return NULL; } image->window = window_create(display); image->widget = window_frame_create(image->window, image); window_set_title(image->window, title); image->display = display; image->image_counter = image_counter; *image_counter += 1; image->initialized = false; window_set_user_data(image->window, image); widget_set_redraw_handler(image->widget, redraw_handler); // widget_set_resize_handler(image->widget, resize_handler); // window_set_keyboard_focus_handler(image->window, keyboard_focus_handler); // window_set_fullscreen_handler(image->window, fullscreen_handler); window_set_close_handler(image->window, close_handler); // widget_set_enter_handler(image->widget, enter_handler); // widget_set_motion_handler(image->widget, motion_handler); // widget_set_button_handler(image->widget, button_handler); // widget_set_axis_handler(image->widget, axis_handler); window_set_key_handler(image->window, key_handler); widget_schedule_resize(image->widget, 600, 400); return image; } static void display_run_nonloop(struct display *display) { struct task *task; struct epoll_event ep[16]; int i, count, ret; int idx = 0; display->running = 1; // while (1) { while (!wl_list_empty(&display->deferred_list)) { task = container_of(display->deferred_list.prev, struct task, link); wl_list_remove(&task->link); task->run(task, 0); } wl_display_dispatch_pending(display->display); ret = wl_display_flush(display->display); if ((ret < 0) && (errno == EAGAIN)) { ep[0].events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP; ep[0].data.ptr = &display->display_task; epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep[0]); } else if (ret < 0) { // break; return; } count = epoll_wait(display->epoll_fd, ep, ARRAY_LENGTH(ep), -1); for (i = 0; i < count; i++) { task = ep[i].data.ptr; task->run(task, ep[i].events); } } } static int screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list) { struct screenshooter_output *output; buff_size->min_x = buff_size->min_y = INT_MAX; buff_size->max_x = buff_size->max_y = INT_MIN; int position = 0; wl_list_for_each_reverse(output, output_list, link) { output->offset_x = position; position += output->width; } wl_list_for_each(output, output_list, link) { buff_size->min_x = MIN(buff_size->min_x, output->offset_x); buff_size->min_y = MIN(buff_size->min_y, output->offset_y); buff_size->max_x = MAX(buff_size->max_x, output->offset_x + output->width); buff_size->max_y = MAX(buff_size->max_y, output->offset_y + output->height); } if ((buff_size->max_x <= buff_size->min_x) || (buff_size->max_y <= buff_size->min_y)) { return -1; } buff_size->width = buff_size->max_x - buff_size->min_x; buff_size->height = buff_size->max_y - buff_size->min_y; return 0; } char *get_weston_screen_image() { struct wl_display *display_cap; // screenshooter variable struct wl_registry *registry; struct screenshooter_output *output; struct buffer_size buff_size = {}; struct screenshooter_data sh_data = {}; char *cap_img = NULL; // start of screenshoot display_cap = wl_display_connect(NULL); if (display_cap == NULL) { fprintf(stderr, "failed to create display: %s\n", strerror(errno)); return -1; } wl_list_init(&sh_data.output_list); registry = wl_display_get_registry(display_cap); wl_registry_add_listener(registry, ®istry_listener, &sh_data); wl_display_dispatch(display_cap); // printf("0.1 sh_data.shm[%p]\n",sh_data.shm); wl_display_roundtrip(display_cap); if (sh_data.screenshooter == NULL) { fprintf(stderr, "display doesn't support screenshooter\n"); return -1; } weston_screenshooter_add_listener(sh_data.screenshooter, &screenshooter_listener, &sh_data); if (screenshot_set_buffer_size(&buff_size, &sh_data.output_list)) { printf("screenshot_set_buffer_size fail\n"); // continue; return -1; } int idx = 0; wl_list_for_each(output, &sh_data.output_list, link) { if ((output->offset_x == 0) && ((output->width == 1024) && (output->height == 768))) { // printf("output->offset_x[%d]\n",output->offset_x); output->buffer = screenshot_create_shm_buffer(output->width, output->height, &output->data, sh_data.shm); if (output->buffer == NULL) { continue; } weston_screenshooter_shoot(sh_data.screenshooter, output->output, output->buffer); sh_data.buffer_copy_done = 0; wl_display_roundtrip(display_cap); usleep(35000); while (!sh_data.buffer_copy_done) { wl_display_roundtrip(display_cap); usleep(1000); } } } // end of screenshoot if (buff_size.height == 768) { cap_img = get_screenshot(&buff_size, &sh_data.output_list); } else { struct screenshooter_output *output, *next; wl_list_for_each_safe(output, next, &sh_data.output_list, link) { int ret = munmap(output->data, 1024*4*768); // printf("munmap error[%d]\n",ret); // printf("%s\n",strerror(errno)); wl_registry_destroy(output->output); wl_list_remove(&output->link); wl_buffer_destroy(output->buffer); // free(output->buffer); free(output); } } wl_registry_destroy(sh_data.shm); wl_registry_destroy(sh_data.screenshooter); wl_registry_destroy(registry); wl_display_disconnect(display_cap); return cap_img; } int main(int argc, char *argv[]) { struct display *d; // image.c variable int i; int image_counter = 0; char *cap_img = NULL; struct timespec start_time, end_time; d = display_create(&argc, argv); cap_img = get_weston_screen_image(); pimage = image_create(d, argv[i], &image_counter,cap_img); // full screen with 1x zoom scale window_set_fullscreen(pimage->window, pimage->fullscreen = 1); pimage->matrix.xx = 1.0; pimage->matrix.xy = 0.0; pimage->matrix.yx = 0.0; pimage->matrix.yy = 1.0; clamp_view(pimage); window_schedule_redraw(pimage->window); display_run_nonloop(d); struct sigaction sigint; sigint.sa_handler = handler_sigint; sigemptyset(&sigint.sa_mask); sigint.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sigint, NULL); clock_gettime(CLOCK_MONOTONIC, &end_time); memcpy(&start_time, &end_time, sizeof(struct timespec)); int loop = 0; long long usec; while (isloop) { // ui loop display_run_nonloop(d); // 20 msec // reload image free(cap_img); cap_img = get_weston_screen_image(); // 80 msec cairo_surface_destroy(pimage->image); // 70 usec pimage->image = cairo_image_surface_create_for_data(cap_img, CAIRO_FORMAT_ARGB32,1024, 768, 1024*4); // ui redraw request window_schedule_redraw(pimage->window); // fps calc. clock_gettime(CLOCK_MONOTONIC, &end_time); usec = (end_time.tv_sec - start_time.tv_sec) * 1000000 + (end_time.tv_nsec - start_time.tv_nsec) / 1000; // printf("%d : %lld usec / %lld fps\n", loop++, usec, 1000000UL / usec); memcpy(&start_time, &end_time, sizeof(struct timespec)); } free(cap_img); if (!is_pass_close_handler) { widget_destroy(pimage->widget); window_destroy(pimage->window); } cairo_surface_destroy(pimage->image); free(pimage); display_destroy(d); return 0; }